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.