web-dev-qa-db-ja.com

デコレータパターンを使用する場合

私は自分のデザインパターンを検討していますが、コーディングでまだ真剣に使用していないパターンの1つは、デコレータパターンです。

私はパターンを理解していますが、デコレータパターンが最良/最適/エレガントなソリューションであるという現実の世界の時間のいくつかの良い具体例が知りたいです。デコレータパターンの必要性が本当に便利な特定の状況。

ありがとう。

56
Alex Baranosky

デコレータパターンは、ストリームで頻繁に使用されます。ストリームをストリームでラップして、機能を追加できます。 .Netフレームワークでこれを見てきました-私が知る限り、これは他の場所で発生します。私のお気に入りは、圧縮を追加するためにFileStreamの周りでGZipStreamを使用することです。

36
Charlie Salts

オブジェクトのクラスではなく、特定のオブジェクトに機能を追加するために、デコレータパターンが使用されます。オブジェクトをサブクラス化することにより、オブジェクトのクラス全体に機能を追加するのは簡単ですが、この方法で単一のオブジェクトを拡張することは不可能です。デコレータパターンを使用すると、1つのオブジェクトに機能を追加し、他のオブジェクトを変更しないでおくことができます。

Javaでのデコレータパターンの典型的な例は、Java I/O Streams実装です。

FileReader       frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);

上記のコードは、ファイルから読み取り、行番号を追跡するリーダー(lrdr)を作成します。 1行目はファイルリーダー(frdr)を作成し、2行目は行番号の追跡を追加します。

実際、Java I/OクラスのJavaソースコードを確認することを強くお勧めします。

68
Pascal Thivent

最近、次のCommandProcessorインターフェースを使用するWebサービスでデコレーターパターンを使用しました。

public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);

基本的に、CommandProcessorはリクエストを受信して​​適切なコマンドを作成し、コマンドを実行して適切なレスポンスを作成し、レスポンスを送信します。タイミングを追加してログに記録する場合、既存のCommandProcessorをコンポーネントとして使用するTimerDecoratorを作成しました。 TimerDecoratorはCommandProcessorインターフェイスを実装しますが、タイミングを追加するだけで、実際のCommandProcessorであるターゲットを呼び出します。このようなもの:

public class TimerDecorator implements CommandProcessor {
   private CommandProcessor target;
   private Timer timer;

   public TimerDecorator(CommandProcessor processor) {
      this.target = processor;
      this.timer = new Timer();
   }

   public Command receive(Request request) {
      this.timer.start();
      return this.target.receive(request);
   }

   public Response execute(Command command) {
      return this.target.execute(command);
   }

   public void respond(Response response) {
      this.target.response(response);
      this.timer.stop();
      // log timer
   }

}

したがって、実際のCommandProcessorはTimerDecorator内にラップされ、TimerDecoratorをターゲットCommandProcessorと同じように扱うことができますが、タイミングロジックが追加されました。

28
Kaleb Brasee

Decorator patternは、オブジェクトの既存の機能に影響を与えることなく、実行時にオブジェクトの機能を動的に変更します。

主なユースケース:

  1. 追加の機能/責任を動的に追加します
  2. 機能/責任を動的に削除する
  3. サブクラス化を避けすぎて、責任を追加しないでください。

欠点:

  1. オープンクローズド原則の過剰使用(拡張のためにオープン、変更のためにクローズ)。コードが変更される可能性が最も低い場所では、この機能を控えめに使用してください。
  2. 少人数のクラスが多すぎるため、メンテナンスオーバーヘッドが追加されます

実世界の例:飲料の価格を計算します。複数のフレーバーが含まれている場合があります。

