#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-ой статье или прочитать всю промежуточную логику с начала цикла.) Присоединюсь к идее этих статей: просто создайте отдельную сущность «набор правил», в которой закодируйте все правила.
Комментариев нет:
Отправить комментарий