Chapter 0 of Design Mode for Station B - Introduction of Design Mode

Keywords: Design Pattern

The root cause of complex software design : Change
Objectives of software design: Reuse!
Solutions to complexity:
1, decompose (Not easy to reuse)
Divide and conquer, break big problems into many small problems, and complex problems into many simple problems.

2. Abstract (easy to reuse)

Because you cannot master all the complex objects, choose to ignore their non-essential details and work with generalized and idealized object models.

Change is the enemy of reuse!
The greatest advantage of object-oriented design is that: Resist change!
Decomposed cases:
(1)Shape1.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{

};

 (2)MainForm1.cpp

class MainForm : public Form {       //Most used by public
private:
	Point p1;
	Point p2;
	vector<Line> lineVector;
	vector<Rect> rectVector;
	//change
	vector<Circle> circleVector;             //decompose

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);
	}
	//change
	else if (...){
		//...
		circleVector.push_back(circle);
	}

	//...
	this->Refresh();
	Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e){
	//For straight lines
	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);
	}

	//For rectangles
	for (int i = 0; i < rectVector.size(); i++){
		e.Graphics.DrawRectangle(Pens.Red,
			rectVector[i].leftUp,
			rectVector[i].width,
			rectVector[i].height);
	}

	//change
	//For circles
	for (int i = 0; i < circleVector.size(); i++){
		e.Graphics.DrawCircle(Pens.Red,
			circleVector[i]);
	}

	//...
	Form::OnPaint(e);
}

Abstract cases:
(1)Shape2.h
class Shape{
public:
	virtual void Draw(const Graphics& g)=0;
	virtual ~Shape() { }     //General base classes have virtual destructors to prevent delete errors
};


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;
	}

	//Implement 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;
	}

	//Implement your own Draw and be responsible for drawing yourself
	virtual void Draw(const Graphics& g){
		g.DrawRectangle(Pens.Red,
			leftUp,width,height);
	}
};

//increase
class Circle : public Shape{
public:
	//Implement your own Draw and be responsible for drawing yourself
	virtual void Draw(const Graphics& g){
		g.DrawCircle(Pens.Red,
			         ...);
	}
};

(2) MainForm2.cpp

class MainForm : public Form {
private:
	Point p1;
	Point p2;
	//Abstract for all shapes
	vector<Shape*> shapeVector;    //Shape*implements its polymorphism

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));
	}
	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));
	}
	//change
	else if (...){
		//...
		shapeVector.push_back(circle);
	}
	//...
	this->Refresh();
	Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e){

	//For all shapes
	for (int i = 0; i < shapeVector.size(); i++){
		shapeVector[i]->Draw(e.Graphics); //Multiple calls, each responsible
	}
	//...
	Form::OnPaint(e);
}


Because of changes in demand, there is much less to change for an abstract solution than for a decomposition solution.

Eight object-oriented design principles: (more important than understanding the design pattern structure itself)

1. Dependency Inversion Principle (DIP)

• High-level modules (stable) should not depend on low-level modules (change), and both should depend on Abstract (stable) .
• Abstraction (stability) should not depend on implementation details (variation), implementation details should depend on Abstract (stable) .

2. Open and Closed Principle (OCP)

• Open to extensions and closed to changes.
• Class modules should be extensible but not modifiable.

3. Single Responsibility Principle (SRP)
• A class should have only one cause for its change.
• The direction of change implies class responsibility.
4. Liskov Replacement Principle (LSP)

 

• Subclasses must be able to replace their base class (IS-A).
• Inheritance expression type is abstract.
5. Interface Isolation Principle (ISP)
• Clients should not be forced to rely on methods they do not use.
• Interfaces should be small and complete.
6. Prefer object combinations over class inheritance
• Class inheritance is usually "white box reuse" and object combination is usually "black box reuse".
• Inheritance breaks encapsulation to some extent, and the parent of the child class is highly coupled.
• Object composition requires only well-defined interfaces and low coupling for the combined objects.
7. Packaging Change Point
• Using encapsulation to create a boundary layer between objects allows designers to modify one side of the boundary layer without adversely affecting the other side, thereby implementing the layer Second loosely coupled.
8. Programming for interfaces, not implementations

 

• Variable types are not declared as a specific specific class, but as an interface.
• Clients don't need to know the specific type of object; they just need to know the interface the object has.
• Reduce dependencies on parts of the system Thus, the type design scheme of "high cohesion, loose coupling" is implemented.
"Good object-oriented design" refers to those that satisfy " Respond to changes and improve reuse "Design.

 

The main point of a design pattern is to "find the point of change and then apply the design pattern at the point of change to better cope with it" Change in Requirements"."When and where to apply design patterns"is more important than"understanding the design pattern structure itself".
Classifying modes from the perspective of encapsulation change
Component collaboration:
• Template Method
• Observer / Event
• Strategy
Single responsibilities:
• Decorator
• Bridge
Object creation:
• Factory Method
• Abstract Factory
• Prototype
• Builder
Object performance:
• Singleton
• Flyweight
Interface isolation:
• Façade
• Proxy
• Mediator
• Adapter
State change:
• Memento
• State
Data structure:
• Composite
• Iterator
• Chain of
Resposibility
Behavioral changes:
• Command
• Visitor
Domain issues:
• Interpreter

Posted by bonaparte on Sat, 02 Oct 2021 10:18:24 -0700