#c_sharp #разработка_игр #проектирование #шаблон_стратегия
Пытаюсь спроектировать систему оружия для игры, в которой бы каждое оружие задавалось некоторой базой и несколькими переменными поведения. Примерно так: public interface iWeapon { void Use(); } public interface iReloadable { void Reload(); } public interface iWeaponUseStrategy { void Perform(); } public interface iWeaponReloadStrategy { void Perform(); } public class BaseWeapon: iWeapon, iReloadable { public iWeaponUseStrategy UseStrategy; public iWeaponReloadStrategy ReloadStrategy; public void Use() { UseStrategy.Perform(); } public void Reload() { ReloadStrategy.Perform(); } } Когда появляется класс оружия, стратегии для которого зависят от состояния самого оружия начинаются трудности с адаптацией более общих стратегий для этого оружия. Текущая реализация выглядит так: public interface iWeaponWithAmmoUseStrategy { // Теперь стратегия меняет состояние внешнего по отношению к ней объекта void Perform(WeaponWithAmmo weapon); } public class SimpleAmmoUseStrategy : iWeaponWithAmmoUseStrategy { public void Perform(WeaponWithAmmo weapon) { // Изменение состояния внешнего объекта weapon.AmmoCurrent -= 1; // код выстрела } } ... public class WeaponWithAmmo: iWeapon, iReloadable { public int AmmoMax {get; set;} public int AmmoCurrent {get; set;} public iWeaponWithAmmoUseStrategy UseStrategy; public void Use() { UseStrategy.Perform(this); } ... } Мне не нравится то, что невозможно использовать существующие стратегии для BaseWeapon в новом WeaponWithAmmo. По логике стратегии iWeaponUseStrategy не имеют никаких специальных требований для оружия, в котором они применимы. А стратегии iWeaponWithAmmoUseStrategy "требуют" от оружия быть экземпляром класса WeaponWithAmmo или его подтипа. Тем не менее, первые не могут быть использованы в оружии WeaponWithAmmo. Единственное решение, которое приходит в голову, это вынести зависимость от оружия в отдельный интерфейс, и для каждого нового класса оружия (и соответствующих ему стратегий) менять интерфейс iWeaponUseStrategy: // Теперь перед использованием оружие надо инициализировать его стратегии public iWeaponWithAmmoInit { void Init(WeaponWithAmmo weapon); } // Но затем можно вызывать методы стратегий без ссылки на внешний объект public iWeaponWithAmmoUseStrategy { void Perform(); } // Кроме того, нужно чтобы базовая стратегия являлась подтипом специфичных для оружия стратегий // Меняем этот код каждый раз при добавлении класса оружия public iWeaponUseStrategy : iWeaponWithAmmoUseStrategy, iSomeNewWeaponTypeUseStrategy { new void Perform(); } Это очень некрасивое решение, нарушающее к тому же принцип "Существующий код должен быть закрыт для изменений". Мне кажется, что изначально мои мысли пошли по неправильному пути и надо строить архитектуру как-то по-другому. Подскажите, в каком направлении лучше двигаться?
Ответы
Ответ 1
Мне кажется, ошибка в том, что вы пытаетесь сложную систему правил закодировать в терминах иерархии объектов. Иерархии объектов просто не предназначены для этого. Хуже того, ваша логика правил использования оружия оказывается размазанной по всем иерархии наследования. Подобную проблему недавно обсуждал Эрик Липперт в цикле статей Wizards and warriors: [1], [2], [3], [4], [5]. (Вы можете просмотреть сразу выводы в 5-ой статье или прочитать всю промежуточную логику с начала цикла.) Присоединюсь к идее этих статей: просто создайте отдельную сущность «набор правил», в которой закодируйте все правила.
Комментариев нет:
Отправить комментарий