Single responsibility mode:
in the design of software components, if the division of responsibilities is not clear, the result of using inheritance is often that the subclass expands sharply with the change of requirements and is full of repeated code. At this time, the key is to clarify the responsibilities.
Typical mode
• Decorator
• Bridge
1. 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?
Code example
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; // Play sound virtual void DrawShape()=0; // Draw image virtual void WriteText()=0; // Write text virtual void Connect()=0; // Connect network virtual ~Messager(){} }; //Platform implementation class PCMessagerBase : public Messager{ //PC platform public: virtual void PlaySound(){ //********** } virtual void DrawShape(){ //********** } virtual void WriteText(){ //********** } virtual void Connect(){ //********** } }; class MobileMessagerBase : public Messager{ //Mobile platform public: virtual void PlaySound(){ //========== } virtual void DrawShape(){ //========== } virtual void WriteText(){ //========== } virtual void Connect(){ //========== } }; //Business abstraction class PCMessagerLite : public PCMessagerBase { // PC platform Lite 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 { // PC platform perfect public: virtual void Login(string username, string password){ PCMessagerBase::PlaySound(); // Play the sound first //******** 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(); }
Messager function module includes PC platform and Mobile platform, and there are simplified version and perfect version on each platform. In PC platform and Mobile platform, the implementation of PlaySound(), DrawShape(), WriteText(), Connect() and other functions is different.
If there are m platforms and each platform has n versions, the number of classes will be 1 + n + m*n, which will be very large. And there is duplicate code in the class.
Convert inheritance relationship to composition
class PCMessagerLite { // PC platform Lite PCMessagerBase* message; public: virtual void Login(string username, string password){ message->Connect(); //........ } // Other functions, omitted } class MobileMessagerLite{ MobileMessagerBase* message; // Other functions, omitted }
Further transformation
class PCMessagerLite { Messager* message; // new PCMessagerBase() //.... } class MobileMessagerLite{ Messager* message; // new MobileMessagerBase() //.... }
These two classes can be combined into one
class MessagerLite{ Messager* message; //.... }
However, there is a problem. PCMessagerBase is an abstract class because it only override s some pure virtual functions of the base class Messager. In the Messager, Login, SendMessage, SendPicture and PlaySound, DrawShape, WriteText and Connect should be separated. These are different change directions. One is platform related and the other is business related.
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(){} }; class MessagerImp{ //Platform implementation related 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 { // MessagerImp* messagerImp; There should have been this sentence, but it was mentioned in the parent class 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 { // MessagerImp* messagerImp; Lift up 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); }
Pattern definition
Separate the abstract part (business function) from the implementation part (platform implementation), so that they can change independently—— Design pattern GoF
Abstraction -- Abstract role
His main responsibility is to define the behavior of the role and save a reference to the implemented role, which is generally an abstract class.
Implementer -- implementing roles
It is an interface or abstract class that defines the behaviors and properties that a role must have.
Refined abstraction -- modify Abstract roles
He refers to the implementation role to modify the abstract role.
Concrete implementer -- concrete role
It implements methods and properties defined by interfaces or abstract classes.
Summary of key points
Bridge mode uses * * "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 (i.e. one class has only one reason for change), and the reusability is relatively 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