web-dev-qa-db-ja.com

BuilderとDecoratorのパターン

From いつビルダーパターンを使用しますか?

ピザの例にはビルダーパターンが適していると言われています。

デコレータを使ってみませんか?チーズ、ペパロニ、ベーコンをベースピザの追加の装飾として扱うことによって。

チーズ/ペパロニを別々に作らなければならないのはそのためですか。既製で入手できるので、別々に作る必要はないと思います。

Plsは明確にします。また、デコレータパターンの実際の良い例と、それがその特定の例に適している理由を探しています。ありがとうございました。

37
bjskishore123

ウィキペディアのデコレータパターンの記事から:

オブジェクト指向プログラミングでは、デコレータパターンは、新しい/追加の動作を既存のオブジェクトに動的に追加できるようにするデザインパターンです。

完全に組み立てられた後、ピザにトッピングを追加する必要はありません。ピザの半分を食べてから、別のトッピングを追加することはありません。

言い換えると、ビルダーパターンを使用すると、独立した方向に拡張可能なオブジェクトを簡単に作成できますat作成時間、デコレータパターンを使用すると、オブジェクトの機能に拡張機能を追加できます構築時間。デコレータパターンを使用してオブジェクトを構築すると、必要なすべてのデコレータが配置されるまでオブジェクトが一貫性のない(または少なくとも正しくない)状態のままになるため、不適切です。これは、セッターを使用してオプションのコンストラクタ引数を指定するJavaBeanの問題と同様です。

44
Philip Potter

あなたは2つの非常に異なることを混同しています。 GoFはBuilderを作成パターンとして分類し、Decoratorは構造パターンとして分類します。それらは次のように説明されています(Gamma et al、page 1):

Builder(97)複雑なオブジェクトの構築をその表現から分離して、同じ構築プロセスで異なる表現を作成できるようにします。

デコレータ(175)オブジェクトに追加の責任を動的に付加します。 デコレータは、機能を拡張するためのサブクラス化の柔軟な代替手段を提供します。

デコレータの強調に注意してください。これは、サブクラス化の柔軟な代替手段です。サブクラス化は、is-a関係をモデル化するために使用されます。チーズはピザではありません。ピザは構成いくつかの材料で構成されており、通常は構成を使用してモデル化されます。

標準化された方法でそれらを構築する必要が生じるほど膨大な数の成分があるため、ビルダーパターンはここに関連しています。

デコレータの実際の例を取り上げるために、最近、jdbcを使用して実行されたクエリをJavaアプリケーションに記録したいと思いました。これは、Connectionインターフェイスを拡張するLoggingConnectionというクラスを実装することで実現しました。

public class LoggingConnection implements Connection
{
    public static class LogEntry
    {
        public String sql;
        public int invocationCount;
        public double avgTime;
        public double maxTime;
    }

    private Connection delegate;

    private Map<String, LogEntry> log;

    public LoggingConnection(Connection delegate)
    {
        this.delegate = delegate;
        this.log = new HashMap<String, LogEntry>();
    }

    public Map<String, LogEntry> getLog()
    {
        return log;
    }

    @Override
    public void clearWarnings()
    throws SQLException
    {
        delegate.clearWarnings();
    }

    @Override
    public void close()
    throws SQLException
    {
        delegate.close();
    }

    // forwarding declarations to all other methods declared in the interface
    ...
}

これにより、接続の具体的な実装を渡し、実行時にその機能を拡張することができます。どの接続オブジェクトが実際に返されるかが必ずしもわからないため、このコンテキストではサブクラス化は問題になります。これは、DriverManagerファクトリを使用して構築されているためです。

Connection conn = DriverManger.getConnection(dsn);

この場合、connオブジェクトはドライバーに含まれている実装であり、私は一般的にその名前を知りません。デコレータアプローチの利点は、私が知る必要がないことと、特定の実装に結び付けられていないことです。

21
Emil H

BuilderDecoratorの主要な特性を見ていきましょう。

ビルダー :(作成パターン)

  1. エラーが発生しやすい可能性があるクライアントプログラムからファクトリクラスに渡す引数が多すぎます
  2. すべてのパラメータを強制的に送信するファクトリとは異なり、一部のパラメータはオプションである可能性があります
  3. オブジェクトは重く、その作成は複雑です。例えばさまざまな種類のピザを作る

デコレータ :(構造パターン)

  1. 実行時にオブジェクトに動作を追加します。継承はこの機能を実現するための鍵であり、これはこのパターンの長所と短所の両方です。
  2. インターフェイスの動作を強化します。
  3. Decoratorは、コンポーネントが1つしかない縮退したコンポジットと見なすことができます。ただし、デコレータは追加の責任を追加します。これはオブジェクトの集約を目的としたものではありません。
  4. デコレータは再帰的合成をサポートします
  5. Decoratorは、サブクラス化せずにオブジェクトに責任を追加できるように設計されています

いつ使用するかデコレータ

  1. オブジェクトの責任と動作は動的に追加/削除する必要があります
  2. 具体的な実装は、責任と行動から切り離す必要があります
  3. サブクラス化のコストが高すぎて、責任を動的に追加/削除できない場合

クエリに戻る:

BuilderはPizzaの正しい作成パターンです。ピザは最初は必須の材料(パンなど)で作成されます。チーズ、ペパロニ、ベーコンはオプションの材料ですが、それでもビルドプロセス中にピザの一部にすることができます。

Decoratorは、作成済みのオブジェクトの実行時に動的な責任を追加するのに役立ちます。

例えば:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));

詳細については、以下の投稿を参照してください。

ビルダーを別のクラスに保持する(流暢なインターフェース)

いつデコレータパターンを使用するのですか?

7
Ravindra babu

ビルダーパターンは特にビルドに使用され、デコレーターはビルド後に特別な機能を追加するために使用されます。たとえば、上記のピザの例では、問題のドメインに基づいて2つのパターンのいずれかを使用することを決定できます。

チリフレークがピザに不可欠であり、同様にピザに追加する材料がたくさんあり、そのうちのいくつかがピザを食用(意味のある状態)にするための基本的なものである場合は、ビルダーを使用することをお勧めします。ピザができたら、後でトマトソースやオリーブなどのさまざまなトッピングで飾ることができます...

また、それらを一緒に使用できることは上記で正しく述べられていますが、これにより複雑さが増します。したがって、問題の領域で必要な場合にのみパターンを賢く使用する必要があります。そうしないと、単純な構造で十分です。

2
andy2375