web-dev-qa-db-ja.com

2つのumodifiableクラスを拡張しながらコードの重複を回避する方法

私はすでにこのコアクラス構造を持っていますそれは変更できません

class A {
    //some basic fields and methods
}
class B {
    //some another basic fields and methods
}

これはコアクラスであり、既存のシステムにいくつかの機能を追加しています。このクラスにいくつかの一般的な機能を追加する必要があります。 ABの実装はほとんど同じです。私の現在の解決策は、次のようなものを構築することです:

class A {
    //some basic fields and methods
}
class B {
    //some another basic fields and methods
}

class Foo extends A {
    //my new features
}
class Bar extends B {
    //my new features (identical with Foo)
}

問題は、AまたはBクラスを変更できず、それらを変更できないだけではないことです。need base ABクラスの両方- および存在するFooおよびBarクラス。したがって、リフレクションを使用してAおよびBクラスを変更することは、解決策ではありません。

私はこの機能をいくつかのapiオブジェクトとして追加しようとしました:

class FeatureApi {
    my new features
}
class Foo extends A {
    FeatureApi api;
}
class Bar extends B {
    FeatureApi api;
}

しかし、別の問題が発生します。新しいメソッドは私のクラスのフィールド/メソッドにアクセスする必要があります。しかし、私はそれらをapi-objectに持っていません。

要約すると:

  • 既存のクラスは変更できません
  • ABを拡張するカスタムクラスを作成したい
  • プライベートフィールドにアクセスする必要はありません。
  • 特定の変更に加えて、私のクラスには両方に同じ機能がたくさんあります。

PD 2主な目的は、2つの変更可能な基本クラスから継承した、2つのカスタムクラスにまったく同じ動作を実装することです。今見ているように、基本クラスの継承は意味をなさないので、それを削除して私の質問を明確にしました。

4
TEXHIK

あなたはFeatureApiの例でそれを行うことができます。 AまたはBの保護フィールドをラップされたFeatureApiに公開するには、いくつかの変更が必要です。

(私はc#で行くつもりです)

class FeatureApi 
{
    FeatureApi(IExposeStuff parent) {..}

    public string myNewFeature()
    {
       var info = parent.GetProtectedInfo();
       //do stuff
    }
}

interface IExposeStuff
{
    string GetProtectedInfo();
}

class NewA : A, IExposeStuff
 {
    FeatureApi api;
    public NewA()
    {
        this.api = new FeatureApi(this)
    }

    public string GetProtectedInfo()
    {
        return this.protectedInfo;
    }
}
0
Ewan

残念ながら、あなたは少し緊張しています。他の誰かが、変更できない継承階層にあなたをロックしました。 AおよびBの動作方法によっては、動作を実装するために各クラスを個別に拡張する必要がある場合があります。ここでは、直面する制約に応じたいくつかのシナリオを示します。

AおよびBのメソッドをオーバーライドする必要があります。

このシナリオでは、AおよびBが独自のメソッドを呼び出し、実装している機能はこれらの呼び出しをインターセプトする必要があります。例えば:

class A {
    private void fizz() {
        buzz();
    }
    void buzz(); // <- you must override this to implement your functionality
}

このシナリオでは、Aのrawインスタンスで動作を取得するonly方法は、Aを拡張してbuzzをオーバーライドすることです。同様に、Bのインスタンスで動作を取得する唯一の方法は、Bを拡張してbuzzをオーバーライドすることです。それらを個別に拡張する必要がありますが、buzzの2つのオーバーライドを静的ユーティリティメソッドまたはAの個別のインスタンスメソッドに呼び出すことにより、コードを再利用できる場合があります。

この状況は厄介で、コードの複製が行き詰まっており、同じ方法でより多くの機能を実装する必要がある場合は、4つのクラスを拡張する必要があります。もう一度行う必要がある場合は、拡張するクラスが8つあります。ご覧のとおり、このアプローチはまったく拡張性がありません。

AおよびBのメソッドをオーバーライドする必要はありません。

このシナリオでは、AおよびBのメソッドは、新しい機能に「コールバック」する必要はありません。このシナリオmightでは、ABの許容度に応じて、デコレータパターンを介して委任を使用できます。このアプローチには、AAの「実際の」インスタンスを取得し、それにすべてのメソッド呼び出しを委譲するサブクラスを使用してAを拡張し、Bを拡張してBを装飾します。

このアプローチが最初のアプローチより優れている1つのsmallの利点は、Aの「実際の」インスタンスを使用するユーティリティメソッドの観点から、追加機能を実装できることです。 BAであるため、AデコレータとBデコレータの両方からこのユーティリティメソッドを呼び出すことができます。

Aが特定のデータを取るコンストラクターしか持っていない場合、サブクラスはスーパークラスのコンストラクターを呼び出す必要があるため、このアプローチは実行できない場合があります。スーパークラスに渡されたデータをファッジすることもできますが、そうするとハッキングされて壊れやすくなります(もしAがファッジされたデータが失敗したという検証を追加したらどうなるでしょうか?)。

インターフェースを導入できます。

AおよびBのインターフェースを導入できる場合は、オプションがかなり開きます。この場合、具象スーパークラスのコンストラクターの呼び出しを心配することなく、デコレーターパターンをきれいに実装できます。それでも2つのデコレータを実装する必要がありますが、追加したすべての機能は、装飾されたオブジェクトを取得する静的ユーティリティメソッドで実装できるため、ボイラープレートを複製するだけで済みます。

このアプローチでは、デコレータインターフェースを公開する必要がないことに注意してください。コードはABにのみ依存します。したがって、ABの両方に機能を追加する必要がある場合は、2回実装するだけで済みます。 3回目に機能を追加する必要がある場合でも、2回実装するだけで済みます。したがって、このアプローチは最初のアプローチよりもはるかに優れています。

1

カプセル化を使用しないのはなぜですか? AとBをそれぞれFooとBarのメンバーにします。マーシャリング、検証などを行うために、現在のAおよびBオブジェクトを返すものに新しい抽象化レイヤーを導入します。

0
ComeIn

継承やインターフェースは使用しないでください。どちらも状況に不適切です。代わりに封じ込めを使用する必要があります。

新しいクラスMyAにクラスAのプライベートインスタンスを作成します。新しいものを実装します。 Aの閉じた機能が必要なときはいつでも、Aを呼び出します。Aの閉じたメソッドと同じ名前でデリゲートするだけの独自の新しいメソッドを作成できます。 Bも同じようにします。

0
Martin Maat