次の不自然なコードがあるとします。
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に違反しているように見えますbase.PrintHierarchy
。実際、C#では、コンパイラは「抽象ベースメンバーを呼び出せません」というエラーでクラスを禁止しています。
Level1
OCPに従っているように見せかける唯一の方法はRoot.PrintHierarchy
、空の仮想メソッドに変更することですが、そうすると、実装する派生クラスを強制するためにコンパイラーに依存できなくなりますPrintHierarchy
。
ここでコードを維持しているときに私が抱えている本当の問題は、をoverride
呼び出さない多数のメソッドが表示されることですbase.Whatever()
。base.Whatever
が抽象である場合は問題ありませんが、そうでない場合は、Whatever
メソッドが具体的なオーバーライド可能なメソッドではなく、インターフェイスにプルされる候補になる可能性があります。または、クラスまたはメソッドを他の方法でリファクタリングする必要がありますが、ちなみに、それは明らかにデザインが悪いことを示しています。
抽象的であることを覚えRoot.PrintHierarchy
たり、中にコメントを入れたりする以外に、のように形成されたクラスがOCPに違反しLevel1.PrintHierarchy
ているかどうかをすばやく特定する他のオプションはありますか?Level1
コメントにはたくさんの良い議論があり、いくつかの良い答えもあります。私はここで何を尋ねるべきかを正確に理解するのに苦労していました。@Jon Hannaが指摘しているように、仮想メソッドが単に「実装する必要がある」と示す場合もあれば、「拡張する必要がある」という意味の場合もあります。ベースバージョンを呼び出せない場合は、私のデザインを壊してください!」ただし、C#は、抽象またはインターフェイスが明らかに「実装する必要がある」状況であることを除いて、どちらを意味するかを示す方法を提供していません。(コードコントラクトに何かがない限り、ここでは少し範囲外だと思います)。
ただし、言語に実装が必要なデコレータと拡張が必要なデコレータがある場合、無効にできない場合は、単体テストで大きな問題が発生する可能性があります。そのような言語はありますか?これは契約による設計のように聞こえるので、たとえばエッフェルにあったとしても驚かないでしょう。
最終結果はおそらく@Jordãoが言うように、そしてそれは完全に文脈的です。しかし、私はまだ答えを受け入れる前に、しばらく議論を開いたままにするつもりです。