8つの構造設計パターンの比較

1.アダプターモード

導入

アダプター パターンは、互換性のないインターフェイスを互換性のあるインターフェイスに変換するために使用される構造設計パターンです。アダプター パターンを使用すると、一方のクラスのインターフェイスを、もう一方のクラスが期待するインターフェイス形式に変換することで、互換性のない 2 つのクラスが連携して動作できるようになります。これにより、互換性のない 2 つのクラスが、既存のコードを変更することなく相互に連携できるようになります。

使用するシーン

アダプター パターンは通常、次のシナリオで使用されます。

  • アダプター パターンは、既存のクラスのインターフェイスを別のインターフェイスに変換する必要がある場合に使用できます。
  • アダプター パターンは、特定の機能を実現するために 1 つ以上の既存のクラスを適応させる必要がある場合に非常に便利なツールです。

コード例

// 目标接口
interface Target {
    
    
    void request();
}

// 需要适配的类
class Adaptee {
    
    
    void specificRequest() {
    
    
        System.out.println("Adaptee's specific request");
    }
}

// 适配器类
class Adapter implements Target {
    
    
    private Adaptee adaptee;

    Adapter(Adaptee adaptee) {
    
    
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
    
    
        adaptee.specificRequest();
    }
}

// 使用适配器
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

アドバンテージ

  • アダプター パターンにより、互換性のない 2 つのインターフェイスが連携して動作できるようになり、コードの再利用性と柔軟性が向上します。
  • アダプター パターンは、クライアント コードを具象クラスから分離し、クライアント コードの具象クラスへの依存を減らすことができます。

欠点がある

  • アダプター パターンでは、アダプターとアダプター間の関係を実装するためにさらに多くのクラスの作成が必要になる場合があり、コードが複雑になります。
  • アダプタ モードでは、アダプタを介してインターフェイス変換を実装する必要があるため、パフォーマンスがある程度低下する可能性があります。

他のモデルを比較する

  • アダプター パターンはブリッジ パターンに似ていますが、アダプター パターンはインターフェイスの変換を処理し、ブリッジ パターンは抽象化と実装の分離を処理します。
  • アダプター パターンはデコレーター パターンに似ていますが、アダプター パターンはインターフェイスの変換に使用され、デコレーター パターンは追加機能をオブジェクトに動的に追加するために使用されます。

2. ブリッジモード

導入

ブリッジパターンとは、抽象部分と実装部分を分離し、独立して変更できるようにした構造的な設計パターンです。ブリッジ パターンでは、抽象化と実装を分離することで、両方を独立して拡張できます。このパターンの中心的な目標は、抽象化と実装を切り離して、相互に独立して変更できるようにすることです。

使用するシーン

ブリッジ モードは通常、次のシナリオで使用されます。

  • ブリッジ パターンは、抽象化と実装を独立して変更できる必要がある場合に使用できます。
  • ブリッジ パターンは、多重継承を回避する必要がある場合に使用できます。
  • クラスを使用する他のクラスに影響を与えずにクラスの実装を変更する必要がある場合は、ブリッジ モードを使用できます。

コード例

// 实现部分接口
interface Implementor {
    
    
    void operationImp();
}

// 具体实现部分A
class ConcreteImplementorA implements Implementor {
    
    
    @Override
    public void operationImp() {
    
    
        System.out.println("ConcreteImplementorA operationImp");
    }
}

// 具体实现部分B
class ConcreteImplementorB implements Implementor {
    
    
    @Override
    public void operationImp() {
    
    
        System.out.println("ConcreteImplementorB operationImp");
    }
}

// 抽象部分接口
abstract class Abstraction {
    
    
    protected Implementor implementor;

    Abstraction(Implementor implementor) {
    
    
        this.implementor = implementor;
    }

    abstract void operation();
}

