brief introduction
Take the following code as an example
1, Decompose
Shape.h
class Point{ public: int x; int y; }; class Line{ public: Point start; Point end; Line(const Point& start, const Point& end){ this->start = start; this->end = end; } }; class Rect{ public: Point leftUp; int width; int height; Rect(const Point& leftUp, int width, int height){ this->leftUp = leftUp; this->width = width; this->height = height; } }; //increase class Circle{ public: point center;//center of a circle int r;//radius Circle(const Point& c, int r){ this->center = c; this->r = r; } };
MainForm.cpp
//The pseudo code written here inherits the Form regardless of him class MainForm : public Form { private: Point p1; Point p2; //change vector<Line> lineVector; vector<Rect> rectVector; //Add circle vector<Circle> circleVector; public: MainForm(){ //... } protected: virtual void OnMouseDown(const MouseEventArgs& e); virtual void OnMouseUp(const MouseEventArgs& e); virtual void OnPaint(const PaintEventArgs& e); }; void MainForm::OnMouseDown(const MouseEventArgs& e){ p1.x = e.X; p1.y = e.Y; //... Form::OnMouseDown(e); } void MainForm::OnMouseUp(const MouseEventArgs& e){ p2.x = e.X; p2.y = e.Y; if (rdoLine.Checked){ Line line(p1, p2); lineVector.push_back(line); } else if (rdoRect.Checked){ int width = abs(p2.x - p1.x); int height = abs(p2.y - p1.y); Rect rect(p1, width, height); rectVector.push_back(rect); } //increase else if (rdoCircle.Checked){ Point center=p1; int r=sqrt((p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y)); Circle circle(center,r); circleVector.push_back(circle); } //... this->Refresh(); Form::OnMouseUp(e); } void MainForm::OnPaint(const PaintEventArgs& e){ //Draw a line for (int i = 0; i < lineVector.size(); i++){ e.Graphics.DrawLine(Pens.Red, lineVector[i].start.x, lineVector[i].start.y, lineVector[i].end.x, lineVector[i].end.y); } //Draw matrix for (int i = 0; i < rectVector.size(); i++){ e.Graphics.DrawRectangle(Pens.Red, rectVector[i].leftUp, rectVector[i].width, rectVector[i].height); } //Hate to add //Draw a circle for (int i = 0; i < circleVector.size(); i++){ e.Graphics.DrawCircle(Pens.Red, circleVector[i].center, circleVector[i].r ); } //... Form::OnPaint(e); }
2, Abstract
Shape.h
Using polymorphic mechanism
class Shape{ public: virtual void Draw(const Graphics& g)=0; virtual ~Shape() { } }; class Point{ public: int x; int y; }; class Line: public Shape{ public: Point start; Point end; Line(const Point& start, const Point& end){ this->start = start; this->end = end; } //Realize your own Draw and be responsible for drawing yourself virtual void Draw(const Graphics& g){ g.DrawLine(Pens.Red, start.x, start.y,end.x, end.y); } }; class Rect: public Shape{ public: Point leftUp; int width; int height; Rect(const Point& leftUp, int width, int height){ this->leftUp = leftUp; this->width = width; this->height = height; } //Realize your own Draw and be responsible for drawing yourself virtual void Draw(const Graphics& g){ g.DrawRectangle(Pens.Red, leftUp,width,height); } }; //Add data structure of circle class Circle : public Shape{ public: point center;//center of a circle int r;//radius Circle(const Point& c, int r){ this->center = c; this->r = r; } //Realize your own Draw and be responsible for drawing yourself virtual void Draw(const Graphics& g){ g.DrawCircle(Pens.Red, center,r); } };
MainForm.cpp
class MainForm : public Form { private: Point p1; Point p2; //Unified processing for all shapes vector<Shape*> shapeVector;// The shape * pointer is a dynamic mechanism public: MainForm(){ //... } protected: virtual void OnMouseDown(const MouseEventArgs& e); virtual void OnMouseUp(const MouseEventArgs& e); virtual void OnPaint(const PaintEventArgs& e); }; void MainForm::OnMouseDown(const MouseEventArgs& e){ p1.x = e.X; p1.y = e.Y; //... Form::OnMouseDown(e); } void MainForm::OnMouseUp(const MouseEventArgs& e){ p2.x = e.X; p2.y = e.Y; if (rdoLine.Checked){ shapeVector.push_back(new Line(p1,p2));// new a heap pointer } else if (rdoRect.Checked){ int width = abs(p2.x - p1.x); int height = abs(p2.y - p1.y); shapeVector.push_back(new Rect(p1, width, height)); } //increase else if (...){ Point center=p1; int r=sqrt((p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y)); Circle circle(center,r); shapeVector.push_back(circle); } //... this->Refresh(); Form::OnMouseUp(e); } void MainForm::OnPaint(const PaintEventArgs& e){ for (int i = 0; i < shapeVector.size(); i++){ shapeVector[i]->Draw(e.Graphics); //Polymorphic calls, each taking its own responsibility. Line calls line's Draw and Rect calls Rect's Draw } //... Form::OnPaint(e); }
Object oriented design principles
This is actually an extension
A class cannot point randomly
The subclass inherits the parent class. There must be a reason
Don't publish unnecessary methods. If only subclasses are used, it is protected. If this class is used, it is private
Object combination, for example, a class b is placed in class a
If the coupling between the inheritance subclass and the parent class is too high, the parent class exposes too many things to the child class
Package change point side stable
Here is the specific type, which violates the object-oriented principle
This is to give an abstract interface, which conforms to the principle.
In fact, the design of software is also based on the principles of other industries!!!
Template Method
Early binding
The method here does not use template_method
template_lib.cpp
//Library developer class Library{ public: void Step1(){ //... } void Step3(){ //... } void Step5(){ //... } };
template_app.cpp
//Application Developer class Application{ public: bool Step2(){ //... } void Step4(){ //... } }; int main() { //Simulation program flow Library lib(); Application app(); lib.Step1(); if (app.Step2()){ lib.Step3(); } for (int i = 0; i < 4; i++){ app.Step4(); } lib.Step5(); }
A late thing calls an early thing is an early binding
Late binding
Adopt the design pattern of template method - the premise is that you have a stable skeleton
The main flow of the program is relatively stable
template_lib.cpp
//Library developer class Library{ public: //Stable template method void Run(){ Step1(); if (Step2()) { //Support polymorphic calling of variant = = > virtual functions Step3(); } for (int i = 0; i < 4; i++){ Step4(); //Support polymorphic calling of variant = = > virtual functions } Step5(); } virtual ~Library(){ }//The destructor of the base class is written as virtual, because if you create a new subclass and you want to delete the subclass, if the fiction of the base class is not written as virtual, the subclass may not be able to call its own destructor protected: void Step1() { //stable //..... } void Step3() {//stable //..... } void Step5() { //stable //..... } //Virtual functions support changes virtual bool Step2() = 0;//change virtual void Step4() =0; //change };
template_app.cpp
//Application Developer //Inherit the library for development class Application : public Library { protected: virtual bool Step2(){ //... subclass override implementation } virtual void Step4() { //... subclass override implementation } }; int main() { //Polymorphic pointer Library* pLib=new Application(); lib->Run();//We will still follow the process here delete pLib;// } }
The early thing calls the late thing is the late binding
Red stands for stability
Blue represents instability
Strategy mode
Violation of opening and closing principle
enum TaxBase { CN_Tax, US_Tax, DE_Tax, FR_Tax //The change violates the opening and closing principle }; class SalesOrder{ TaxBase tax; public: double CalculateTax(){ //... if (tax == CN_Tax){ //CN*********** } else if (tax == US_Tax){ //US*********** } else if (tax == DE_Tax){ //DE*********** } else if (tax == FR_Tax){ //change //... } //.... } };
There are many things to change here, and if else also consumes performance
Comply with the opening and closing principle
Strategy mode
class TaxStrategy{ public: virtual double Calculate(const Context& context)=0; virtual ~TaxStrategy(){} }; class CNTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //*********** } }; class USTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //*********** } }; class DETax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //*********** } }; //The expansion meets the opening and closing principle //********************************* class FRTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //......... } }; class SalesOrder{ private: TaxStrategy* strategy;//Polymorphic pointer public: SalesOrder(StrategyFactory* strategyFactory){ this->strategy = strategyFactory->NewStrategy();//Is a heap object returned by the factory China or the United States } ~SalesOrder(){ delete this->strategy; } public double CalculateTax(){ //... Context context(); double val = strategy->Calculate(context); //The polymorphic call depends on which subclass object is returned by strategyfactory - > newstrategy() //... } };
The red remains stable and the blue changes
Observer mode
1, Observer mode is not used
Look at the code
FileSplitter.cpp
class FileSplitter { string m_filePath; int m_fileNumber; ProgressBar* m_progressBar;// Notification control here, what if we don't want to notify the progress in the form of progress bar, but want to present the situation in percentage? Isn't it hard to change public: FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) : m_filePath(filePath), m_fileNumber(fileNumber), m_progressBar(progressBar){ } void split(){ //1. Read large files //2. Write to small files in batches for (int i = 0; i < m_fileNumber; i++){ //... //separate if (m_progressBar != nullptr) { float progressValue = m_fileNumber; progressValue = (i + 1) / progressValue; m_progressBar->setValue(progressValue);//Update progress bar } } } };
MainForm.cpp
class MainForm : public Form { TextBox* txtFilePath; TextBox* txtFileNumber; ProgressBar* progressBar; public: void Button1_Click(){ string filePath = txtFilePath->getText(); int number = atoi(txtFileNumber->getText().c_str()); FileSplitter splitter(filePath, number, progressBar); splitter.split(); } };
2, The observer design pattern is used
FileSplitter.cpp
class IProgress{ public: virtual void DoProgress(float value)=0; virtual ~IProgress(){} }; class FileSplitter { string m_filePath; int m_fileNumber; //ProgressBar* m_progressBar;// Notification control tight coupling List<IProgress*> m_iprogressList; // Abstract notification mechanism, supporting loose coupling of multiple observers (with addIProgress and removeIProgress) public: FileSplitter(const string& filePath, int fileNumber) : m_filePath(filePath), m_fileNumber(fileNumber){ } void split(){ //1. Read large files //2. Write to small files in batches for (int i = 0; i < m_fileNumber; i++){ //... float progressValue = m_fileNumber; progressValue = (i + 1) / progressValue; onProgress(progressValue);//Send notification } } //Join IProgress void addIProgress(IProgress* iprogress){ m_iprogressList.push_back(iprogress); } //Delete IProgress void removeIProgress(IProgress* iprogress){ m_iprogressList.remove(iprogress); } protected: virtual void onProgress(float value){ List<IProgress*>::iterator itor=m_iprogressList.begin(); while (itor != m_iprogressList.end() ) (*itor)->DoProgress(value); //The update progress bar is selected according to the specific category of tor itor++; } } };
MainForm.cpp
//Adopt one to many observer mode //If we want to make the notification space in the form of percentage, we can define another class to accept the percentage information, then define an object of this class in the mainform class, and then add to m_iprogressList class MainForm : public Form, public IProgress { TextBox* txtFilePath; TextBox* txtFileNumber; ProgressBar* progressBar; public: void Button1_Click(){ string filePath = txtFilePath->getText(); int number = atoi(txtFileNumber->getText().c_str()); ConsoleNotifier cn; CirclePercent cp; FileSplitter splitter(filePath, number); splitter.addIProgress(this); //Subscription notification splitter.addIProgress(&cn); //Subscription notification splitter.addIProgress(&cp); //Subscription notification splitter.split(); splitter.removeIProgress(this); } virtual void DoProgress(float value){ progressBar->setValue(value); } }; class ConsoleNotifier : public IProgress { public: virtual void DoProgress(float value){ cout << "."; } }; //Here is the display percentage added by yourself. If the windows interface already has a component in the form of disk percentage class CirclePercent:public IProgress { CirclePanelPercent* circlePanelPercent; public: virtual void DoProgress(float value) { circlePanelPercent->setvalue(value); } };
The red part is the stable part and the dependent part. The blue is the part that changed
Decoration mode
For extension operations, it can be encrypted or buffered. If m extension operations are added, the total combination is (m+m-1 +... + 1) / 2, m > 2, and n represents the type of file stream.
decorator1.cpp
//Business operation class Stream{ public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream(){} }; //Principal class class FileStream: public Stream{ public: virtual char Read(int number){ //Read file stream } virtual void Seek(int position){ //Locate file stream } virtual void Write(char data){ //Write file stream } }; class NetworkStream :public Stream{ public: virtual char Read(int number){ //Read network flow } virtual void Seek(int position){ //Location network flow } virtual void Write(char data){ //Write network stream } }; class MemoryStream :public Stream{ public: virtual char Read(int number){ //Read memory stream } virtual void Seek(int position){ //Locate memory stream } virtual void Write(char data){ //Write memory stream } }; //Extended operation class CryptoFileStream :public FileStream{ public: virtual char Read(int number){ //Additional encryption operations FileStream::Read(number);//Read file stream } virtual void Seek(int position){ //Additional encryption operations FileStream::Seek(position);//Locate file stream //Additional encryption operations } virtual void Write(byte data){ //Additional encryption operations FileStream::Write(data);//Write file stream //Additional encryption operations } }; class CryptoNetworkStream : :public NetworkStream{ public: virtual char Read(int number){ //Additional encryption... The code is repeated here NetworkStream::Read(number);//Read network flow } virtual void Seek(int position){ //Additional encryption... The code is repeated here NetworkStream::Seek(position);//Location network flow //Additional encryption operations } virtual void Write(byte data){ //Additional encryption... The code is repeated here NetworkStream::Write(data);//Write network stream //Additional encryption operations } }; class CryptoMemoryStream : public MemoryStream{ public: virtual char Read(int number){ //Additional encryption... The code is repeated here MemoryStream::Read(number);//Read memory stream } virtual void Seek(int position){ //Additional encryption... The code is repeated here MemoryStream::Seek(position);//Locate memory stream //Additional encryption operations } virtual void Write(byte data){ //Additional encryption... The code is repeated here MemoryStream::Write(data);//Write memory stream //Additional encryption operations } }; class BufferedFileStream : public FileStream{ //... }; class BufferedNetworkStream : public NetworkStream{ //... }; class BufferedMemoryStream : public MemoryStream{ //... } class CryptoBufferedFileStream :public FileStream{ public: virtual char Read(int number){ //Additional encryption operations //Additional buffering operations FileStream::Read(number);//Read file stream } virtual void Seek(int position){ //Additional encryption operations //Additional buffering operations FileStream::Seek(position);//Locate file stream //Additional encryption operations //Additional buffering operations } virtual void Write(byte data){ //Additional encryption operations //Additional buffering operations FileStream::Write(data);//Write file stream //Additional encryption operations //Additional buffering operations } }; void Process(){ //Compile fashion accessories CryptoFileStream *fs1 = new CryptoFileStream(); BufferedFileStream *fs2 = new BufferedFileStream(); CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream(); }
You can see that there is a lot of redundant code above
decorator2.cpp
Eliminate redundancy by polymorphism (combined mode)
//Business operation //Eliminating redundancy using polymorphism class Stream{ public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream(){} }; //Principal class class FileStream: public Stream{ public: virtual char Read(int number){ //Read file stream } virtual void Seek(int position){ //Locate file stream } virtual void Write(char data){ //Write file stream } }; class NetworkStream :public Stream{ public: virtual char Read(int number){ //Read network flow } virtual void Seek(int position){ //Location network flow } virtual void Write(char data){ //Write network stream } }; class MemoryStream :public Stream{ public: virtual char Read(int number){ //Read memory stream } virtual void Seek(int position){ //Locate memory stream } virtual void Write(char data){ //Write memory stream } }; //The extension operation refers to the reading and positioning of various types of files class CryptoStream: public Stream {//The purpose of inheriting stream here is to improve the specification of the interface Stream* stream;//... public: CryptoStream(Stream* stm):stream(stm){ } virtual char Read(int number){ //Additional encryption operations stream->Read(number);//Read file stream } virtual void Seek(int position){ //Additional encryption operations stream::Seek(position);//Locate file stream //Additional encryption operations } virtual void Write(byte data){ //Additional encryption operations stream::Write(data);//Write file stream //Additional encryption operations } }; class BufferedStream : public Stream{ Stream* stream;//... public: BufferedStream(Stream* stm):stream(stm){ } //... }; void Process(){ //Running fashion FileStream* s1=new FileStream(); CryptoStream* s2=new CryptoStream(s1); BufferedStream* s3=new BufferedStream(s1);//Caching of file streams BufferedStream* s4=new BufferedStream(s2);//Encryption and caching of file streams }
decorator3.cpp
Further improvement
//Business operation class Stream{ public: virtual char Read(int number)=0; virtual void Seek(int position)=0; virtual void Write(char data)=0; virtual ~Stream(){} }; //Principal class class FileStream: public Stream{ public: virtual char Read(int number){ //Read file stream } virtual void Seek(int position){ //Locate file stream } virtual void Write(char data){ //Write file stream } }; class NetworkStream :public Stream{ public: virtual char Read(int number){ //Read network flow } virtual void Seek(int position){ //Location network flow } virtual void Write(char data){ //Write network stream } }; class MemoryStream :public Stream{ public: virtual char Read(int number){ //Read memory stream } virtual void Seek(int position){ //Locate memory stream } virtual void Write(char data){ //Write memory stream } }; //Extended operation DecoratorStream: public Stream{ protected: Stream* stream;//... DecoratorStream(Stream * stm):stream(stm){ } }; class CryptoStream: public DecoratorStream { public: CryptoStream(Stream* stm):DecoratorStream(stm){ } virtual char Read(int number){ //Additional encryption operations stream->Read(number);//Read file stream } virtual void Seek(int position){ //Additional encryption operations stream::Seek(position);//Locate file stream //Additional encryption operations } virtual void Write(byte data){ //Additional encryption operations stream::Write(data);//Write file stream //Additional encryption operations } }; class BufferedStream : public DecoratorStream{ Stream* stream;//... public: BufferedStream(Stream* stm):DecoratorStream(stm){ } //... }; void Process(){ //Running fashion FileStream* s1=new FileStream(); CryptoStream* s2=new CryptoStream(s1); BufferedStream* s3=new BufferedStream(s1); BufferedStream* s4=new BufferedStream(s2); }
The body and extension should be separated
Red represents the stable part and blue represents the changing part