C# · 12月 19, 2021

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 宽:%dn”,_width,_height);

}

圆形:

//圆

class MyCircle {

public:

MyCircle(int centerX,int centerY,int radio);

void draw();

private:

int _centerX,_centerY,_radio;

};

MyCircle::MyCircle(int centerX,int radio) {

_centerX = centerX;

_centerY = centerY;

_radio = radio;

}

void MyCircle::draw() {

printf(“绘制圆 – 圆心:(%d,%d) 半径:%dn”,_centerX,_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 secondY) {

_firstX = firstX;

_firstY = firstY;

_secondX = secondX;

_secondY = secondY;

}

void MyLine::draw() {

printf(“绘制直线 – 起点:(%d,%d),终点:(%d,%d)n”,_firstX,_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,_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,95);

_myRect = new MyRectangle(150,300);

_myCircle = new MyCircle(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 radio);

void draw()override;

private:

int _centerX,_radio;

};

//直线

class MyLine :public IDraw {

public:

MyLine(int firstX,int secondY);

void draw()override;

private:

int _firstX,_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 _drawObjs;

};

MyDraw::MyDraw() {

}

void MyDraw::init() {

_drawObjs.push_back(new MyPoint(10,20));

_drawObjs.push_back(new MyLine(30,95));

_drawObjs.push_back(new MyRectangle(150,300));

_drawObjs.push_back(new MyCircle(100,50));

}

void MyDraw::draw() {

int cnt = _drawObjs.size();

for (int i = 0; i < cnt;i++){

_drawObjs.at(i)->draw();

}

}

这样是不是更加方便了,客户端都有不用做任何的修改!

如果要添加新的图形,只要是继承IDraw这个基类,然后在子类中完成绘制功能,把生成的子类对象,添加到_drawObjs中就可以了。绘制这部分功能就会自动的调用完成了。

外观模式优点:

1.减少了系统之间的耦合性。

2.提高灵活性。