クラス階層をレイアウトするとき、コードを共有しながら機能をカプセル化できることのギャップに不満を感じることがよくあります。もちろん、問題の一部は多重継承の欠如ですが、インターフェイスはいくらか役に立ちます。インターフェイスで保護されたメソッドを定義できないことが、より大きな問題のように思えます。
標準的な解決策は、保護された抽象基本クラスによって実装されるパブリック インターフェイスを持つようです。問題は、次の場合です
public interface Foo {
public String getName();
}
abstract protected BaseFoo implements Foo {
abstract protected int getId();
private String name;
protected BaseFoo(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
public class ConcreteFoo extends BaseFoo {
public ConcreteFoo (String name) {
super(name);
}
@Override
protected int getId() {
return 4; // chosen by fair dice roll.
// guaranteed to be random.
}
}
// in the foo package with the classes above
public class FooCollection {
private static Map<Integer, Foo> foos = new HashMap();
public static void add(Foo foo) {
synchronized(foos) {
foos.put(foo.getId(), foo); // can't call foo.getId()
}
}
}
// client code, not in the foo package
FooCollection.add(new ConcreteFoo("hello world"));
つまり、適切にカプセル化されたオブジェクトの 1 つを呼び出し元に返しますが、そのオブジェクトを取得するメソッドは、何らかの内部機能に依存できる必要があります。その内部機能をインターフェイスの一部にすることはできませんが (カプセル化が壊れる可能性があります)、抽象基本クラスの一部にするには、キャストを使用する必要があります。
Foo を抽象クラスにすることはできません。これは、他のインターフェイスがそれを拡張して、ここに表示されているよりも複雑な階層にオプションの直交機能を追加する必要があるためです。
この問題に対する標準的なアプローチは何ですか? クライアントが使用すべきではないにもかかわらず、getId を Foo インターフェースに追加しますか? FooCollection.add で BaseFoo への安全でないキャストを実行しますか? キャストする前に確認する場合、すべての意図と目的のために常に型が一致する必要があるにもかかわらず、型が一致しない場合はどうしますか?
この種の状況でのベスト プラクティスに関する情報は、非常に役立ちます。
編集:明確でない場合のために、この例は意図的に単純化されています。重要な点は、オブジェクトの「インターフェイス ビュー」を返す場合があるということです。その「インターフェイス ビュー」がパッケージ固有のクラスに戻される場合、渡されるメソッドは、その実装で内部機能を使用する必要がある可能性があります。内部機能と公開機能の不一致をどのように管理しますか?