Предположим, у меня есть следующий надуманный код:
abstract class Root
{
public abstract void PrintHierarchy();
}
class Level1 : Root
{
override public void PrintHierarchy()
{
Console.WriteLine("Level1 is a child of Root");
}
}
class Level2 : Level1
{
override public void PrintHierarchy()
{
Console.WriteLine("Level2 is a child of Level1");
base.PrintHierarchy();
}
}
Если я смотрю только на класс Level2
, я сразу вижу, что Level2.PrintHierarchy
следует принципу открытого / закрытого потому что он что-то делает сам по себе и вызывает замещающий базовый метод.
Однако, если я посмотрю только на класс Level1
, он, похоже, нарушает OCP b потому что он не вызывает base.PrintHierarchy
- фактически, в C # компилятор запрещает его с ошибкой «Невозможно вызвать абстрактный элемент базы».
Единственный способ заставить Level1
следовать за OCP - это изменить корень.PrintHierarchy
в пустой виртуальный метод, но тогда я больше не могу полагаться на компилятор для принудительного создания классов для реализации PrintHierarchy
.
Настоящая проблема, с которой я сталкиваюсь при поддержании кода здесь, заключается в том, что я вижу десятки методов переопределения
, которые не вызывают base.Whatever ()
. Если base.Whatever
является абстрактным, тогда все в порядке, но если нет, то метод Whatever
может быть кандидатом на включение в интерфейс, а не конкретный метод с возможностью переопределения - либо класс или метод нужно реорганизовать каким-либо другим образом, но в любом случае это явно указывает на плохой дизайн.
Если не вспоминать, что Root.PrintHierarchy
является абстрактным, или помещать комментарий внутри Level1.PrintHierarchy
], есть ли у меня другие варианты, чтобы быстро определить, сформирован ли класс как Уровень 1
нарушает OCP?
В комментариях было много хороших обсуждений и несколько хороших ответов. Я не мог понять, что именно здесь спрашивать. Я думаю, что меня расстраивает, , как указывает @Jon Hanna , иногда виртуальный метод просто указывает «Вы должны реализовать меня», тогда как в других случаях это означает «вы должны продлить меня - если вы не сможете назовите базовую версию, вы сломаете мою конструкцию! " Но C # не предлагает никакого способа указать, какой из них вы имеете в виду, кроме абстрактного или интерфейса, явно требующего реализации. (Если только в кодовых контрактах нет чего-то, что, я думаю, здесь немного выходит за рамки).
Но если бы в языке имелся декоратор, который необходимо реализовать, а не расширять, то, вероятно, это создало бы огромные проблемы для модульного тестирования, если бы его нельзя было отключить. Есть ли такие языки? Это звучит скорее как дизайн по контракту , поэтому я бы не удивился, если бы это было, например, в Eiffel.
Конечным результатом, вероятно, будет , как сказал @ Jordão , и он полностью контекстный; но я собираюсь оставить обсуждение открытым на некоторое время, прежде чем все же приму какие-либо ответы.