В этом случае контракты кода направляют ваше внимание на ошибку проектирования в структуре вашего класса, и вам будет разумно прислушаться к этому предупреждению.
Чтобы увидеть проблему, подумайте, как можно использовать классы.
protected void executeButton_Click(object sender, EventArgs args)
{
BaseCommand command = GetCurrenctlySelectedCommand();
command.Execute();
}
Если переменная command
содержит объект типа DeleteItemCommand
, то этот объект имеет предварительные условия, которые должны быть выполнены, или будет выдано исключение. Мы хотели бы избежать этого исключения, так как мы можем проверить, что предварительное условие выполнено?
К сожалению, не существует простого способа сделать это. Мы не можем рассуждать обо всех возможных предварительных условиях для каждого типа производного объекта, который может обитать в этой переменной. Фактически, эта переменная может содержать тип объекта, который не был изобретен при написании этого кода. Фактически, тип этого объекта может даже не находиться в домене доступности этого метода, если он был предоставлен фабрикой в другой сборке.
Поскольку нет способа проверить, что для этого объекта выполнены предварительные условия, мы не можем гарантировать правильность этого кода. Из этого можно сделать вывод, что контракты на код бесполезны или что код разработан неправильно.
Я понимаю, что это предупреждение выдается, потому что я нарушаю принцип Лискова.
Итак, вы признаете это!
Однако в моем случае условия отличаются от одного конкретного класса
другому. Мой класс BaseCommand на самом деле определяет некоторые общие
атрибуты, такие как CommandIdentifier, состояние и другие конечные
функции, которые я здесь убрал, чтобы упростить вопрос.
Ваш анализ сам предлагает правильную альтернативу.
Вместо BaseCommand
абстрактного класса вы должны создать CommandAttributes
конкретный запечатанный класс. Затем включите экземпляр этого класса во все объекты вашей команды.
Используя композицию, а не наследование, каждый из ваших классов команд получает необходимую им функциональность и может определять любые предварительные условия или постусловия, в которых они нуждаются. И любые методы, которые используют эти классы, могут проверить, что эти предварительные условия выполнены, и использовать в своих интересах постусловия.