В этой серии постов в блоге Эрик Липперт описывает проблему объектно-ориентированного проектирования на примере мастеров и воинов, где:
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();
состояние изменяется Command
s и в соответствии с Rule
s:
... мы создаем
Command
объект с именем,Wield
который принимает два объекта состояния игры, aPlayer
и aWeapon
. Когда пользователь выдает команду системе «этот волшебник должен владеть этим мечом», тогда эта команда оценивается в контексте набораRule
s, который создает последовательностьEffect
s. У нас есть такой,Rule
который говорит, что когда игрок пытается владеть оружием, эффект состоит в том, что существующее оружие, если оно есть, сбрасывается, и новое оружие становится оружием игрока. У нас есть другое правило, которое усиливает первое правило, которое гласит, что эффекты первого правила не применяются, когда волшебник пытается владеть мечом.
Мне в принципе нравится эта идея, но я беспокоюсь о том, как ее можно использовать на практике.
Кажется, ничто не мешает разработчику обойти « Commands
и Rule
», просто установив « Weapon
на» Player
. Weapon
Собственности должны быть доступны по Wield
команде, так что это не может быть сделаны private set
.
Итак, что же предотвратить разработчик делать это? Они просто должны помнить, чтобы не делать?