既に存在するオブジェクトに機能を追加するにはどうすればよいですか?


25

特定の量の明確に定義された機能を備えたインターフェイスがあります。まあ言ってみれば:

interface BakeryInterface {
  public function createCookies();
  public function createIceCream();
}

これはインターフェイスのほとんどの実装でうまく機能しますが、いくつかのインスタンスでは、いくつかの新しい機能を追加する必要があります(新しいメソッドにロールインするなどcreateBrownies())。これを行うための明白/単純なアプローチは、インターフェースを拡張することです。

interface BrownieBakeryInterface extends BakeryInterface {
  public function createBrownies();
}

しかし、既存のAPIを変更せずに新しい機能を追加できないという点でかなり大きな欠点があります(新しいインターフェイスを使用するようにクラスを変更するなど)。

インスタンス化後に機能を追加するためにアダプターを使用することを考えていました。

class BrownieAdapter {
  private brownieBakery;

  public function construct(BakeryInterface bakery) {
    this->brownieBakery = bakery;
  }

  public function createBrownies() {
    /* ... */
  }
}

これは私に次のようになります:

bakery = new Bakery();
bakery = new BrownieBakery(bakery);
bakery->createBrownies();

これは問題の良い解決策のように思えますが、私はそれをすることで古い神々を目覚めさせているのだろうかと思っています。アダプタは進むべき道ですか?従うべきより良いパターンはありますか?それとも、本当に弾丸を噛んで、元のインターフェイスを拡張するだけですか?


Delphiにはヘルパークラスがあります。これは、メソッドを実際に変更せずに既存のクラスに追加するようなものです。たとえば、DelphiのグラフィックユニットにはTBitmapクラスが定義されています。TBitmapにFlip関数などを追加するヘル​​パークラスを作成できます。ヘルパークラスがスコープ内にある限り、MyBitmap.Flipを呼び出すことができます。
ビル

回答:


14

正確な要件、言語、および必要な抽象化レベルに応じて、ハンドルボディパターンのいずれかが説明に適合する可能性があります。

純粋なアプローチは、Decoratorパターンで、これはまさにあなたが探しているものを実行し、オブジェクトに動的に責任を追加します。あなたが実際にベーカリーを構築している場合、それは間違いなく過剰であり、アダプタを使用する必要があります。


これはまさに私が必要としていたものです。アダプターを使用すると依存性注入が失敗することに気付きましたが、デコレーターを使用するとそれを回避できます。

5

水平再利用の概念を調査します。ここでは、Traits、まだ実験的でありながらすでに生産に耐えるアスペクト指向プログラミング、時には嫌われているMixinsなどを見つけることができます。

クラスにメソッドを直接追加する方法は、プログラミング言語にも依存します。Rubyは、クラスが実際には存在しないJavascriptのプロトタイプベースの継承中に、モンキーパッチを許可します。オブジェクトを作成し、コピーして追加し続けるだけです。

var MyClass = {
    do : function(){...}
};

var MyNewClass = new MyClass;
MyClass.undo = function(){...};


var my_new_object = new MyNewClass;
my_new_object.do();
my_new_object.undo();

最後に、水平方向の再利用、またはリフレクションによるクラス/オブジェクトの動作の実行時の「変更」と「追加」をエミュレートすることもできます。


4

bakeryインスタンスがその動作を動的に変更する必要があるという要件がある場合(ユーザーのアクションなどに応じて)、Decoratorパターンを使用する必要があります。

Ifはbakery動的にその動作を変更するものではありませんが、変更することはできませんBakery class(外部APIなど)をあなたがために行く必要がありAdapterパターン

場合は、bakery動的にその振る舞いを変更しないと、あなたは変更することができますBakery class(あなたが最初に提案されたように)その後、既存のインターフェイスを拡張する必要があるかご紹介新しいインターフェイスを BrownieInterfaceしてみましょうBakery二つのインタフェースを実装BakeryInterfaceしてをBrownieInterface
それ以外の場合は、正当な理由がないために(Decoratorパターンを使用して)コードに不要な複雑さを追加します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.