15

私は、Code Completeの Steve McConnellが言及したものと非常によく似た状況にあります。私の問題が車とトライクに基づいているということだけが、たまたま法律で車のカテゴリに分類されることにあります。今までの車は四輪でした。とにかく、私のドメインは不必要に複雑なので、以下の猫の例に固執するのは簡単です.

ルーチンをオーバーライドし、派生ルーチン内で何もしないクラスを疑ってください。これは通常、基本クラスの設計に誤りがあることを示しています。たとえば、クラス Cat とルーチン Scratch() があり、最終的に何匹かの猫の爪が剥がれて引っ掻くことができないことがわかったとします。ScratchlessCat という名前の Cat から派生したクラスを作成し、何もしないように Scratch() ルーチンをオーバーライドしたくなるかもしれません。このアプローチにはいくつかの問題があります。

インターフェイスのセマンティクスを変更することにより、Cat クラスで提示される抽象化 (インターフェイス コントラクト) に違反します。

このアプローチは、他の派生クラスに拡張するとすぐに制御不能になります。しっぽのない猫を見つけたらどうしますか?それともねずみを捕まえない猫?それともミルクを飲まない猫?最終的には、ScratchlessTaillessMicelessMilklessCat のような派生クラスになります。

時間が経つにつれて、このアプローチは維持するのに混乱を招くコードを生み出します。これは、祖先クラスのインターフェイスと動作が、その子孫の動作についてほとんど、またはまったく意味を持たないためです。

この問題を修正する場所は、基本クラスではなく、元の Cat クラスにあります。Claws クラスを作成し、それを Cats クラス内に含めます。根本的な問題は、すべての猫が引っ掻くという仮定でした。そのため、目的地で包帯を巻くのではなく、発生源でその問題を修正してください。

上記の彼の偉大な本のテキストによると. フォローが悪い

親クラスは抽象である必要はありません

public abstract class Cat {
   public void scratch() {
      System.out.println("I can scratch");
   }
}

派生クラス

public class ScratchlessCat extends Cat {
   @Override
   public void scratch() {
      // do nothing
   }
}

現在、彼は別のクラスを作成することを提案しClawsていますが、このクラスを使用して の必要性を回避する方法がわかりませんScratchlessCat#Scratch

4

2 に答える 2

11

すべての猫が爪を持っているわけではなく、引っ掻くことができるということは、その API でCatパブリック メソッドを定義するべきではないという大きな手がかりです。最初のステップは、そもそもscratch定義した理由を検討することです。scratchおそらく、猫は攻撃されたときにできれば引っ掻きます。そうでない場合、彼らはシューッという音を立てるか逃げます。

public class Cat extends Animal {
    private Claws claws;

    public void onAttacked(Animal attacker) {
        if (claws != null) {
            claws.scratch(attacker);
        }
        else {
            // Assumes all cats can hiss.
            // This may also be untrue and you need Voicebox.
            // Rinse and repeat.
            hiss();
        }
    }
}

これで、任意のサブクラスを別のサブクラスに置き換えることができCat、爪の有無に応じて正しく動作します。、、DefenseMechanismなどのさまざまな防御を整理するクラスを定義できます。ScratchHissBite

于 2012-05-28T01:12:39.543 に答える
10

メソッドはまだありますがscratch()、派生クラスによってオーバーライドされることはありません。

public class Cat {
  Claw claw_;
  public Cat(Claw claw) {claw = claw_;}
  public final void scratch() {
    if (claw_ != null) {
      claw_.scratch(this);
    }
  }
}

これにより、含まれているオブジェクトが存在する場合は、そのオブジェクトにスクラッチ ロジックを委譲できますClaw(爪がない場合はスクラッチしません)。cat から派生したクラスは、どのようにスクラッチするかについて発言権がないため、能力に基づいてシャドウ階層を作成する必要はありません。

scratch()また、派生クラスはメソッドの実装を変更できないため、基本クラスのインターフェイスでメソッドの意図されたセマンティクスを壊すという問題はありません。

これを極端にすると、多くのクラスがあり、多くの派生物がないことに気付くかもしれません。ほとんどのロジックは、派生クラスに委ねられているのではなく、コンポジション オブジェクトに委譲されています。

于 2012-05-28T01:12:05.673 に答える