C++設計模式-外觀模式詳解
外觀模式:要求一個子系統與外部通訊必須通過一個同一的系統完成,也就是把一系列的子系統封裝在一個同一的大系統中。子系統中的所有呼叫行為都是通過大系統提供同一的介面來完成。
在一個繪圖系統中,需要繪製各種這樣的圖形,比如說是矩形、圓形、直線等。那好我們就根據以上場景搭建一個簡單的繪製場景。首先把各種繪製的圖形定義出來。
矩形:
//矩形
class MyRectangle {
public:
MyRectangle(int w,int h);
void draw();
private:
int _width, _height;
};
MyRectangle::MyRectangle(int w, int h) { _width = w; _height = h; } void MyRectangle::draw() { printf("繪製矩形 - 長:%d 寬:%d\n", _width, _height); }
圓形:
//圓
class MyCircle {
public:
MyCircle(int centerX, int centerY, int radio);
void draw();
private:
int _centerX, _centerY, _radio;
};
MyCircle::MyCircle(int centerX, int centerY, int radio) { _centerX = centerX; _centerY = centerY; _radio = radio; } void MyCircle::draw() { printf("繪製圓 - 圓心:(%d,%d) 半徑:%d\n", _centerX, _centerY, _radio); }
直線:
//直線
class MyLine {
public:
MyLine(int firstX, int firstY, int secondX, int secondY);
void draw();
private:
int _firstX, _firstY, _secondX, _secondY;
};
MyLine::MyLine(int firstX, int firstY, int secondX, int secondY) { _firstX = firstX; _firstY = firstY; _secondX = secondX; _secondY = secondY; } void MyLine::draw() { printf("繪製直線 - 起點:(%d,%d),終點:(%d,%d)\n", _firstX, _firstY, _secondX, _secondY); }
點:
//點
class MyPoint {
public:
MyPoint(int x, int y);
void draw();
private:
int _x, _y;
};
MyPoint::MyPoint(int x, int y) {
_x = x;
_y = y;
}
void MyPoint::draw() {
printf("繪製點 - 座標:(%d,%d)\n", _x, _y);
}
以上需要繪製的圖形已經新增完成了,下面看一下客戶端是如何呼叫的:
int main() {
//繪製點
MyPoint* pt = new MyPoint(10, 20);
pt->draw();
//繪製直線
MyLine* l = new MyLine(30, 30, 90, 95);
l->draw();
//繪製矩形
MyRectangle* rect = new MyRectangle(150, 300);
rect->draw();
//繪製圓
MyCircle* circle = new MyCircle(100, 100, 50);
circle->draw();
return 0;
}
程式碼編寫完成了,執行一下:
OK,呼叫完成了,看到了嗎?
也就是說我每次新增一個繪製的圖形都要重寫一遍客戶端,如果新增的圖形比較複雜的,或者是有很多組合圖形的情況,客戶端就會變得非常的臃腫,不易於維護。
那麼怎麼解決的?咱們把需要繪製的這個部分功能重新封裝起來。需要重新定義一個類實現這個功能。
class MyDraw {
public:
MyDraw();
void init();
void draw();
private:
MyRectangle* _myRect;
MyCircle* _myCircle;
MyLine* _myLine;
MyPoint* _myPoint;
};
MyDraw::MyDraw() {
}
void MyDraw::init() {
_myPoint = new MyPoint(10, 20);
_myLine = new MyLine(30, 30, 90, 95);
_myRect = new MyRectangle(150, 300);
_myCircle = new MyCircle(100, 100, 50);
}
void MyDraw::draw() {
_myPoint->draw();
_myLine->draw();
_myRect->draw();
_myCircle->draw();
}
現在把客戶端的呼叫之後修改一下:
int main() {
MyDraw* myDraw = new MyDraw;
myDraw->init();
myDraw->draw();
return 0;
}
執行一下:
嗯哼,執行效果是一樣的。
客戶端只要編寫很少的程式碼就可以實現上述的功能,也許你會說,這有什麼區別嘛?只不過把我需要的功能重新封裝了一下而已呀。
但是你不要忘了,如果直接使用的話,各種繪製圖形就會和客戶端產生一定的耦合性,如果是客戶端做了修改就會影響到這部分的修改,但是如果做了封裝的話就是把客戶端和實際的繪製過程分開來,互不影響。
每次需要繪製的時候,只要把封裝的介面直接呼叫就可以了。
其次,如果想要新增新的繪製圖形,比如說三角形、圖片等,只要在封裝的類中新增行的繪製就可以了,而不用修改客戶端的程式碼。
再次,如果這部分繪製的過程是通過DLL形式釋出出去的,那麼每次只要修改DLL中程式碼之後重新編譯就可以了,和客戶端沒有什麼關係,客戶端沒有修改任何程式碼就能實現的新增新的繪製圖形。
看看咱們的程式碼是不是還有優化的空間?
既然會都有繪製的功能,是不是可以把繪製的過程提取出來形成基類呢?
class IDraw {
public:
IDraw();
~IDraw();
virtual void draw();
};
IDraw::IDraw() {
}
IDraw::~IDraw() {
}
void IDraw::draw() {
}
繪製相關的類都繼承這個基類:
//矩形
class MyRectangle:public IDraw {
public:
MyRectangle(int w,int h);
void draw()override;
private:
int _width, _height;
};
//圓
class MyCircle :public IDraw {
public:
MyCircle(int centerX, int centerY, int radio);
void draw()override;
private:
int _centerX, _centerY, _radio;
};
//直線
class MyLine :public IDraw {
public:
MyLine(int firstX, int firstY, int secondX, int secondY);
void draw()override;
private:
int _firstX, _firstY, _secondX, _secondY;
};
//點
class MyPoint :public IDraw {
public:
MyPoint(int x, int y);
void draw()override;
private:
int _x, _y;
};
CPP檔案保持不變,這裡就不在重複了。
然後在修改一下繪製類:
class MyDraw {
public:
MyDraw();
void init();
void draw();
private:
std::vector<IDraw*> _drawObjs;
};
MyDraw::MyDraw() {
}
void MyDraw::init() {
_drawObjs.push_back(new MyPoint(10, 20));
_drawObjs.push_back(new MyLine(30, 30, 90, 95));
_drawObjs.push_back(new MyRectangle(150, 300));
_drawObjs.push_back(new MyCircle(100, 100, 50));
}
void MyDraw::draw() {
int cnt = _drawObjs.size();
for (int i = 0; i < cnt;i++){
_drawObjs.at(i)->draw();
}
}
這樣是不是更加方便了,客戶端都有不用做任何的修改!
如果要新增新的圖形,只要是繼承IDraw這個基類,然後在子類中完成繪製功能,把生成的子類物件,新增到_drawObjs中就可以了。繪製這部分功能就會自動的呼叫完成了。
外觀模式優點:
1.減少了系統之間的耦合性。
2.提高靈活性。