web-dev-qa-db-ja.com

OOD:Java継承とキャストによる子メソッドへのアクセス

いくつかのクラスParentChild1 ... Child9 Javaで実装されています。 Parentは抽象クラスであり、子クラスのすべての共通変数(多くの場合、これがParentをインターフェイスではなく抽象クラスにした主な理由です)を含み、いくつかの抽象といくつかの実装されたメソッド。

一部の子クラスには、それらに固有のカスタムメソッドがあります。ダウンキャストを使用して子メソッドを呼び出すことがよくあります。

Parent p = new Child1();
((Child1) p).child1SpecificMethod();

どういうわけか、これはOODの悪い習慣だと感じていますが、それが本当にデザインの改善方法であるかどうかはよくわかりません。

-Edit-おそらくとにかく変更する必要があるのは、多くの(現時点では)共通変数を整理するためにParentクラスを使用し、それらを具象クラスのメンバー(またはコンテナーオブジェクト)メンバーにすることです。

8
jpmath

これは悪い習慣だけではなく、不必要に複雑です。

なぜ一般的にinheritanceを使用するのですか?

継承を使用すると、多くの異なるキャリアで利用できるようにする共通の動作セットがあります。これにはクラス継承とals インターフェイス継承が含まれます。 相続人、いわば、多くの場合、継承元のクラスの専門化です。これは主にクラスの継承に当てはまります。

クラスcarとサブクラスporscheを考えてください(一般的なis a-relationship)。 starting/stopping enginesteeringなどの一般的な動作があります。 porscheを車のように扱う場合、その動作のこの側面に拘束されます。 porscheだけが必要で、それだけを扱うポルシェとしてであることがわかっている場合、ポルシェを-としてインスタンス化するのは冗長ですcarおよびcastingを介してporsche-behaviourを取得します。

ポリモーフィズムは、その逆の意味を持ちます。

あなたはポルシェを持っていて、車の観点からそれを扱う必要があります;例えば運転

porschesteer leftを受け入れる限り、steer rightshift upshift downなど。ポリモーフィズム/置換を相互に使用できます。

オブジェクトを特殊な形式でインスタンス化することをお勧めします。そうすれば、polymorphismを最大限に活用して、-needの場合にのみ使用できます。

つまり、Parent p = new Child1();は私には意味がありません。

編集:porscheを実装します(--compositionを介して)。ただし、例としてis a carとします。

11
Thomas Junk

あなたの気持ちはそれを悪い習慣だと考えるのは正しいです。あなたのサンプルコードが少し違うと想像してください:

Parent p = createObject();
((Child1) p).child1SpecificMethod();

Pの値が本当にChild1であることをどうやって知っていますか?そうしないと、実行時にClassCastExceptionが発生する可能性があります。コードでchild1SpecificMethod()を呼び出す必要がある場合は、pの型がChild1であることを確認する必要があります。 Object pがタイプParentとしてコードに(たとえばメソッドパラメーターとして)渡されるためにそれが不可能な場合は、 Visitor-Pattern のバリアントを使用して、handle-Methodでchild1SpecificMethodを実行することを検討できます。 Child1を処理するビジターオブジェクトの.

4
Benni

代わりに機能のルックアップを使用します。子クラスへのアクセスを許可しないで、それらを親クラスの実装と見なします。

次に、いくつかの機能、機能を指定するインターフェイスを定義します。

interface Child1Specific {
    void child1SpecificMethod();
}

使用法:

Parent parent = ...
Child1Specific specific = parent.lookup(Child1Specific.class);
if (specific1 != null) {
    specific1.child1SpecificMethod();
}

この検出メカニズムは非常に柔軟です。継承の代わりに委任を使用すると、かなりやりがいがあります。子クラスを持つことはもはや必要ないことに注意してください。

またはJava 8(いくつかのバリエーションが可能であり、インターフェースも機能する可能性がある場合)で:

Optional<Child1Specific> specific = parent.lookup(Child1Specific.class);
if (specific1.isPresent()) {
    specific1.get().child1SpecificMethod();
}

Parentクラスで、いくつかの機能のルックアップを作成します。

public class Parent {
    protected final Map<Class<?>, Object> capabilities = new HashMap<>();
    protected final <T> void registerCapability(Class<T> klass, T object);

    public <T> T lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return object == null ? null : klass.cast(object);
    }

またはJava 8:

    public <T> Optional<T> lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return Optional.ofNullable(klass.cast(object));
    }

子クラス:

class Child1 extend Parent implements Child1Specific {
    Child1() {
        registerCapability(Child1Specific.class, this);
    }
}

またはより動的:

class Child1 extends Parent {
    private Child1Specific specific = new Child1Specific() {
        ... Parent.this ...
    };
    Child1() {
        registerCapability(Child1Specific.class, specific);
    }
}
4
Joop Eggen