[Design Mode] - Bridge Bridge Bridge Mode

Posted by Fixxer on Fri, 24 Dec 2021 20:18:12 +0100

Bridge mode

In the design of software components, if the responsibility division is not clear, the result of inheritance tends to be a rapid expansion of subclasses with duplicate code as demand changes.

motivation

Because of some fixed inherent implementation logic, they have two or more changing dimensions, and even more.

How do you cope with multidimensional changes? How do you use face-to-face object relationship technology to make it easy for types to change in two or more directions without introducing additional complexity?

Definition

Separating Abstract parts (business functions) from implementation parts (platform implementations) is that they can all change independently.

Sample Code

class Messager {
public:
 // Business Functions
 virtual void Login(string username, string password) = 0;
 virtual void SendMessage(string message) = 0;
 virtual void SendPicture(Image image) = 0;

 // Functional implementation
 virtual void PlaySound() = 0;
 virtual void DrawShape() = 0;
 virtual void WriteText() = 0;
 virtual void Connect() = 0;
 virtual ~Messager() {}
};

Suppose we now have a Messager base class that defines interfaces related to business functions as well as interfaces implemented by functions. Now we will base our implementation on different platforms, such as PC and mobile, which mainly implement the interface of function implementation:

// 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() { // === }
};

In the business abstraction, we need to provide different versions, the compact version or the high-profile version, taking the PC side as an example:

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();
   // ........
 }
};

The PCMessagerPerfect class is equivalent to the high-profile version of PCMessagerLite, which implements richer content in the interface. Subclasses of MobileMessagerLite and MobileMessagerPerfect, where expansion of subclasses can also occur. There are n platforms, and each platform has m versions, so there will be 1+n+m*n classes to write in the end. Then the angle used is:

void Process() {
    // Compile-time Matching
    Messager *m = new PCMessagerPerfect();
}

As described in the previous decoration pattern, one effective way to prevent subclasses from expanding is to inherit to combine. Here is an example of the Lite version for each platform:

class PCMessagerLite {
    PCMessagerBase* message; // Parent class representation can be used with MobileMessagerBase
public:
 virtual void Login(string username, string password){
   message->Connect();
   // ........
 }
  virtual void SendMessage(string message){
   message->WriteText();
   // ........
 }
   virtual void SendPicture(Image image){
   message->DrawShape();
   // ........
 }
};

class MobileMessagerLite {
    MobileMessagerBase* message;
public:
 virtual void Login(string username, string password){
   message->Connect();
   // ........
 }
  virtual void SendMessage(string message){
   message->WriteText();
   // ........
 }
   virtual void SendPicture(Image image){
   message->DrawShape();
   // ........
 }
};

You can see that after the change, the two forms are almost identical, that is, they are different from the message member variable and belong to the same base class, so the two can be merged into one:

class MessagerLite {
    Messager* message;
public:
 MessagerLite(Messager *m) : message(m) {}
 virtual void Login(string username, string password){
   message->Connect();
   // ........
 }
  virtual void SendMessage(string message){
   message->WriteText();
   // ........
 }
   virtual void SendPicture(Image image){
   message->DrawShape();
   // ........
 }
};

Similarly, for the virtual function interface specification, MessagerLite needs to inherit an interface base class. In Decorator Decoration mode, base class Messager can be inherited directly, but since the business only implements part of the virtual base class interface, and the subclass of function implementation also implements part of the virtual function interface, direct inheritance is not appropriate. What you need to do at this point is to split the original Essager base class into two classes with your business.

class Messager {
public:
 // Business Functions
 virtual void Login(string username, string password) = 0;
 virtual void SendMessage(string message) = 0;
 virtual void SendPicture(Image image) = 0;
 virtual ~Messager() {}
};

class MessagerImp {
public:
 virtual void PlaySound() = 0;
 virtual void DrawShape() = 0;
 virtual void WriteText() = 0;
 virtual void Connect() = 0;
 virtual ~MessagerImp() {}
};

The MessagerLite class can safely inherit the Messager base class. The last modified code is:

class Messager {
 MessagerImp* messageImp; // Subclass Move Up
public:
  Messager(MessagerImp* imp): messageImp(imp){}
 // Business Functions
 virtual void Login(string username, string password) = 0;
 virtual void SendMessage(string message) = 0;
 virtual void SendPicture(Image image) = 0;
 virtual ~Messager() {}
};

class MessagerImp {
public:
 virtual void PlaySound() = 0;
 virtual void DrawShape() = 0;
 virtual void WriteText() = 0;
 virtual void Connect() = 0;
 virtual ~MessagerImp() {}
};

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() { // === }
};

class MessagerLite : public Messager {
    // MessagerImp* messageImp; Share with MessagerPerfect, move up to parent
public:
 virtual void Login(string username, string password){
   messageImp->Connect();
   // ........
 }
  virtual void SendMessage(string message){
   messageImp->WriteText();
   // ........
 }
   virtual void SendPicture(Image image){
   messageImp->DrawShape();
   // ........
 }
};

class MessagerPerfect : public Messager {
    // MessagerImp* message; Share with MessagerLite, move up to parent
public:
 virtual void Login(string username, string password){
   messageImp->PlaySound();
   //*********
   messageImp->Connect();
   // ........
 }
  virtual void SendMessage(string message){
   messageImp->PlaySound();
   //*********
   messageImp->WriteText();
   // ........
 }
   virtual void SendPicture(Image image){
   messageImp->PlaySound();
   //*********
   messageImp->DrawShape();
   // ........
 }
};

From the point of view of use:

void Process() {
    // Runtime assembly
  	MessagerImp *imp = new PCMessagerImp();
    Messager *m = new MessagerPerfect(imp);
}

The above is an example process of bridge mode. With the modification of the bridge mode, the number of classes here has changed from 1+n+m*n to 1+n+m.

Structural Diagram

summary

  • Look ahead Decorator Decoration Mode Students must be familiar with this part of the content, the refactoring methods are similar, but here different directions are placed in the same base class, resulting in no direct inheritance, need to have a process of splitting the base class, then inherit separately, and assemble the same.

  • Bridge Bridge mode refers to the "combinatorial relationships between objects" (combinations) that decouple the inherent binding relationship between abstraction and implementation, and that abstraction and implementation can change along their respective dimensions, i.e., subclassify them separately.

  • Bridge Bridge Bridge mode is similar to multiple inheritance scheme, but multiple inheritance often violates the principle of single responsibility and is less reusable. A better solution than multiple inheritance is to combine them.

  • Bridge Bridge mode is used in two very strong changing dimensions, and sometimes a class has two extra transforming dimensions, so Bridge's extended mode can be used.

Summary of other design patterns:
[Design Mode] - Introduction and Classification of Design Mode

Topics: C++ Design Pattern bridge