Lecture 1 on design patterns -- Introduction to design patterns

one   Course objectives

(1) Understand loosely coupled design ideas

(2) Master object-oriented design principles

(3) Master reconstruction techniques to improve design

(4) Master GOF core design mode

 

two   Design patterns: the foundation of Reusable Object-Oriented Software

Reusability is the goal of design patterns, and object-oriented is the method. Generally speaking, the default design pattern is object-oriented design pattern, but this does not mean that design pattern is equal to object-oriented design pattern.

 

three   Bottom thinking: down, how to grasp the bottom of the machine and understand the object structure from the onlookers

Language construction, compilation and conversion, memory model, runtime mechanism.

Abstract thinking: upward, how to abstract the world around us into program code

Object oriented, component encapsulation, design pattern, architecture pattern.

 

Deep understanding of object orientation:

Downward: deeply understand the three object-oriented mechanisms

Encapsulation: hiding internal implementations

Inheritance: reusing existing code

Polymorphism: modifying object behavior

 

Upward: deeply grasp the abstract meaning brought by object-oriented mechanisms, understand how to use these mechanisms to express the real world, and master what is "good object-oriented design".

 

four   How to solve complexity?

(1) Decompose divide and conquer, decompose large problems into multiple small problems, and decompose complex problems into multiple simple problems.

(2) At a higher level of abstraction, there is a general technology for people to deal with complexity, that is, abstraction. Because we can't grasp all the complex objects, we choose to ignore its non essential details and deal with the generalized and idealized object model.

 

5. Suppose you can draw lines and rectangles on an interface

(1) The pseudo code is as follows:

shape.h file content

#pragma once

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->height = height;
		this->width = width;
	}
};

Each shape corresponds to its own class.

Contents in MainForm.cpp

#include "Shape.h"

#include<vector>

using namespace std;

class MainForm :public Form
{
private:
	Point p1;    //Represents the point at which the mouse is pressed and raised
	Point p2;

	vector<Line> lineVector;
	vector<Rect> rectVector;

public:
	MainForm()
	{
		//...
	}

protected:
	virtual void OnMouseDown(const MouseEventArgs& e);   //Mouse down
	virtual void OnMouseUp(const MouseEventArgs& e);     //Mouse up
	virtual void OnPaint(const PaintEventArgs& e);       //Interface refresh
};

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

	//...
	this->Refresh();   //OnPaint is called

	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 rectangle
	for (int i = 0; i < rectVector.size(); i++)
	{
		e.Graphics.DrawRectangle(Pens.red,
			rectVector[i].leftUp,
			rectVector[i].width,
			rectVector[i].height);
	}

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

Now there is a requirement that circles can be drawn on the interface.

In order to achieve this goal, the written code is as follows:

shape.h file

#pragma once

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->height = height;
		this->width = width;
	}
};

//New code
class Circle
{
public:
	Point Center;
	int Radius;

	Circle(const Point& center, int radius)
	{
		this->Center = center;
		this->Radius = radius;
	}
};

Contents of MainForm.cpp file

#include "Shape.h"

#include<vector>

using namespace std;

class MainForm :public Form
{
private:
	Point p1;    //Represents the point at which the mouse is pressed and raised
	Point p2;

	vector<Line> lineVector;
	vector<Rect> rectVector;
	vector<Circle> circleVector;

public:
	MainForm()
	{
		//...
	}

protected:
	virtual void OnMouseDown(const MouseEventArgs& e);   //Mouse down
	virtual void OnMouseUp(const MouseEventArgs& e);     //Mouse up
	virtual void OnPaint(const PaintEventArgs& e);       //Interface refresh
};

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);
	}
	else if (rdoCircle.Checked)
	{
		Point center;
		center.x = (p1.x + p2.x) / 2;
		center.y = (p1.y + p2.y) / 2;

		int radius = sqrt(pow(abs(p1.x - p2.x), 2) + pow(abs(p1.y - p2.y), 2)) / 2;

		Circle circle(center,radius);
		circleVector.push_back(circle);
	}

	//...
	this->Refresh();   //OnPaint is called

	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 rectangle
	for (int i = 0; i < rectVector.size(); i++)
	{
		e.Graphics.DrawRectangle(Pens.red,
			rectVector[i].leftUp,
			rectVector[i].width,
			rectVector[i].height);
	}

	//For circular
	for (int i = 0; i < circleVector.size(); i++)
	{
		e.Graphics.DrawCircle(Pens.red,
			rectVector[i].Center,
			rectVector[i].Radius);
	}

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