// 具体抽象部分
class ConcreteAbstraction extends Abstraction {
    
    
    ConcreteAbstraction(Implementor implementor) {
    
    
        super(implementor);
    }

    @Override
    void operation() {
    
    
        implementor.operationImp();
    }
}

// 使用桥接模式
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Implementor implementorA = new ConcreteImplementorA();
        Implementor implementorB = new ConcreteImplementorB();

        Abstraction abstractionA = new ConcreteAbstraction(implementorA);
        abstractionA.operation();

        Abstraction abstractionB = new ConcreteAbstraction(implementorB);
        abstractionB.operation();
    }
}

アドバンテージ

  • ブリッジ モードでは、抽象部分と実装部分を独立して変更できるため、抽象化と実装が分離され、コードの柔軟性とスケーラビリティが向上します。
  • ブリッジ モードでは、クライアント コードを変更せずに、実行時に実装を動的に切り替えることができます。

欠点がある

  • ブリッジ パターンでは、ブリッジするために追加の抽象クラスと実装クラスを作成する必要があるため、システムの複雑さが増す可能性があります。
  • ブリッジ パターンでは、ブリッジを介して抽象化と実装を分離する必要があるため、システムのオーバーヘッドが増加する可能性があります。

他のモデルを比較する

  • Bridge パターンは、Adapter パターンに似ていますが、Bridge パターンは抽象化と実装の分離を処理するために使用され、Adapter パターンはインターフェイスの変換を処理するために使用されます。
  • ブリッジ パターンはデコレータ パターンに似ていますが、ブリッジ パターンは抽象化と実装を分離するために使用され、デコレータ パターンは追加機能をオブジェクトに動的に追加するために使用されます。

3. コンビネーションモード

導入

複合パターンは、オブジェクトをツリー構造に結合して全体の部分の階層を表現できるようにする構造設計パターンです。合成パターンを使用すると、オブジェクトの組み合わせと個々のオブジェクトを統一した方法で操作できます。

使用するシーン

  • オブジェクトをツリー構造で整理したい場合は、組み合わせパターンを使用できます。たとえば、ファイル システム内のファイルとディレクトリは複合パターンを使用して表すことができ、各ディレクトリにはサブディレクトリとファイルを含めることができます。
  • オブジェクトのコレクション全体またはその一部を処理する必要がある場合は、合成パターンを使用できます。たとえば、部門内の全従業員の給与総額を計算するには、部門のツリー構造をたどることによって計算できます。

コード例

// 抽象组件
interface Component {
    
    
    void operation();
}

// 叶子组件
class Leaf implements Component {
    
    
    public void operation() {
    
    
        System.out.println("Leaf operation");
    }
}

// 复合组件
class Composite implements Component {
    
    
    private List<Component> components = new ArrayList<>();

    public void add(Component component) {
    
    
        components.add(component);
    }

    public void remove(Component component) {
    
    
        components.remove(component);
    }

    public void operation() {
    
    
        System.out.println("Composite operation");
        for (Component component : components) {
    
    
            component.operation();
        }
    }
}

// 使用示例
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Component leaf1 = new Leaf();
        Component leaf2 = new Leaf();
        Component composite1 = new Composite();
        Component composite2 = new Composite();
        composite1.add(leaf1);
        composite1.add(leaf2);
        composite2.add(composite1);
        composite2.operation();
    }
}

長所と短所

アドバンテージ:

  • クライアントのコードが簡素化され、クライアントは複合オブジェクトとリーフ オブジェクトを均一に処理できるようになります。
  • コンポーネントの追加や削除は簡単で、クライアントは特定のコンポーネントの種類を気にする必要がありません。

欠点:

  • これによりシステムの複雑さが増し、場合によってはコード保守の負担が増大する可能性があります。
  • 合成パターンを使用する場合、透明性とセキュリティの間にはトレードオフがあり、デザインがあいまいになる可能性があります。

