プロキシ、デコレータ、アダプタ、ブリッジのパターン

1前書き

この記事では、Javaの構造設計パターンに焦点を当てて、これらが何であるか、およびそれらのうちのいくつかの基本的な違いについて説明します。

2構造設計パターン

Gang Of Four(GoF)によると、デザインパターンは3つのタイプに分類できます。

  1. 創作

  2. 構造的

  3. 行動的

簡単に言えば、構造パターンはクラスとオブジェクトの構成を扱います。それらは、抽象化のためにオブジェクトの構成と継承を使用するさまざまな方法を提供します。

3プロキシパターン

このパターンで、私たちは他のリソース、例えばファイル、接続へのインターフェースとして機能する仲介者を作成します。この二次アクセスは、実際のコンポーネントに代わるものを提供し、それを根本的な複雑さから保護します。

3.1. プロキシパターンの例

初期設定が必要な、重いJavaオブジェクト(JDBC接続や SessionFactory など)を考えてください。

そのようなオブジェクトはオンデマンドで初期化したいだけなので、一度それらが初期化されたら、すべての呼び出しにそれらを再利用したいと思います。

リンク:/uploads/MrvrsH6-300x140.jpg%20300w[]

それでは、このオブジェクトの簡単なインターフェースと設定を作成しましょう。

public interface ExpensiveObject {
    void process();
}

そして、大きな初期設定でこのインターフェースを実装しました:

public class ExpensiveObjectImpl implements ExpensiveObject {

    public ExpensiveObjectImpl() {
        heavyInitialConfiguration();
    }

    @Override
    public void process() {
        LOG.info("processing complete.");
    }

    private void heavyInitialConfiguration() {
        LOG.info("Loading initial configuration...");
    }

}

これで、プロキシパターンを利用してオブジェクトをオンデマンドで初期化します。

public class ExpensiveObjectProxy implements ExpensiveObject {
    private static ExpensiveObject object;

    @Override
    public void process() {
        if (object == null) {
            object = new ExpensiveObjectImpl();
        }
        object.process();
    }
}

クライアントが process() メソッドを呼び出すときはいつでも、処理を見ることができるだけで、初期設定は常に隠されたままになります。

public static void main(String[]args) {
    ExpensiveObject object = new ExpensiveObjectProxy();
    object.process();
    object.process();
}

process() メソッドを2回呼び出していることに注意してください。舞台裏では、設定部分はオブジェクトが最初に初期化されたときに一度だけ発生します。

それ以降の呼び出しごとに、このパターンは初期設定をスキップし、処理のみが行われます。

Loading initial configuration...
processing complete.
processing complete.

3.2. プロキシを使用する場合

  • パターンの使い方** を理解することは重要です。

  • いつ** 使用するかを理解することが重要です。

プロキシパターンを使用するタイミングについて説明しましょう。

  • ** 複雑なオブジェクトや重いオブジェクトを単純化したものが欲しいとき。

この場合、我々はそれをロードするスケルトンオブジェクトで表現することができます。 元のオブジェクトはオンデマンドで、遅延初期化とも呼ばれます。これは 仮想プロキシとして知られている 元のオブジェクトが異なるアドレス空間に存在する場合

ローカルに表現したい 。すべてを実行するプロキシを作成できます 作成やメンテナンスのような必要な定型文 接続、エンコーディング、デコーディングなど ローカルアドレス空間に存在していました。これはリモートと呼ばれます プロキシ ** 元の基礎にセキュリティの層を追加したい場合

クライアントのアクセス権に基づいて制御されたアクセスを提供するオブジェクト** 。これは保護プロキシと呼ばれます

3.3. 差別化のポイント

  • プロキシは、それが保持しているオブジェクトと同じインターフェイスを提供します。

を参照しても、データを変更することはありません。入った を変更して装飾するアダプタとデコレータのパターンとは対照的 それぞれ既存のインスタンスの機能 ** 代理人は通常、本物の主題についての情報を持っています。

デコレータとアダプタは実行時にインジェクトされるのに対し、実際のオブジェクトのインタフェースのみを知っているため

4.デコレータパターン

  • このパターンはオブジェクトの振る舞いを向上させるのに役立ちます** 詳細な概要については、ここで焦点を絞ったチュートリアルを見てください。

  • 差別化のポイント:**

  • ProxyとDecoratorのパターンは似たような構造ですが、

意図が異なります。プロキシの主な目的は簡単にすることです 使用またはアクセス制御の場合、デコレータは追加の 責任 ** ProxyとAdapterの両方のパターンは、オリジナルへの参照を保持します。

物 ** このパターンのすべてのデコレータは再帰的に使うことができます。

他のモデルでは不可能な無限の回数

5アダプターパターン

  • アダプタパターンは、直接接続できない2つの互換性のないインタフェース間のコネクタとして機能します** アダプタは、既存のクラスを新しいインタフェースでラップするため、クライアントのインタフェースと互換性があります。

このパターンを使用する主な目的は、既存のインターフェースをクライアントが期待する別のインターフェースに変換することです。通常、アプリケーションが設計されると実装されます。

5.1. アダプターパターン例

米国で開発されたアプリで、時速マイル(MPH)で高級車の最高速度を返すシナリオがあるとします。今度は私達はイギリスの私達の顧客のための同じアプリを使用する必要がありますが、同じ結果を望んでいますが時速キロメートル(km/h)で。

