Еще один вопрос о дизайне, связанный с конкретной проблемой

Скажем, у меня есть ниндзя с определенными настраиваемыми навыками. Под настраиваемым я подразумеваю, что программа считывает файл конфигурации и добавляет их во время выполнения. то есть:

Ninja:
  color: Red
  Skills:
    - High Jump
    - Invisibility

Теперь предположим, что эти навыки необходимы для динамического добавления функциональности классу Ninja. Например, если мы настроим Ninja на навык High Jump, тогда класс должен будет следовать интерфейсу CanHighJump с общедоступным void highJump () в качестве метода.

Я могу думать о том, чтобы сделать это двумя способами. Моей первой реакцией было бы иметь класс Ninja с цветом String, а затем использовать Reflection для добавления функциональности. Однако теперь я понимаю, что могу сделать это и с помощью реляционной базы данных. Имейте класс Ninja с цветом String и забудьте о любых объектах, связанных с Ninjas with Skills - вместо этого, когда мне нужно проверить навык, просто позвоните в базу данных, чтобы получить возможные навыки, которые может использовать этот класс. Это единственные два действительно динамичных решения, которые я могу придумать, и я не могу понять достоинства и недостатки дизайна каждого из них. Я чувствую, что решение для базы данных было бы гораздо более масштабируемым, но метод отражения имел бы для меня наибольший смысл при кодировании. Не нужно спрашивать, какими навыками обладает ниндзя, потому что я могу легко проверить интерфейс.

Я надеюсь, что кто-то может дать мне некоторое представление о стандартном дизайнерском решении этой проблемы. Будь то один из двух, которые я придумал, или, что более вероятно, что-то, о чем я не подумал.


person Phil    schedule 20.07.2011    source источник


Ответы (2)


Похоже, то, что вы ищете, похоже на шаблон возможностей.

public interface IHasSkills {
    public <T> getSkill(Class<T> skill);
}

public interface ICanAddSkills extends IHasSkills {
    public void addSkill(ISkill skill)
}

public class Ninja implements ICanAddSkills {
    private List<ISkill> _skills;

    public void addSkill(ISkill skill) {
        _skills.Add(skill);
    }

    public <T> GetSkill(Class<T> skillType) {
        for(ISkill skill : _skills) {
           if(skill instanceof skillType) return skill;
        }
        return null;
    }
}

public interface ISkill{}

public interface IHighjumpSkill extends IHighJumpCapable{
    public void highJump();
}

public class NinjaHighJumpSkill implements IHighJumpCapable{
    private Ninja _canHighJump;
    public NinjaHighJumpSkill(Ninja ninja) {
        _canHighJump = ninja;
    }

    @Override
    public void highJump() {
        //Note that this could be combined with the [Property Pattern][2] 
        //(which was referenced in another answer) to set/get arbitrary
        //properties on the ninja.
        _canHighJump.DoWhateverAHighJumpDoes();
    }
}

Вы можете использовать этот шаблон для добавления новых возможностей к объекту без необходимости фактически реализовывать их в объекте. Клиент получит экземпляр IHighJumpCapable при вызове myNinja.getSkill(IHighJumpCapable.class), если у ниндзя есть этот навык, или null в противном случае. Затем вы можете вызвать метод highJump () для IHighJumpCapable.

public static void Main(String args[]) {
    Ninja ninja = new Ninja();
    ninja.AddSkill(new NinjaHighJumpSkill(ninja));
    IHighJumpCapable canHighJump = ninja.GetSkill(IHighJumpCapable.class);
    canHighJump.highJump();
}
person Ryan Gross    schedule 20.07.2011
comment
Да, похоже, это именно то, что имел в виду шаблон свойств, только немного более конкретный для проблемы. Спасибо, что нашли время написать код. Я закончу этим. Я никогда раньше не думал об отношениях HasSkills - ›CanAddSkills. Это очень гибко. - person Phil; 21.07.2011

Это своего рода продолжение того, что предложил Райан. Следующий способ заставит ниндзя выполнить навык без необходимости спрашивать и проверять тип навыка, если это желательно.

Посмотрите, добавляет ли это ценность -

public class Ninja implements ICanAddSkills {
private List<ISkill> _skills;

public void addSkill(ISkill skill) {
    _skills.Add(skill);
}

public <T> getSkill(Class<T> skillType) {
    for(ISkill skill : _skills) {
       if(skill instanceof skillType) return skill;
    }
    return null;
    }
}

    public interface ISkill{
        public void performSkill();
    }

public interface IHighjumpSkill extends ISkill,IHighJumpCapable{
    public void highJump();
}

public class NinjaHighJumpSkill implements IHighjumpSkill{
    private Ninja _canHighJump;
    public NinjaHighJumpSkill(Ninja ninja) {
        _canHighJump = ninja;
    }

    @Override
    public void highJump() {
        _canHighJump.DoWhateverAHighJumpDoes();
    }

    // delgate to whatever this skill can do
    @Override
    public void performSkill() {
        highJump();
    }
}


public static void Main(String args[]) {
    Ninja ninja = new Ninja();
    ninja.AddSkill(new NinjaHighJumpSkill(ninja));
    // loop through skills and perform actions using performSkill()

     for(ISkill skill : _skills) {
          skill.performSkill();
        }

}
person Nrj    schedule 21.07.2011