シリーズ記事ディレクトリ
C++ スキル シリーズ
Linux 通信アーキテクチャ シリーズ
C++ 高性能最適化プログラミング シリーズ
ソフトウェア アーキテクチャの深い理解設計シリーズ
高度な C++ 同時スレッド プログラミング
設計パターン シリーズ
ご注目をお待ちしております!!!
现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.
構造設計モードの橋梁モード
1. ブリッジモードの概要
⚠️ 意图:
「抽象化を実装から切り離して、両者が独立して変化できるようにします。」抽象化
この文には、 、 、实现化
という3 つのキーワードがあります脱耦
。
⚠️ 主要解决:
変更が多数ある場合、継承を使用するとクラス爆発の問題が発生し、柔軟な拡張ができません。
⚠️ 何时使用:
実装システムは複数の観点から分類される場合があり、それぞれが異なる場合があります。
⚠️ 如何解决:
この多角的な分類を分離し、それらを独立して変化させ、それらの間の結合を減らします。継承関係を合成関係に単純化します。
ブリッジモードでの実装とは、抽象基本クラスの具体的なサブクラスによる抽象基本クラスの仮想関数(インターフェース)の実装ではなく、ユーザーのニーズをどのように実現するかを指します。つまり、Abstractionのインターフェース機能は具体的なImplementクラスで実現され、組み合わせ(委譲)によって実現されるため、ブリッジモードでの実現は基底クラスの継承や基底クラスの実現を参照しません。インターフェイスだけでなく、ユーザーのニーズをオブジェクトの組み合わせによって実現します。
ブリッジ モードは、継承関係を合成関係に変換することで、システム間の結合を減らし、コードの記述量を削減します。合成 (デリゲーション) を使用して抽象化と実装を完全に分離すると、抽象化と実装を独立して変更できるという利点があり、システムの結合も大幅に軽減されます。
抽象化をその実装から分離して、それらが独立して変更できるようにします。抽象化は実装から分離されており、new RefinedAbstractionA(imp)、new RefinedAbstractionB(imp)のように抽象化を変更できますが、new ConcreteImplementA()、new ConcreteImplementB()のように実装部分を独立して変更することもできます。
2. ブリッジモードのメリットとデメリット
2.1 利点
-
インターフェースとその実装部分を分離します。実装は必ずしもインターフェイスにバインドされる必要はありません。抽象クラスの実装は実行時に構成でき、オブジェクトは実行時に実装を変更することもできます。Abstraction と Implement を分離すると、実装部分のコンパイル時間への依存が軽減され、実装クラスを変更するときに Abstraction クラスとそのクライアント プログラムを再コンパイルする必要がなくなります。このプロパティは、クラス ライブラリの異なるバージョン間でバイナリ互換性を確保するために存在する必要があります。さらに、インターフェイスと実装の分離は階層化に役立ち、その結果、より適切に構造化されたシステムが得られ、システムの高レベル部分は抽象化と実装のみを知る必要があります。
-
スケーラビリティを向上させます。抽象化階層と実装階層は個別に拡張できます。
-
実装の詳細はクライアントに対して透過的です。共有 Implement オブジェクトや対応する参照カウント メカニズムなどの実装の詳細は、クライアントから隠すことができます。
-
共有できる変更部分が抽出され、コード内の情報の重複が削減されます。
-
オブジェクトの特定の実装はより柔軟になり、複数の要素の変化する要件を満たすことができます。
2.2 欠点
-
ブリッジモードの導入により、システムの理解や設計の難易度が高まり、抽象層で集約関係が構築されるため、開発者は抽象化に向けた設計やプログラミングが必要になります。
-
ブリッジ パターンでは、システム内で独立して変化する 2 つの次元を正確に識別する必要があるため、その使用範囲には一定の制限があります。
-
クライアントはどのタイプの実装を選択するかを知っている必要があります
3. ブリッジモードの使用シナリオ
-
オブジェクトに複数の変数がある場合は、具体的な実装ではなく抽象的な実装に依存することを検討してください。例えば、携帯電話のブランドにはブランドと機能の2つの変化要因があります。
-
複数の変更要素が複数のオブジェクト間で共有される場合は、変更部分を抽象化して集約または結合することを検討してください。
-
オブジェクトの複数の変化要因が動的に変化する可能性があることを考慮した場合、例えば、携帯電話のブランドが変わり、携帯電話の機能も変化するため、それぞれの変動要因を分離して変化させることを検討してください。独立して。
-
Abstract Factory パターンを使用して、特定のブリッジ パターンを作成および構成できます。
-
アダプター パターンは、無関係なクラスの連携を支援するために使用され、通常はシステム設計の完了後に使用されます。ただし、ブリッジパターンはシステムの開始時に使用されるため、抽象インターフェースと実装部分は独立して変更できます。
-
ブリッジ モードとデコレーション モードはどちらも、ある程度サブクラスの数を減らし、複雑な継承関係を回避しますが、解決策は異なります。デコレーション モードでは、新しい関数のニーズを満たすために、サブクラスの基本クラスよりも余分な部分を別のクラスに置きます。新しい関数を記述するクラスが基本クラスのオブジェクトにカプセル化されるとき、必要なサブクラス オブジェクト、説明するクラス新機能は組み合わせにより多くの機能の組み合わせを実現できます。ブリッジ モードは、元の基本クラスの実装の詳細を抽象化し、それを実装構造に構築し、次に元の基本クラスを抽象階層構造に変換するため、システムを多次元で独立した変更で実現できます。
4番目、ブリッジモードの実装
抽象化基本クラス:
#ifndef BRIDGE_ABSTRACTION_H
#define BRIDGE_ABSTRACTION_H
#include "Implement.h"
//抽象基类
class Abstraction {
public:
//需要实现的接口
virtual void Request() = 0;
protected:
explicit Abstraction(Implement* imp):implement_(imp) {
}
protected:
Implement *implement_;
};
#endif //BRIDGE_ABSTRACTION_H
RefineddbstractionA 具象クラス:
#ifndef BRIDGE_REFINEDBSTRACTIONA_H
#define BRIDGE_REFINEDBSTRACTIONA_H
#include <iostream>
#include "Abstraction.h"
#include "Implement.h"
using namespace std;
class RefinedbstractionA : public Abstraction{
public:
explicit RefinedbstractionA(Implement* imp) : Abstraction(imp){
}
~RefinedbstractionA() = default;
void Request() override {
cout << "RefinedbstractionA::Request" << endl;
//调用实现部分
implement_->OperationImpl();
}
};
#endif //BRIDGE_REFINEDBSTRACTIONA_H
RefineddbstractionB 具象クラス:
#ifndef BRIDGE_REFINEDBSTRACTIONB_H
#define BRIDGE_REFINEDBSTRACTIONB_H
#include <iostream>
#include "Abstraction.h"
#include "Implement.h"
using namespace std;
class RefinedbstractionB : public Abstraction{
public:
explicit RefinedbstractionB(Implement* imp) : Abstraction(imp){
}
~RefinedbstractionB() = default;
void Request() override {
cout << "RefinedbstractionB::Request" << endl;
//调用实现部分
implement_->OperationImpl();
}
};
#endif //BRIDGE_REFINEDBSTRACTIONB_H
抽象実装クラスを実装します。
#ifndef BRIDGE_IMPLEMENT_H
#define BRIDGE_IMPLEMENT_H
//抽象实现类
class Implement {
protected:
Implement() = default;
~Implement() = default;
public:
virtual void OperationImpl() = 0;
};
#endif //BRIDGE_IMPLEMENT_H
ConcreteImplement特定の実装クラス:
#ifndef BRIDGE_CONCRETEIMPLEMENTA_H
#define BRIDGE_CONCRETEIMPLEMENTA_H
#include <iostream>
#include "Implement.h"
using namespace std;
//具体实现类
class ConcreteImplementA : public Implement{
public:
ConcreteImplementA() = default;
~ConcreteImplementA() = default;
//具体实现的功能函数
void OperationImpl() override{
cout << "ConcreteImplementA::OperationImpl" << endl;
}
};
#endif //BRIDGE_CONCRETEIMPLEMENTA_H
ConcreteImplementB 固有の実装クラス:
#ifndef BRIDGE_CONCRETEIMPLEMENTB_H
#define BRIDGE_CONCRETEIMPLEMENTB_H
#include <iostream>
#include "Implement.h"
using namespace std;
//具体实现类
class ConcreteImplementB : public Implement{
public:
ConcreteImplementB() = default;
~ConcreteImplementB() = default;
//具体实现的功能函数
void OperationImpl() override{
cout << "ConcreteImplementB::OperationImpl" << endl;
}
};
#endif //BRIDGE_CONCRETEIMPLEMENTB_H
BridgeMain は次のクラスを使用します。
#include <iostream>
#include "Abstraction.h"
#include "Implement.h"
#include "ConcreteImplementA.h"
#include "ConcreteImplementB.h"
#include "RefinedbstractionA.h"
#include "RefinedbstractionB.h"
int main() {
Implement* imp = new ConcreteImplementA();
Abstraction* abs = new RefinedbstractionA(imp);
abs->Request();
Implement* imp1 = new ConcreteImplementB();
Abstraction* abs1 = new RefinedbstractionB(imp1);
abs1->Request();
return 0;
}
抽象部分と実装部分を分離する: 実装システムには複数の角度 (次元) 分類があり、それぞれの分類が変化する可能性があります。複数の角度を分離して、独立して変化し、それらの間の結合を軽減します。
オブジェクトを多角的に分類して実装する必要があり、継承だけではクラスが大量に増えてオープンクローズ原則を満たせない場合には、ブリッジモードの使用を検討してください。
合成/集約の再利用原則: クラス継承の代わりに合成/集約を使用するようにしてください。
オブジェクトの合成/集約を優先すると、各クラスがカプセル化され、単一のタスクに集中し続けることができます。クラスとクラス継承階層は小さく保たれ、管理不能な巨大なものに成長する可能性が低くなります。
5. ブリッジモードの応用例
コンピュータのブランドとオペレーティング システムは 2 つの概念であり、異なるブランドのコンピュータに同じオペレーティング システムをインストールすることも、異なるオペレーティング システムをインストールすることもできますが、どちらのオペレーティング システムにも大きなばらつきがあります。コンピュータのブランドやオペレーティングシステムを基底クラスとして継承・拡張するとクラス数が大幅に増加し結合度が高くなりますが、コンピュータのブランドを変更したり、オペレーティングシステムの種類を追加すると多くの変更が発生します。追加される。
Computer と OS の 2 つの基本クラスを抽象化します。Computer クラスに OS オブジェクトの基本クラスを集約すると、コンピュータ ブランドとオペレーティング システムの拡張が混乱するという問題が解決されます。2 つの拡張は比較的柔軟であり、必要な時間が短縮されます。受取人に連絡します。
コンピュータインターフェース:
#ifndef COMPUTER_H
#define COMPUTER_H
class OS;
class Computer
{
public:
virtual void installOS(OS* os) = 0;
};
#endif // COMPUTER_H
AppleComputer の具体的な実装:
#ifndef APPLECOMPUTER_H
#define APPLECOMPUTER_H
#include "Computer.h"
#include "OS.h"
class AppleComputer : public Computer
{
public:
virtual void installOS(OS* os)
{
cout << "AppleComputer ";
os->installOS_Imp();
}
};
#endif // APPLECOMPUTER_H
ThinkPadComputer 固有の実装:
#ifndef THINKPADCOMPUTER_H
#define THINKPADCOMPUTER_H
#include "Computer.h"
#include "OS.h"
class ThinkPadComputer : public Computer
{
public:
virtual void installOS(OS* os)
{
cout << "ThinkPadComputer ";
os->installOS_Imp();
}
};
#endif // THINKPADCOMPUTER_H
OSインターフェース:
#ifndef OS_H
#define OS_H
#include <iostream>
using namespace std;
class OS
{
public:
virtual void installOS_Imp() = 0;
};
#endif // OS_H
LinuxOS の具体的な実装:
#ifndef LINUXOS_H
#define LINUXOS_H
#include "OS.h"
class LinuxOS : public OS
{
public:
virtual void installOS_Imp()
{
cout << "has installed Linux OS" << endl;
}
};
#endif // LINUXOS_H
Windows OS の具体的な実装:
#ifndef WINDOWSOS_H
#define WINDOWSOS_H
#include "OS.h"
class WindowsOS : public OS
{
public:
virtual void installOS_Imp()
{
cout << "has installed Windows OS" << endl;
}
};
#endif // WINDOWSOS_H