Bridge mode
motivation
- Due to the inherent implementation logic of some types, they have two changing dimensions, and even multiple latitude changes.
- How to deal with this "multi-dimensional change"? How to use object-oriented technology to make types change easily in two or more directions without introducing additional complexity?
Pattern definition
Separate the abstract part (business function) from the implementation part (platform implementation), so that they can change independently—— Design pattern GoF
SHOW ME THE CODE
Initial code
bridge1.cpp
class Messager{ public: virtual void Login(string username, string password)=0; virtual void SendMessage(string message)=0; virtual void SendPicture(Image image)=0; virtual void PlaySound()=0; virtual void DrawShape()=0; virtual void WriteText()=0; virtual void Connect()=0; virtual ~Messager(){} }; //Platform implementation class PCMessagerBase : public Messager{ public: virtual void PlaySound(){ //********** } virtual void DrawShape(){ //********** } virtual void WriteText(){ //********** } virtual void Connect(){ //********** } }; class MobileMessagerBase : public Messager{ public: virtual void PlaySound(){ //========== } virtual void DrawShape(){ //========== } virtual void WriteText(){ //========== } virtual void Connect(){ //========== } }; //Business abstraction class PCMessagerLite : public PCMessagerBase { public: virtual void Login(string username, string password){ PCMessagerBase::Connect(); //........ } virtual void SendMessage(string message){ PCMessagerBase::WriteText(); //........ } virtual void SendPicture(Image image){ PCMessagerBase::DrawShape(); //........ } }; class PCMessagerPerfect : public PCMessagerBase { public: virtual void Login(string username, string password){ PCMessagerBase::PlaySound(); //******** PCMessagerBase::Connect(); //........ } virtual void SendMessage(string message){ PCMessagerBase::PlaySound(); //******** PCMessagerBase::WriteText(); //........ } virtual void SendPicture(Image image){ PCMessagerBase::PlaySound(); //******** PCMessagerBase::DrawShape(); //........ } }; class MobileMessagerLite : public MobileMessagerBase { public: virtual void Login(string username, string password){ MobileMessagerBase::Connect(); //........ } virtual void SendMessage(string message){ MobileMessagerBase::WriteText(); //........ } virtual void SendPicture(Image image){ MobileMessagerBase::DrawShape(); //........ } }; class MobileMessagerPerfect : public MobileMessagerBase { public: virtual void Login(string username, string password){ MobileMessagerBase::PlaySound(); //******** MobileMessagerBase::Connect(); //........ } virtual void SendMessage(string message){ MobileMessagerBase::PlaySound(); //******** MobileMessagerBase::WriteText(); //........ } virtual void SendPicture(Image image){ MobileMessagerBase::PlaySound(); //******** MobileMessagerBase::DrawShape(); //........ } }; void Process(){ //Compile fashion accessories Messager *m = new MobileMessagerPerfect(); }
Deja vu feeling, let's look at the expansion scale. If so n n n platforms, each with m m m versions. Then the number of classes 1 + n + m ∗ n 1+n+m*n 1+n+m∗n
The code also has redundancy, such as
virtual void Login(string username, string password){ PCMessagerBase::PlaySound(); //******** PCMessagerBase::Connect(); //........ } virtual void Login(string username, string password){ MobileMessagerBase::PlaySound(); //******** MobileMessagerBase::Connect(); //........ }
Optimization 1: inheritance to combination
Similar to the Decorator design pattern, we can think of using pointers to optimize inheritance to composition
Make the following modifications to PCMessagerLite (see the steps in the Decorator design pattern for each step)
//class PCMessagerLite : public PCMessagerBase { class PCMessagerLite{ Message* messager;//=new PCMessagerBase(); public: virtual void Login(string username, string password){ //PCMessagerBase::Connect(); messager->Connect(); //........ } virtual void SendMessage(string message){ //PCMessagerBase::WriteText(); messager->WriteText(); //........ } virtual void SendPicture(Image image){ //PCMessagerBase::DrawShape(); messager->DrawShape(); //........ } };
Now it is found that PCMessagerLite and MoblieMessagerLite can be merged
Optimization 2: split abstract base classes
Another problem is that PCMessagerBase is an abstract class (pure virtual base class), but we don't cover all virtual functions
Message* messager;//=new PCMessagerBase(); This is actually not true
It can be found that several pure virtual functions in the Messager should not be put together, but should be separated
class Messager{ public: virtual void Login(string username, string password)=0; virtual void SendMessage(string message)=0; virtual void SendPicture(Image image)=0; virtual void PlaySound()=0; virtual void DrawShape()=0; virtual void WriteText()=0; virtual void Connect()=0; virtual ~Messager(){} };
Add a MessagerImp
class Messager{ protected: MessagerImp* messagerImp;//... public: virtual void Login(string username, string password)=0; virtual void SendMessage(string message)=0; virtual void SendPicture(Image image)=0; virtual ~Messager(){} }; // Different change directions (business and platform), so it is divided into two categories class MessagerImp{ public: virtual void PlaySound()=0; virtual void DrawShape()=0; virtual void WriteText()=0; virtual void Connect()=0; virtual MessagerImp(){} };
Like the Decorator design pattern, promote a field and then add a constructor
bridge2.cpp
class Messager{ protected: MessagerImp* messagerImp;//... The field refers to the parent class public: virtual void Login(string username, string password)=0; virtual void SendMessage(string message)=0; virtual void SendPicture(Image image)=0; virtual ~Messager(){} }; // Different change directions (business and platform), so it is divided into two categories class MessagerImp{ public: virtual void PlaySound()=0; virtual void DrawShape()=0; virtual void WriteText()=0; virtual void Connect()=0; virtual MessagerImp(){} }; //Platform implementation n class PCMessagerImp : public MessagerImp{ public: virtual void PlaySound(){ //********** } virtual void DrawShape(){ //********** } virtual void WriteText(){ //********** } virtual void Connect(){ //********** } }; class MobileMessagerImp : public MessagerImp{ public: virtual void PlaySound(){ //========== } virtual void DrawShape(){ //========== } virtual void WriteText(){ //========== } virtual void Connect(){ //========== } }; //Business abstraction m //Number of classes: 1+n+m class MessagerLite :public Messager { public: virtual void Login(string username, string password){ messagerImp->Connect(); //........ } virtual void SendMessage(string message){ messagerImp->WriteText(); //........ } virtual void SendPicture(Image image){ messagerImp->DrawShape(); //........ } }; class MessagerPerfect :public Messager { public: virtual void Login(string username, string password){ messagerImp->PlaySound(); //******** messagerImp->Connect(); //........ } virtual void SendMessage(string message){ messagerImp->PlaySound(); //******** messagerImp->WriteText(); //........ } virtual void SendPicture(Image image){ messagerImp->PlaySound(); //******** messagerImp->DrawShape(); //........ } }; void Process(){ //Running fashion MessagerImp* mImp=new PCMessagerImp(); Messager *m =new Messager(mImp); }
Become the present scale 1 + n + m 1+n+m 1+n+m, the problem has been effectively solved
The change direction of platform implementation and business abstraction is different. They should be separated so that they can change independently
Structural design
Red is stable and blue changes
Summary of key points
- The Bridge pattern uses the "composite relationship between objects" to decouple the inherent binding relationship between abstraction and implementation, so that abstraction and implementation can change along their respective dimensions. The so-called abstraction and implementation change along their respective latitudes, that is, "subclassing" them.
- Bridge mode is sometimes similar to multi inheritance schemes, but multi inheritance schemes often violate the principle of single responsibility (that is, a class has only one reason for change), and the reusability is poor. The bridge pattern is a better solution than the multi inheritance scheme.
- Bridge mode is generally applied in "two very strong change dimensions". Sometimes a class also has more than two change dimensions. At this time, bridge extension mode can be used.