В этой серии постов в блоге Эрик Липперт описывает проблему объектно-ориентированного проектирования на примере мастеров и воинов, где:
abstract class Weapon { }
sealed class Staff : Weapon { }
sealed class Sword : Weapon { }
abstract class Player
{
public Weapon Weapon { get; set; }
}
sealed class Wizard : Player { }
sealed class Warrior : Player { }
а затем добавляет пару правил:
- Воин может использовать только меч.
- Волшебник может использовать только посох.
Затем он продолжает демонстрировать проблемы, с которыми вы сталкиваетесь, если вы пытаетесь применить эти правила с помощью системы типов C # (например, Wizardвозложить на класс ответственность за то, чтобы мастер мог использовать только персонал). Вы нарушаете принцип подстановки Лискова, рискуете получить исключения во время выполнения или получаете код, который сложно расширить.
Решение, которое он предлагает, заключается в том, что класс Player не выполняет проверку. Он используется только для отслеживания состояния. Затем вместо того, чтобы дать игроку оружие:
player.Weapon = new Sword();
состояние изменяется Commands и в соответствии с Rules:
... мы создаем
Commandобъект с именем,Wieldкоторый принимает два объекта состояния игры, aPlayerи aWeapon. Когда пользователь выдает команду системе «этот волшебник должен владеть этим мечом», тогда эта команда оценивается в контексте набораRules, который создает последовательностьEffects. У нас есть такой,Ruleкоторый говорит, что когда игрок пытается владеть оружием, эффект состоит в том, что существующее оружие, если оно есть, сбрасывается, и новое оружие становится оружием игрока. У нас есть другое правило, которое усиливает первое правило, которое гласит, что эффекты первого правила не применяются, когда волшебник пытается владеть мечом.
Мне в принципе нравится эта идея, но я беспокоюсь о том, как ее можно использовать на практике.
Кажется, ничто не мешает разработчику обойти « Commandsи Rule», просто установив « Weaponна» Player. WeaponСобственности должны быть доступны по Wieldкоманде, так что это не может быть сделаны private set.
Итак, что же предотвратить разработчик делать это? Они просто должны помнить, чтобы не делать?