他のモードと比較してみる

  • コンポジション パターンとデコレータ パターン: どちらもオブジェクトのコンポジションを伴いますが、デコレータ パターンはオブジェクトに関数を動的に追加することに重点を置いているのに対し、コンポジション パターンはツリー構造に重点を置いています。
  • 複合パターンとブリッジ パターン: どちらもオブジェクトの合成を伴いますが、ブリッジ パターンは抽象化と実装の分離に重点を置いているのに対し、複合パターンはツリー構造に重点を置いています。
  • コンビネーション モードとフライウェイト モード: どちらもオブジェクトの共有を伴いますが、コンビネーション モードはツリー構造に焦点を当てて全体を共有するのに対し、フライウェイト モードはきめの細かいオブジェクトの共有に重点を置きます。

4. デコレーターモード

導入

Decorator パターンは、オブジェクトに追加機能を動的に追加できるようにする構造設計パターンです。デコレータ パターンは、元のオブジェクトを追加のオブジェクトでラップし、元のオブジェクトのメソッドを呼び出す前後に追加の操作を実行します。

使用するシーン

  • 追加の機能や動作をオブジェクトに動的に追加する必要がある場合は、デコレータ パターンを使用できます。たとえば、実行時にデータベース クエリ結果をキャッシュするには、デコレータ パターンを使用してクエリ操作をラップします。
  • デコレータ パターンは、継承関係をより柔軟にしたい場合に使用できます。デコレータ パターンを使用すると、静的継承の制限を回避して、追加の機能をオブジェクトに動的に追加できます。

コード例

# 抽象组件
class Component:
    def operation(self):
        pass

# 具体组件
class ConcreteComponent(Component):
    def operation(self):
        print("ConcreteComponent operation")

# 装饰器基类
class Decorator(Component):
    def __init__(self, component):
        self._component = component

    def operation(self):
        self._component.operation()

# 具体装饰器
class ConcreteDecoratorA(Decorator):
    def operation(self):
        super().operation()
        print("ConcreteDecoratorA operation")

class ConcreteDecoratorB(Decorator):
    def operation(self):
        print("ConcreteDecoratorB operation")
        super().operation()

# 使用示例
component = ConcreteComponent()
decoratorA = ConcreteDecoratorA(component)
decoratorB = ConcreteDecoratorB(decoratorA)
decoratorB.operation()

長所と短所

アドバンテージ:

  • 元のオブジェクトのコードを変更せずに、追加の機能をオブジェクトに動的に追加できます。
  • デコレータ パターンは開始と終了の原則に従っており、既存のコードを変更せずにオブジェクトに新しい機能を追加できます。

欠点:

  • これによりシステムが複雑になるため、デコレータの過度のネストを避ける必要があります。
  • デコレータの実行順序は最終結果に影響を与える可能性があるため、デコレータの順序に注意する必要があります。

他のモードと比較してみる

  • デコレータ パターンとプロキシ パターン: どちらもオブジェクトのパッケージ化に関係しますが、デコレータ パターンは機能を動的に追加することに重点を置き、プロキシ パターンはオブジェクトへのアクセスを制御することに重点を置きます。
  • デコレータ パターンとアダプタ パターン: どちらもオブジェクトのパッケージ化に関係しますが、アダプタ パターンは、異なるインターフェイスを持つオブジェクトを統一インターフェイスに適応させることに重点を置いているのに対し、デコレータ パターンは機能を動的に追加することに重点を置いています。
  • デコレーター パターンとブリッジ パターン: どちらもオブジェクトのパッケージ化に関係しますが、デコレーター パターンはオブジェクトに機能を動的に追加することに焦点を当てているのに対し、ブリッジ パターンは抽象部分と実装部分を分離することに焦点を当てています。

5. 外観モード

導入

ファサード パターンは、サブシステムの一連のインターフェイスにアクセスするための統一インターフェイスを提供する構造設計パターンです。ファサード パターンは、複雑なサブシステム インターフェイスのセットをカプセル化することで、クライアントとサブシステム間の対話を簡素化します。

