4

私が使用しているサードパーティのライブラリには、次のような階層があります。

public abstract class Foo
{
    // Class is public in the Java sense and public in the library's API
}

public class FooImpl extends Foo
{
    // While this class is public in the Java sense, it is
    // NOT part of the public API and is not even documented
    // Library functions that hand back instances of this class
    // hand them back as type Foo
}

ライブラリ内のさまざまなメソッドは、 type のものを操作しFooます。ドキュメントによると、Fooユーザーは拡張Fooして具体的な実装を作成でき、(明らかに) ライブラリは具体的な実装を提供します。

ライブラリ提供の実装には満足していますが、その動作を少し変更したいメソッドが 1 つあります。しかし、私が与えられたヒエラルキーは私をつまずかせています。

インターフェイスだった場合 Foo(そうではありません!)、私はこれを行うだけです:

public FooWrapper implements Foo
{
    private Foo wrappedImpl;

    public FooWrapper(Foo toBeWrapped) {
        wrappedImpl = toBeWrapped;
    }

    List<Whatever> methodIWantToTweak() {
        List<Whatever> list = wrappedImpl.methodIWantToTweak();
        do_something_to_list(list);
        return list;
    }

    Something methodThatsOk() {
        return wrappedImpl.methodThatsOk();
    }

    // similar delegations for remaining methods
}

しかし、それはインターフェースではないので、これを行うことはできません。少なくとも、きれいに行うことはできません。

私が考えられる唯一のことは次のとおりです。

1) API 非公開クラス FooImpl を拡張し、微調整したいメソッドをオーバーライドします。

2) 次のようにします。

public class FooWrapper extends Foo
{
    private Foo wrappedImpl;

    public FooWrapper(Foo toBeWrapped) {
        super();
        wrappedImpl = toBeWrapped;
    }

    List<Whatever> methodIWantToTweak() {
        List<Whatever> list = wrappedImpl.methodIWantToTweak();
        do_something_to_list(list);
        return list;
    }

    Something methodThatsOk() {
        return wrappedImpl.methodThatsOk();
    }

    // Override all non-final public, protected, and package-local
    // methods and delegate them to wrappedImpl
}

これらのアプローチは両方とも、私には臭いがします。最初のものには、それが知っているはずのないクラスに依存し、ライブラリの将来のバージョンで変更/消失/名前が変更される可能性があるという問題があります。2 つ目は、 のスーパークラス部分がFooWrapper実際には使用されず、 の最終メソッドまたはプライベート メソッドをオーバーライドできないというFoo問題があります (これらの場合、 にデリゲートできないため、問題が発生しますwrappedImpl)。

少なくとも正しい動作が得られるため、最初のアプローチを使用する必要があると思いますが、2番目のアプローチは、Foo.

では、私は運が悪いのでしょうか?私が見落としている他のアプローチ/アイデアは何ですか?

4

2 に答える 2

3

Fooの final または protected (以下を参照) メソッドをオーバーライドする必要がない場合は、2 番目のアプローチFooを使用します。つまり、インターフェイスとして扱い、その場合に行うことを行います。利点は、変更可能な に依存しないことですFooImpl

finalprivateのメソッドはFoo問題ではありません。いずれにせよ、インターフェイス内FooImplまたはFooインターフェイスである場合はそれらをオーバーライドできないためです。protectedまた、あなたはの代わりに意味したと思いますprivate

メソッドをオーバーライドする必要がある場合protected、最初のアプローチを使用する必要があります。

于 2012-05-28T00:38:03.693 に答える
2

これほど臭くないアプローチは他に考えられません。(リフレクションは機能しますが、この場合、回避しようとしている問題よりも悪いことになります。)

2番目のアプローチが最適だと思います。Foo抽象クラスがパブリック クラスとして設計されている場合、つまずくような厄介な内部の詳細が含まれていてはなりません。もしそうなら、彼らは設計が間違っています。

「実際の」 Foo インスタンスのラッパー クラスを作成しているため、次の点に注意してください。

  • 「実際の」Foo サブクラスが使用するために抽象クラスが提供する機能のほとんどは、おそらく無視できます。

  • おそらく、Foo レベルの属性によって表される状態は無視できます。

  • アプリケーションで使用する必要のない API のメソッドをスタブ化できます。たとえば、 をスローするようにコーディングしますUnsupportedOperationException

これらはすべて、問題の複雑さを軽減するのに役立ちます。

于 2012-05-28T00:38:20.140 に答える