1

私はEffective JavaからItem 16を読み、継承よりも構成を優先しますか? そして今、私が Java を理解し始めた 1 年前に書いたコードに適用してみます。

私は、水泳、肉食などの特徴を持ち、さまざまな種類の食物を得ることができる動物をモデル化しようとしています。

public class Animal {
    private final List<Trait> traits = new ArrayList<Trait>();
    private final List<Food> eatenFood = new ArrayList<Food>();

}

項目 16 では、構成と転送の再利用可能なアプローチが提案されています。

public class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;
    public ForwardingSet(Set<E> s) {this.s = s;}

    //implement all interface methods
    public void clear() {s.clear();}

    //and so on
}

public class InstrumentedSet<E> extends ForwardingSet<E> {
   //counter for how many elements have been added since set was created
}

実装できますが、クラスForwardingList<E>に2回適用する方法がわかりません。Animal現在、Animal以下のような多くのメソッドがあります fortraitsおよび for eatenFood. これは私には不愉快に思えます。

public boolean addTrait (Trait trait) {
    return traits.add(trait);
}

public boolean removeTrait (Trait trait) {
    return traits.remove(trait);
}
  1. クラスをどのように再設計しAnimalますか?

  2. そのままにしておくか、申請してみるForwardingListか。

4

1 に答える 1

1

この問題のために List を特殊化する理由はありません。ここではすでにコンポジションを使用していますが、これは私がこのクラスに期待するものとほとんど同じです。

コンポジションとは基本的に、1 つ (通常は複数) のメンバーを持つクラスを作成することです。転送とは、事実上、メソッドが保持しているオブジェクトの 1 つを呼び出して、それを処理するようにすることです。これはまさにあなたがすでに行っていることです。

とにかく、あなたが言及したメソッドは、Traitを持つクラスに期待するメソッドとまったく同じです。同様の addFood / removeFood 種類の食品のメソッドを期待します。彼らが間違っているとすれば、それはほとんどの人が犯すのとまったく同じ種類の間違いです。

IIRC (Effective Java の私のコピーは動作中です): ForwardingSet の存在は、明示的に拡張するように設計されていないクラスを安全に拡張できないという単純な理由によるものです。自己使用パターンなどが文書化されていない場合、デフォルトの実装のために addAll が add を繰り返し呼び出す場合と呼び出さない場合があることを知らないため、呼び出しをスーパー メソッドに合理的に委譲することはできません。ただし、委任先のオブジェクトがラッパー オブジェクトを呼び出すことはないため、安全に呼び出しを委任できます。これはここではまったく当てはまりません。あなたはすでに呼び出しをリストに委任しています。

于 2013-02-27T06:11:27.280 に答える