使用するシーン

  • 複雑なサブシステムを簡素化し、統一されたインターフェイスを提供したい場合は、ファサード パターンを使用できます。たとえば、クライアントがデータベース、キャッシュ、メッセージ キューなどの複数のコンポーネントと対話する必要がある場合、これらの対話は外観パターンを通じてカプセル化され、外部への統一インターフェイスを提供できます。
  • ファサード パターンは、クライアントとサブシステム間の依存関係を分離する必要がある場合に使用できます。

コード例

# 子系统A
class SubsystemA:
    def methodA(self):
        print("SubsystemA methodA")

# 子系统B
class SubsystemB:
    def methodB(self):
        print("SubsystemB methodB")

# 外观类
class Facade:
    def __init__(self):
        self._subsystemA = SubsystemA()
        self._subsystemB = SubsystemB()

    def operation(self):
        self._subsystemA.methodA()
        self._subsystemB.methodB()

# 使用示例
facade = Facade()
facade.operation()

長所と短所

アドバンテージ:

  • クライアントとサブシステム間の対話が簡素化され、クライアントは外観クラスと対話するだけでよく、サブシステムの詳細を知る必要はありません。
  • クライアントとサブシステム間の依存関係を切り離すことで、クライアントは外観クラスに依存するだけで済みます。

欠点:

  • 外観クラスに依存しすぎるとシステムの保守性が低下する可能性があり、システムの拡張にもつながりません。

他のモードと比較してみる

  • アピアランス モードとアダプタ モード: どちらもカプセル化を伴いますが、アピアランス モードは一連のインターフェイスの統合カプセル化に重点を置いているのに対し、アダプタ モードはさまざまなインターフェイスの適応に重点を置いています。
  • ファサード パターンとメディエーター パターン: どちらもクライアントとサブシステムの依存関係の分離を伴いますが、メディエーター パターンは仲介オブジェクトを介した対話に焦点を当てているのに対し、ファサード パターンは統一インターフェイスの提供に焦点を当てています。
  • ファサード パターンとプロキシ パターン: どちらもカプセル化を伴いますが、プロキシ パターンはオブジェクトへのアクセスの制御に重点を置いているのに対し、ファサード パターンは一連のインターフェイスの統合カプセル化に重点を置いています。

6. フライングダラーモード

導入

フライウェイト パターンは、オブジェクトを共有して多数のきめの細かいオブジェクトをサポートすることでメモリ使用量を削減する構造設計パターンです。Flyweight モードでは、オブジェクトの状態を内部状態と外部状態に分割し、内部状態を共有し、外部状態をクライアントから渡すことができます。

使用するシーン

  • フライウェイト パターンは、多数のきめの細かいオブジェクトを作成する必要があり、内部状態を共有する必要がある場合に使用できます。たとえば、Web ページ エディターのレター オブジェクトは、フライウェイト モードを通じて共有できます。
  • フライウェイト パターンは、オブジェクトの状態のほとんどを外部から渡すことができ、特定のオブジェクトに依存しない場合に使用できます。

コード例

import java.util.HashMap;
import java.util.Map;

// 具体享元类
class ConcreteFlyweight implements Flyweight {
    
    
    private String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
    
    
        this.intrinsicState = intrinsicState;
    }

    public void operation(String extrinsicState) {
    
    
        System.out.println("Intrinsic state: " + intrinsicState);
        System.out.println("Extrinsic state: " + extrinsicState);
    }
}

// 享元工厂
class FlyweightFactory {
    
    
    private Map<String, Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {
    
    
        if (!flyweights.containsKey(key)) {
    
    
            flyweights.put(key, new ConcreteFlyweight(key));
        }
        return flyweights.get(key);
    }
}