(2) Abstract ideas to achieve the function of drawing lines and rectangles on the interface

Shape1.h file content

#pragma once

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 Graphic& 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);
	}
};

Create a base class named Shape. The line class and rectangle class inherit the Shape base class and implement the Draw method in their respective classes.

MainForm1.cpp

#include "Shape1.h"

#include<vector>

using namespace std;

class MainForm : public Form
{
private:
	Point p1;
	Point p2;

	//For all shapes
	vector<Shape*> shapeVector;   //When destructing, you need to free the memory in this place

public:
	MainForm()
	{
		//...
	}

protected:
	virtual void OnMouseDown(const MouseEventArgs& e);   //Mouse down
	virtual void OnMouseUp(const MouseEventArgs& e);     //Mouse up
	virtual void OnPaint(const PaintEventArgs& e);       //Interface refresh
};

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

	//...
	this->Refresh();   //OnPaint is called

	Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e)
{
	//For all shapes
	for (int i = 0; i < shapeVector.size(); i++)
	{
		//Polymorphic call, each responsible
		shapeVector[i]->Draw(e.Graphics);
	}

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

At this time, you only need to create a vector array storing Shape * in the MainForm class, instead of creating a vector for each type.

Now we have the same requirements. We can draw circles on the interface. The corresponding modified code is as follows:

Shape1.h

#pragma once

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 Graphic& 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);
	}
};

class Circle :public Shape
{
public:
	Point Center;
	int Radius;

	Circle(const Point& center, int radius)
	{
		this->Center = center;
		this->Radius = radius;
	}

	// Realize your own Draw and be responsible for drawing yourself
	virtual void Draw(const Graphics & g)
	{
		g.DrawCircle(Pens.red, Center, Radius);
	}
};

  MainForm.cpp

#include "Shape1.h"

#include<vector>

using namespace std;

class MainForm : public Form
{
private:
	Point p1;
	Point p2;

	//For all shapes
	vector<Shape*> shapeVector;   //When destructing, you need to free the memory in this place

public:
	MainForm()
	{
		//...
	}

protected:
	virtual void OnMouseDown(const MouseEventArgs& e);   //Mouse down
	virtual void OnMouseUp(const MouseEventArgs& e);     //Mouse up
	virtual void OnPaint(const PaintEventArgs& e);       //Interface refresh
};

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));
	}
	else if (rdoCircle.Checked)
	{
		Point center;
		center.x = (p1.x + p2.x) / 2;
		center.y = (p1.y + p2.y) / 2;

		int radius = sqrt(pow(abs(p1.x - p2.x), 2) + pow(abs(p1.y - p2.y), 2)) / 2;

		shapeVector.push_back(new Circle(center, radius));
	}

	//...
	this->Refresh();   //OnPaint is called

	Form::OnMouseUp(e);
}

void MainForm::OnPaint(const PaintEventArgs& e)
{
	//For all shapes
	for (int i = 0; i < shapeVector.size(); i++)
	{
		//Polymorphic call, each responsible
		shapeVector[i]->Draw(e.Graphics);
	}

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

 

It can be seen that corresponding to the same function, the object-oriented design method changes much less than the decomposition design method.

Code reusability is much better.

 

Posted by kiranoleti on Wed, 24 Nov 2021 17:20:18 -0800