從重構入手,瞭解設計模式。
一、實現軟件
完成一個圖形軟件,畫出Line和我Rect
方法一:分解
#include <iostream>
#include <vector>
using namespace std;
//畫圖
class Point
{
public:
int m_x;
int m_y;
};
class Line
{
public:
Line(){std::cout << "Line" << std::endl;}
void draw (int i){std::cout << "draw line" << std::endl;}
};
class Rect
{
public:
Rect() {std::cout << "Rect" << std::endl;}
void draw (int i){std::cout << "draw Rect" << std::endl;}
};
class Paint
{
public:
vector<Line> m_vecLine;
vector<Rect> m_vecRect;
//添加
void paintData(int type)
{
if (type == 1)
{
Line line;
m_vecLine.push_back(line);
}
else if(type == 2)
{
Rect rect;
m_vecRect.push_back(rect);
}
}
//顯示(分解處理)
void paintShow()
{
for(vector<Line>::iterator ite = m_vecLine.begin(); ite != m_vecLine.end(); ite++)
{
ite->draw(1);
}
for(vector<Rect>::iterator ite = m_vecRect.begin(); ite != m_vecRect.end(); ite++)
{
ite->draw(1);
}
}
};
int main()
{
Paint p;
//添加圖形
p.paintData(1);
p.paintData(2);
//顯示圖形
p.paintShow();
}
//打印以下內容
Line
Rect
draw line
draw Rect
方法二:抽象
#include <iostream>
#include <vector>
using namespace std;
//畫圖
class Point
{
public:
int m_x;
int m_y;
};
class sharpe //不同處
{
public:
virtual ~sharpe(){};
virtual void draw(int i){};
};
class Line : public sharpe
{
public:
Line(){std::cout << "Line" << std::endl;}
void draw (int i){std::cout << "draw line" << std::endl;}
};
class Rect : public sharpe
{
public:
Rect() {std::cout << "Rect" << std::endl;}
void draw (int i){std::cout << "draw Rect" << std::endl;}
};
class Paint
{
public:
vector<sharpe *> m_vecPic;//不同處
//添加
void paintData(int type)
{
if (type == 1)
{
m_vecPic.push_back(new Line);
}
else if(type == 2)
{
m_vecPic.push_back(new Rect);
}
}
//顯示(抽象統一處理)
void paintShow()
{
//不同處
for(vector<sharpe*>::iterator ite = m_vecPic.begin(); ite != m_vecPic.end(); ite++)
{
(*ite)->draw(1);
}
}
};
int main()
{
Paint p;
//添加圖形
p.paintData(1);
p.paintData(2);
//顯示圖形
p.paintShow();
}
//打印以下內容
Line
Rect
draw line
draw Rect
二、實現一需求更改
現在把上面的需求再增加一個,多了一個circle打印圓形的功能
方法一:分解(更改後,需要修改5個地方)
#include <iostream>
#include <vector>
using namespace std;
//畫圖
class Point
{
public:
int m_x;
int m_y;
};
class Line
{
public:
Line(){std::cout << "Line" << std::endl;}
void draw (int i){std::cout << "draw line" << std::endl;}
};
class Rect
{
public:
Rect() {std::cout << "Rect" << std::endl;}
void draw (int i){std::cout << "draw Rect" << std::endl;}
};
class Circle //1
{
public:
Circle() {std::cout << "Circle" << std::endl;}
void draw (int i){std::cout << "draw Circle" << std::endl;}
};
class Paint
{
public:
vector<Line> m_vecLine;
vector<Rect> m_vecRect;
vector<Circle> m_vecCircle; //2
//添加
void paintData(int type)
{
if (type == 1)
{
Line line;
m_vecLine.push_back(line);
}
else if(type == 2)
{
Rect rect;
m_vecRect.push_back(rect);
}
else if(type == 3) //3
{
Circle circle;
m_vecCircle.push_back(circle);
}
}
//顯示
void paintShow()
{
for(vector<Line>::iterator ite = m_vecLine.begin(); ite != m_vecLine.end(); ite++)
{
ite->draw(1);
}
for(vector<Rect>::iterator ite = m_vecRect.begin(); ite != m_vecRect.end(); ite++)
{
ite->draw(1);
}
for(vector<Circle>::iterator ite = m_vecCircle.begin(); ite != m_vecCircle.end(); ite++) //4
{
ite->draw(1);
}
}
};
int main()
{
Paint p;
//添加圖形
p.paintData(1);
p.paintData(2);
p.paintData(3);
//顯示圖形
p.paintShow();
}
方法二:抽象(更改後,只更改了兩個地方)
#include <iostream>
#include <vector>
using namespace std;
//畫圖
class Point
{
public:
int m_x;
int m_y;
};
class sharpe
{
public:
virtual ~sharpe(){};
virtual void draw(int i){};
};
class Line : public sharpe
{
public:
Line(){std::cout << "Line" << std::endl;}
void draw (int i){std::cout << "draw line" << std::endl;}
};
class Rect : public sharpe
{
public:
Rect() {std::cout << "Rect" << std::endl;}
void draw (int i){std::cout << "draw Rect" << std::endl;}
};
class Circle : public sharpe //1
{
public:
Circle() {std::cout << "Circle" << std::endl;}
void draw (int i){std::cout << "draw Circle" << std::endl;}
};
class Paint
{
public:
vector<sharpe *> m_vecPic;
//添加
void paintData(int type)
{
if (type == 1)
{
m_vecPic.push_back(new Line);
}
else if(type == 2)
{
m_vecPic.push_back(new Rect);
}
else if(type == 3)
{
m_vecPic.push_back(new Circle); //2
}
}
//顯示
void paintShow()
{
for(vector<sharpe*>::iterator ite = m_vecPic.begin(); ite != m_vecPic.end(); ite++)
{
(*ite)->draw(1);
}
}
};
int main()
{
Paint p;
//添加圖形
p.paintData(1);
p.paintData(2);
p.paintData(3);
//顯示圖形
p.paintShow();
}
//打印以下內容
Line
Rect
Circle
draw line
draw Rect
draw Circle
結論:
抽象比分解解決問題修改起來更方便,可複用性強
C++小知識
class A;
class B;
class C;繼承自B
vector<A> t;
vector<B*> t; //使用指針可以使用多態
#include <iostream>
#include <vector>
using namespace std;
enum COLOR
{
RED=1, YELLO, BLACK, WHITE
};
//畫圖
class Point
{
public:
int m_x;
int m_y;
};
class sharpe
{
public:
virtual ~sharpe(){};
virtual void draw(int i){}; //3,沒有變化的draw1、draw2\ 5繼承的人都有用到draw
};
class Color
{
public:
int curColor(){}
};
class Pen
{
public:
Color color;
void setColor(int i) {}
};
class Line : public sharpe
{
public:
Pen m_pen; //6
//Color Ccolor;//8 color在pen裏面使用,這裏不能使用
void setPen(Pen &pen){ m_pen = pen; }
Line(){std::cout << "Line" << std::endl;}
void draw (int i){drawTest();std::cout << "draw line" << std::endl;}
private:
void drawTest(){} //5
};
class Rect : public sharpe
{
public:
Rect() {std::cout << "Rect" << std::endl;}
void draw (int i){std::cout << "draw Rect" << std::endl;}
};
class Circle : public sharpe
{
public:
Circle() {std::cout << "Circle" << std::endl;}
void draw (int i){std::cout << "draw Circle" << std::endl;}
};
class Paint
{
public:
vector<sharpe *> m_vecPic; //1 4-1
//添加
void paintData(int type)
{
if (type == 1)
{
m_vecPic.push_back(new Line);//4-2
}
else if(type == 2)
{
m_vecPic.push_back(new Rect);
}
else if(type == 3)
{
m_vecPic.push_back(new Circle); //2
}
}
//顯示
void paintShow()
{
for(vector<sharpe*>::iterator ite = m_vecPic.begin(); ite != m_vecPic.end(); ite++)
{
(*ite)->draw(1); //2(不是每個Line\Rect都要重新固定,重新寫)
}
}
};
int main()
{
Paint p;
//添加圖形
p.paintData(1);
p.paintData(2);
p.paintData(3);
//顯示圖形
p.paintShow();
}
//打印以下內容
Line
Rect
Circle
draw line
draw Rect
draw Circle
7大設計模式原則
1.依賴倒置原則DIP(面對接口編程,不要對實現編程)
高層模塊(穩定)不依賴底層模塊(變化),二者都要依賴抽象(穩定)
抽象(穩定)不依賴實現細節(變化),實現細節應該依賴抽象(穩定)
如:vector m_vecPic; 針對接口,而不是真實類型
2.開放封閉原則(OCP)
對擴展開放,對更改封閉
如上例需要添加畫圓圈,秩序添加Class Circle即可,其他不用更改
3.單一職責原則(SRP)
一個類只有一個引起它變化的原因,比如:
比如:sharp類只有一個draw類是變化的,需要與衆不同的。
4.Liskov替換原則(LSP)
子類必須能替換父類(父類的功能,子類不能有父類同名的非虛函數)
繼承表達,類型抽象(Line和Rect兩個對外統一爲sharpe替換)
5.接口隔離原則(ISP)
接口要小(不是接口的函數都用private)
繼承的接口類那些虛函數都要有用到,不然就把他拆分掉,使用多繼承
6.優先使用對象組合而不是類繼承(繼承應該是生物和動物的包含關係,而不是簡單的父親和兒子的繼承關係)
繼承子類和父類耦合度高
對象組合要求對象接口的合作,耦合度低
7.封裝變化點
封裝變化點,一側穩定,一側變化。穩定如Paint類,是變化的,而Sharpe是穩定的,Line和Rect是變化的。
8.迪米特原則(最少知道原則)
一個類裏面不管屬性還是行爲,用到的其他類儘量少。
設計模式需要遵循
需求變化的點和穩定的點
提高複用
重構而不是直接使用設計模式
設計模式從重構開始-重構關鍵方法
a 靜態-》動態(A::test() 調用改爲 a->test();)
b 早綁定-》晚綁定 (void printTest(A &a){ a.test(); } A應該只是一個接口類,並且test是一個虛函數)https://blog.csdn.net/a133900029/article/details/80185428
c 繼承-》組合(類A繼承類B 改爲 類A包含類B元素)
d 編譯時依賴-》運行時依賴()
e 緊耦合-》鬆耦合(分接口出來)