// 使用示例
public class Main {
    
    
    public static void main(String[] args) {
    
    
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight flyweight1 = factory.getFlyweight("key1");
        flyweight1.operation("state1");
        Flyweight flyweight2 = factory.getFlyweight("key2");
        flyweight2.operation("state2");
    }
}

長所と短所

アドバンテージ:

  • メモリ使用量を削減し、システムのパフォーマンスを向上させることができます。
  • 大規模で粒度の細かいオブジェクトを使用するシナリオでは、多くのメモリ領域を節約できます。

欠点:

  • これによりシステムの複雑さが増し、外部状態の管理が必要になり、スレッドの安全性の問題が発生する可能性があります。
  • 内部状態と外部状態の間の共有の程度の間にはトレードオフがあります。

他のモードと比較してみる

  • フライウェイト パターンとシングルトン パターン: どちらも共有オブジェクトが関係しますが、シングルトン パターンは単一のインスタンスに焦点を当て、フライウェイト パターンはきめの細かいオブジェクトの共有に焦点を当てます。
  • フライウェイト パターンとプロトタイプ パターン: どちらもオブジェクトの作成を伴いますが、プロトタイプ パターンはオブジェクトのクローン作成に重点を置き、フライウェイト パターンは共有オブジェクトに重点を置きます。
  • フライウェイト モードとプロキシ モード: どちらもオブジェクトのカプセル化を伴いますが、プロキシ モードはオブジェクトへのアクセスの制御に重点を置き、フライウェイト モードは共有オブジェクトの内部状態に重点を置きます。

7. 代理店モデル

導入

プロキシ パターンは、オブジェクトへのアクセスを制御し、オブジェクトにアクセスするときに追加の処理を追加するために使用される構造設計パターンです。プロキシ モードでは、プロキシ オブジェクトを作成することで元のオブジェクトが置き換えられます。クライアントは、プロキシ オブジェクトを使用して操作を実行します。プロキシ オブジェクトは、操作の実行前後に追加のロジックを追加できます。

使用するシーン

  • オブジェクトへのアクセスを制御する必要がある場合は、プロキシ パターンを使用できます。たとえば、機密データへのアクセスを制限するには、機密データにアクセスする前に認証するプロキシ オブジェクトを作成できます。
  • プロキシ パターンは、オブジェクトにアクセスするときに追加の処理ロジックを追加する必要がある場合に使用できます。たとえば、結果のログ記録やキャッシュなどの操作は、プロキシ オブジェクト内で完了できます。

コード例

// 抽象主题
interface Subject {
    
    
    void operation();
}

// 具体主题
class RealSubject implements Subject {
    
    
    public void operation() {
    
    
        System.out.println("RealSubject operation");
    }
}

// 代理类
class Proxy implements Subject {
    
    
    private RealSubject realSubject;

    public void operation() {
    
    
        beforeOperation();
        if (realSubject == null) {
    
    
            realSubject = new RealSubject();
        }
        realSubject.operation();
        afterOperation();
    }

    private void beforeOperation() {
    
    
        System.out.println("Proxy before operation");
    }

    private void afterOperation() {
    
    
        System.out.println("Proxy after operation");
    }
}

// 使用示例
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Proxy proxy = new Proxy();
        proxy.operation();
    }
}

長所と短所

アドバンテージ:

  • オブジェクトへのアクセスを制御し、オブジェクトにアクセスするときに追加の処理を追加できます。
  • プロキシ モードはオープンとクローズの原則に従い、クライアントは元のオブジェクトのコードを変更する必要はなく、プロキシ オブジェクトを使用するだけで済みます。

欠点:

  • プロキシ パターンでは、新しいオブジェクトが導入されるため、システムが複雑になります。
  • プロキシ オブジェクトには追加の処理ロジックが必要となるため、システムのパフォーマンスが低下する可能性があります。

