c + + design pattern

Keywords: C++

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

Posted by badproduce on Wed, 06 Oct 2021 20:39:13 -0700