c++设计模式第一回(简介)
由于之前对c的了解仅限于代码层面解决问题的思路往往比较微观因此想深入学习下设计模式从宏观上能够了解别人设计代码时的思想为自己看待问题多提供思路能够编出执行效率更高的代码。如果又碰巧也在专研设计模式的同学也欢迎在我的文章下面一起评论共同进步~~首先我们来简单的了解一下设计模式“每一个设计模式描述了一个在我们周围不断重复发生的问题以及该问题解决方案的核心。这样我们就能一次又一次使用该方案解决问题。”这是最早对于设计模式的定义其实这是一个建筑学家的定义但是我们说写代码就好比造房子你编出来的代码看起来像是豪华别墅还是平民社区这就需要看你是否有深厚的功底。先来谈一下面向对象我们说我们的代码编出来时属于一种抽象思维的实现也就是把你的想法变为现实而电脑编译并且执行你写的代码就是属于一种底层思维电脑需要为上层的代码运行提供良好的机器环境。众所周知面向对象有三大属性大家跟我一起读多态、继承、封装。多态指的是改用对象的实现封装是指隐藏内部的实现继承是指复用现有的代码。面向对象然后我们说设计模式的一个主要的特点是抽象可以将需要看似不是一个类型的问题抽象到一个问题这样就可以使用某种模式解决这个问题。然后当我们最先开始学习C语言或者其他面向过程的编程语言时我们解决一个很复杂的问题第一个想到的是去将其分解将一个大问题分解成许多个小问题这样只需要解决每个小问题就可以把整个大问题解决掉。下面我们就来通过code深入的理解一下分解和抽象的区别。下面的代码是这样的我在编写这个程序的时候使用的是伪代码并且有些代码为了书写方便没有太遵循C的编译规则如果其他同学需要具体实现的话可以遵循正常的c编译规则进行实现。因为如果要把整个可以编译执行的代码贴过来的话会很复杂我们只需要了解里面的思想就可以。好这个例子实现的是一个简单的画图工具一个画图工具必不可少的有描画的点、线、形状等。分解代码如下// 头文件定义 #ifndef SHAPE_H #define SHAPE_H // 描画的点的类 class Point { private: int x; int y; }; // 描画线条的类需要知道起点和终点 class Line { public: Line(const Point start, const Point end) { this-start start; this-end end; } public: Point start; Point end; }; // 描画矩形的类需要知道和描画的矩形的长和宽 class Rect { public: Rect(const Point leftUp, int width, int height) { this-lefUp leftUp; this-width width; this-height height; } private: Point lefUp; int width; int height; }; // 如果需要增加新的形状的话只需要添加新的类 #endif // SHAPE_H // 下面是具体实现 #include Shape.h #include vector // 这个类是用于在画框上进行描画的类 class MainForm :public Form { public: MainForm() { // ... } private: Point p1; Point p2; // 用于存放Line和Rect的类 vectorLine lineVector; vectorRect rectVector; protected: // 操作的回调函数 void onMouseDown(const MouseEventArg e); void onMouseUp(const MouseEventArg e); void onPaint(const MouseEventArg e); }; void MainForm::onMouseDown(const MouseEventArg e) { p1.x e.x; p1.y e.y; Form::onMouseDown(e); } void MainForm::onMouseUp(const MouseEventArg e) { p2.x e.x; p2.y e.y; // 判断当前画的是线条还是矩形如果新加了其他的形状也需要新家判断去满足此形状的描画 if (moduleLine.checked) { Line line(p1, p2); lineVector.push_back(line); } else if (moduleRect.checked) { int width trans(p2.x - p1.x); int height trans(p2.y - p1.y); Rect rect(p1, width, height); rectVector.push_back(rect); } // ... // 设置好之后需要刷新下界面进行显示 this-refresh(); // ... From::onMouseUp(e); } void MainForm::onPaint(const MouseEventArg e) { // 真正描画形状时的回调如果有新的形状添加进来就需要增加新的循环进行描画 for (int i 0; i lineVector.size(); i) { e.Grapic.Draw(Pens.RED, lineVector[i].start.x, lineVector[i].start.y, lineVector[i].end.x, lineVector[i].end.y); } for (int i 0; i rectVector.size(); i) { e.Grapic.Draw(Pens.RED, rectVector[i].leftUp, rectVector[i].width, rectVector[i].height); } }从上面的例子可以看出如果要新加形状的话需要变更的地方有很多首先要新加类然后要在回调的时候加上对应的形状的判断这样如果形状有成百上千个的话变更量会很巨大因此这种方法虽然易懂但是变更量太大。我们再来看下抽象的你思维方法会怎么应对增加形状时的变更// 头文件定义 #ifndef SHAPE_H #define SHAPE_H // 基类shape class Shape { public: // 设置Draw的纯虚函数以及虚方法 virtual void Draw(const Graphic g) 0; virtual ~Shape(){} }; // 坐标点的类继承自Shape class Point { private: int x; int y; }; // 描画线条的类继承自Shape class Line :public Shape { public: Line(const Point start, const Point end) { this-start start; this-end end; } // 添加虚函数Draw描画自己的图形 virtual Draw(const Graphic g) { g.DrawLine(Pens.RED, start.x, start.y, end.x, end.y); } private: Point start; Point end; }; // 描画矩形的类 class Rect :public Shape { public: Rect(const Point leftUp, int width, int height) { this-lefUp leftUp; this-width width; this-height height; } // 添加虚函数Draw描画自己的图形 virtual Draw(const Graphic g) { g.DrawLine(Pens.RED, lefUp, width, height); } private: Point lefUp; int width; int height; }; #endif // SHAPE_H // cpp的具体实现 #include Shape.h #include vector class MainForm :public Form { public: MainForm() { // ... } private: Point p1; Point p2; // 使用基类的指针这样可以实现复用 vectorShape* shapeVector; protected: void onMouseDown(const MouseEventArg e); void onMouseUp(const MouseEventArg e); void onPaint(const MouseEventArg e); }; void MainForm::onMouseDown(const MouseEventArg e) { p1.x e.x; p1.y e.y; Form::onMouseDown(e); } void MainForm::onMouseUp(const MouseEventArg e) { p2.x e.x; p2.y e.y; // 判断形状时依旧需要增加类型变化但是在后面加上工厂模式之后就可以不用变化 if (moduleLine.checked) { shapeVector.push_back(new line(p1, p2)); } else if (moduleRect.checked) { int width trans(p2.x - p1.x); int height trans(p2.y - p1.y); shapeVector.push_back(new rect(p1, width, height)); } // ... this-refresh(); // ... From::onMouseUp(e); } void MainForm::onPaint(const MouseEventArg e) { // 从shapeVector中取出的元素类型为Shape*这个指针可以根据具体push_back进去的形状 // 调用不同的函数 for (int i 0; i shapeVector.size(); i) { shapeVector[i]-Draw(e.Graphics); } }我们使用抽象的思维时将所有的形状都继承一个基类然后每个子类实现自己的Draw方法这样就可以在调用时依赖多态性调用到具体的Draw方法即使有很多形状依然只需要一个for循环取出形状。通过上面的例子可以看出使用抽象的思维解决问题时可以更好的应对变化复用性更强分解的思维虽然可以解决问题但是一旦客户的需求变化那么变更就会增多很多。 所以两种思维的应用场景不一致。下一回会正式进入设计模式的详解。本次反向内容据来自于自己通过看c设计模式的视频的一些总结设计模式原视频可以参考极客李建忠老师讲的23种设计模式我认为讲的很到位。