他のモードと比較してみる

  • プロキシ パターンとデコレータ パターン: どちらもオブジェクトのカプセル化を伴いますが、デコレータ パターンはオブジェクトに機能を動的に追加することに重点を置き、プロキシ パターンはオブジェクトへのアクセスの制御に重点を置きます。
  • プロキシ モードとアダプタ モード: どちらもオブジェクトの変換を伴いますが、アダプタ モードは異なるインターフェイスを持つオブジェクトを統一インターフェイスに適応させることに重点を置き、プロキシ モードはオブジェクトへのアクセスの制御に重点を置きます。
  • プロキシ モードとアピアランス モード: どちらもカプセル化を伴いますが、アピアランス モードは統一インターフェイスの提供に重点を置いているのに対し、プロキシ モードはオブジェクトへのアクセスの制御に重点を置いています。

8. デコレーターモード

導入

Decorator パターンは、オブジェクトに追加機能を動的に追加できるようにする構造設計パターンです。デコレータ パターンは、元のオブジェクトを追加のオブジェクトでラップし、元のオブジェクトのメソッドを呼び出す前後に追加の操作を実行します。

使用するシーン

  • 追加の機能や動作をオブジェクトに動的に追加する必要がある場合は、デコレータ パターンを使用できます。たとえば、元のクラスを変更せずに、ログ記録、パフォーマンス統計、キャッシュ、その他の関数をオブジェクトに追加します。
  • デコレータ パターンは、継承関係をより柔軟にしたい場合に使用できます。デコレータ パターンを使用すると、追加の関数をオブジェクトに動的に追加でき、静的継承によって引き起こされるクラスの爆発の問題を回避できます。

コード例

# 抽象组件
class Component:
    def operation(self):
        pass

# 具体组件
class ConcreteComponent(Component):
    def operation(self):
        print("ConcreteComponent operation")

# 装饰器基类
class Decorator(Component):
    def __init__(self, component):
        self._component = component

    def operation(self):
        self._component.operation()

# 具体装饰器
class ConcreteDecoratorA(Decorator):
    def operation(self):
        print("ConcreteDecoratorA operation")
        super().operation()

class ConcreteDecoratorB(Decorator):
    def operation(self):
        super().operation()
        print("ConcreteDecoratorB operation")

# 使用示例
component = ConcreteComponent()
decoratorA = ConcreteDecoratorA(component)
decoratorB = ConcreteDecoratorB(decoratorA)
decoratorB.operation()

長所と短所

アドバンテージ:

  • 元のオブジェクトのコードを変更せずに、追加の機能をオブジェクトに動的に追加できます。
  • デコレータ パターンは開始と終了の原則に従っており、既存のコードを変更せずにオブジェクトに新しい機能を追加できます。

欠点:

  • これによりシステムが複雑になるため、デコレータの過度のネストを避ける必要があります。
  • デコレータの実行順序は最終結果に影響を与える可能性があるため、デコレータの順序に注意する必要があります。

他のモードと比較してみる

  • デコレータ パターンとプロキシ パターン: どちらもオブジェクトのパッケージ化に関係しますが、デコレータ パターンは機能を動的に追加することに重点を置き、プロキシ パターンはオブジェクトへのアクセスを制御することに重点を置きます。
  • デコレータ パターンとアダプタ パターン: どちらもオブジェクトのパッケージ化に関係しますが、アダプタ パターンは、異なるインターフェイスを持つオブジェクトを統一インターフェイスに適応させることに重点を置いているのに対し、デコレータ パターンは機能を動的に追加することに重点を置いています。
  • デコレーター パターンとブリッジ パターン: どちらもオブジェクトのパッケージ化に関係しますが、デコレーター パターンはオブジェクトに機能を動的に追加することに焦点を当てているのに対し、ブリッジ パターンは抽象部分と実装部分を分離することに焦点を当てています。

おすすめ

転載: blog.csdn.net/jingyoushui/article/details/133048945