1. 程式人生 > >C++設計模式-外觀模式詳解

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.提高靈活性。