abstract class Beverage {
    protected String name;
    protected int price;
    public Beverage(){

    }
    public  Beverage(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    protected void setPrice(int price){
        this.price = price;
    }
    protected int getPrice(){
        return price;
    }
    protected abstract void decorateBeverage();

}
class Tea extends Beverage{
    public Tea(String name){
        super(name);
        setPrice(10);
    }
    public void decorateBeverage(){
        System.out.println("Cost of:"+ name +":"+ price);
        // You can add some more functionality
    }
}
class Coffee extends Beverage{
    public Coffee(String name){
        super(name);
        setPrice(15);
    }
    public void decorateBeverage(){
        System.out.println("Cost of:"+ name +":"+ price);
        // You can add some more functionality
    }   
}
abstract class BeverageDecorator extends Beverage {
    protected Beverage beverage;
    public BeverageDecorator(Beverage beverage){    
        this.beverage = beverage;   
        setName(beverage.getName()+"+"+getDecoratedName());
        setPrice(beverage.getPrice()+getIncrementPrice());
    }
    public void decorateBeverage(){
        beverage.decorateBeverage();
        System.out.println("Cost of:"+getName()+":"+getPrice());
    }   
    public abstract int getIncrementPrice();
    public abstract String getDecoratedName();
}
class SugarDecorator extends BeverageDecorator{
    public SugarDecorator(Beverage beverage){
        super(beverage);
    }
    public void decorateBeverage(){
        super.decorateBeverage();
        decorateSugar();        
    }
    public void decorateSugar(){
        System.out.println("Added Sugar to:"+beverage.getName());
    }
    public int getIncrementPrice(){
        return 5;
    }
    public String getDecoratedName(){
        return "Sugar";
    }
}
class LemonDecorator extends BeverageDecorator{
    public LemonDecorator(Beverage beverage){
        super(beverage);
    }
    public void decorateBeverage(){
        super.decorateBeverage();
        decorateLemon();    
    }
    public void decorateLemon(){
        System.out.println("Added Lemon to:"+beverage.getName());       
    }
    public int getIncrementPrice(){
        return 3;
    }
    public String getDecoratedName(){
        return "Lemon";
    }
}

public class VendingMachineDecorator {  
    public static void main(String args[]){
        Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
        beverage.decorateBeverage();
        beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
        beverage.decorateBeverage();
    }
}

出力:

Cost of:Assam Tea:10
Cost of:Assam Tea+Lemon:13
Added Lemon to:Assam Tea
Cost of:Assam Tea+Lemon+Sugar:18
Added Sugar to:Assam Tea+Lemon
Cost of:Cappuccino:15
Cost of:Cappuccino+Lemon:18
Added Lemon to:Cappuccino
Cost of:Cappuccino+Lemon+Sugar:23
Added Sugar to:Cappuccino+Lemon

この例では、飲料に多くのフレーバーを追加した後、Vending Machineで飲料のコストを計算します。

上記の例:

茶のコスト= 10、レモン= 3、砂糖=5。砂糖+レモン+茶を作る場合、18がかかります。

コーヒーのコスト= 15、レモン= 3、砂糖=5。砂糖+レモン+コーヒーを作る場合、コストは23です。

両方の飲料(Tea and Coffee)に同じデコレーターを使用することにより、サブクラスの数が削減されました。 Decoratorパターンがない場合は、異なる組み合わせに対して異なるサブクラスが必要です。

組み合わせは次のようになります。

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

等.

両方の飲料に同じDecoratorを使用することにより、サブクラスの数が削減されました。このパターンで使用されているinheritanceコンセプトではなく、compositionが原因である可能性があります。

関連するSEの質問:

IOの装飾パターン

便利なリンク:

design-patterns-decorator by dzone

装飾 ソースメイキングによる

oodesign 記事

8
Ravindra babu

デコレータはシンプルでありながら非常に強力です。懸念の分離を達成するための鍵であり、オープンクローズドプリンシプルにとって不可欠なツールです。製品を注文する一般的な例を見てみましょう。

IOrderGateway
{
    void PlaceOrder(Order order);
{

主な実装:AmazonAffiliateOrderGateway

可能なデコレータは次のとおりです。

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

here のより詳細な例は、注文の完了時にギフトカードを使用して作成された注文の連絡先を保存するデコレータを示しています。

class OrderWithGiftCardGateway extends OrderGatewayDecorator
{
    ...

    public function createOrder(CreateOrderRequest $order)
    {
        if ($this->containsGiftCard($order))
        {
            $this->addContactToFolder($order);
        }

        return parent::createOrder($order);
    }
}
7
Mike Valenty

Zend Frameworkはフォーム要素にデコレーターを使用します

詳細: http://framework.zend.com/manual/en/zend.form.decorators.html

3
Mike B
  1. 個々のオブジェクトに責任を動的かつ透過的に追加します。
  2. 撤回できる責任に対して。
  3. サブクラス化による拡張が実用的でない場合。場合によっては、多数の独立した拡張が可能であり、あらゆる組み合わせをサポートするためにサブクラスの爆発的な増加が発生します。
2
Karim Haddad

デコレータパターンは、C#言語自体によって使用されます。 C#のストリームI/Oクラスを装飾するために使用されます。装飾されたバージョンは、BufferedStream、FileStrem、MemoryStrem、NetworkStream、およびCryptoStreamクラスです。

これらのサブクラスは、Streamクラスを継承し、Streamクラスのインスタンスも含みます。

続きを読む こちら

0
Ayushmati