Абстрактные методы и принцип открытого-закрытого

Предположим, у меня есть следующий надуманный код:

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 , и он полностью контекстный; но я собираюсь оставить обсуждение открытым на некоторое время, прежде чем все же приму какие-либо ответы.

7
задан Community 23 May 2017 в 09:58
поделиться