この問題に対処するために、値を変換して目的の結果を得るためのアダプタを作成します。

リンク:/uploads/Rpt__ER5p-768x246.jpg%20768w[]

まず、高級車の速度を時速マイルで返すことになっているオリジナルのインターフェース Movable を作成します。

public interface Movable {
   //returns speed in MPH
    double getSpeed();
}

このインターフェースの具体的な実装を1つ作成します。

public class BugattiVeyron implements Movable {

    @Override
    public double getSpeed() {
        return 268;
    }
}

これで、同じ Movable クラスに基づくアダプタインターフェイス MovableAdapter を作成しましょう。さまざまなシナリオでさまざまな結果が得られるように、少し変更することがあります。

public interface MovableAdapter {
   //returns speed in KM/H
    double getSpeed();
}

このインタフェースの実装は、変換に使用されるプライベートメソッド convertMPHtoKMPH() で構成されます。

public class MovableAdapterImpl implements MovableAdapter {
    private Movable luxuryCars;

   //standard constructors

    @Override
    public double getSpeed() {
        return convertMPHtoKMPH(luxuryCars.getSpeed());
    }

    private double convertMPHtoKMPH(double mph) {
        return mph **  1.60934;
    }
}

これで、アダプタで定義されているメソッドのみを使用するようになり、変換後の速度が得られます。この場合、次のようになります。

@Test
public void whenConvertingMPHToKMPH__thenSuccessfullyConverted() {
    Movable bugattiVeyron = new BugattiVeyron();
    MovableAdapter bugattiVeyronAdapter = new MovableAdapterImpl(bugattiVeyron);

    assertEquals(bugattiVeyronAdapter.getSpeed(), 431.30312, 0.00001);
}

ここで気付くことができるように、私達のアダプターはこの特定の場合のために 268 mph 431 km/h に変換します。

5.2. アダプタパターンを使用する場合

  • ** 外部コンポーネントが魅力的な機能を提供する場合

再利用したいのですが、現在のアプリケーションと互換性がありません** 。

それぞれに対応するように適切なアダプタを開発することができます。 その他の ** 当社のアプリケーションが当社のインターフェースと互換性がない場合

クライアントは期待しています ** 作成せずにアプリケーションでレガシーコードを再利用する場合

元のコードの変更

5.3. 差別化のポイント

  • プロキシは同じインタフェースを提供しますが、アダプタは異なるインタフェースを提供します

クライアントと互換性のあるインターフェイス ** アダプタパターンはアプリケーションコンポーネントが設計された後に使用されます

ソースコードを変更せずにそれらを使用できるようにするためです。これは、コンポーネントが設計される前に使用されるブリッジパターンとは対照的です。

6. 橋の模様

Gang of Four(GoF)によって導入されたブリッジデザインパターンの正式な定義は、その実装から抽象化を切り離して、2つが独立して変わるようにすることです。

これは、責任をさまざまな抽象クラスに分離するためにOOPの原則を使用するブリッジインターフェースを作成することを意味します。

6.1. ブリッジパターン例

ブリッジパターンについては、2層の抽象化を検討します。ひとつは幾何学的形状(三角形や四角形など)で、さまざまな色で塗りつぶされています(2番目の抽象化レイヤ)

リンク:/uploads/zfq OUu M-768x226.jpg%20768w[]

まず、カラーインターフェイスを定義します。

public interface Color {
    String fill();
}

それでは、このインターフェース用の具象クラスを作成しましょう。

public class Blue implements Color {
    @Override
    public String fill() {
        return "Color is Blue";
    }
}

それでは、 Color オブジェクトへの参照(ブリッジ)からなる抽象 Shape クラスを作成しましょう。

public abstract class Shape {
    protected Color color;

   //standard constructors

    abstract public String draw();
}

Color インターフェースのメソッドも利用する Shape インターフェースの具象クラスを作成しましょう。

public class Square extends Shape {

    public Square(Color color) {
        super(color);
    }

    @Override
    public String draw() {
        return "Square drawn. " + color.fill();
    }
}

このパターンでは、次のようになります。

@Test
public void whenBridgePatternInvoked__thenConfigSuccess() {
   //a square with red color
    Shape square = new Square(new Red());

    assertEquals(square.draw(), "Square drawn. Color is Red");
}

ここでは、ブリッジパターンを使用し、目的のカラーオブジェクトを渡します。

出力からわかるように、形状は希望の色で描画されます。

Square drawn. Color: Red
Triangle drawn. Color: Blue

6.2. ブリッジデザインパターンを使用する場合

  • 親抽象クラスに一連の基本規則を定義させたい場合は、

そしてルールを追加するための具体的なクラス ** オブジェクトへの参照を持つ抽象クラスがあるとき、

そしてそれはそれぞれの具象クラスで定義される抽象メソッドを持ちます

6.3. 差別化のポイント

  • ブリッジパターンはアプリケーションが実装される前にのみ実装できます。

設計。

抽象化と実装を独立して変更することができます。

アダプタパターンは互換性のないクラスが一緒に動作することを可能にしますが

7. 結論

この記事では、構造設計パターンとそのタイプ間の違いに焦点を当てました。

いつものように、このチュートリアルの完全な実装はhttps://github.com/eugenp/tutorials/tree/master/patterns/design-patterns[Githubについて]で見つけることができます。