什么时候设计模式?
“每一个模式描述了一个在我们周围不断重复发生的问题, 以及该问题的解决方案的核心。这样,你就能一次又一次 地使用该方案而不必做重复劳动”
——Christopher Alexander
GOF设计模式
Design Patterns: Elements of Reusable Object-Oriented Software
中译名: 设计模式 可复用面向对象软件的基础
从面向对象谈起
底层思维->程序员<-抽象思维
底层思维: 向下, 如何把握机器底层从微观理解对象构造
- 语言构造
- 编译转换
- 内存模型
- 运行时机制
抽象思维: 向上, 向上,如何将我们的周围世界抽象为程序代码
- 面向对象
- 组件封装
- 设计模式
架构模式
向下:深入理解三大面向对象机制
封装,隐藏内部实现
- 继承,复用现有代码
- 多态,改写对象行为
向上:深刻把握面向对象机制所带来的抽象意义,理解如何使用这些机制来表达现实世界,掌握什么是“好的面向对象设计”
软件设计固有的复杂性-变化
建筑商从来不会去想给一栋已建好的100层高的 楼房底下再新修一个小地下室——这样做花费极大而且注定要失败。然而令人惊奇的是,软件系统的用户在要求作出类似改变时却不会仔细考虑,而且他们认为这只是需要简单编程的事。
——Object-Oriented Analysis and Design with Applications
软件设计复杂的根本原因-变化
- 客户需求的变化
- 技术平台的变化
- 开发团队的变化
- 市场环境的变化
如何解决复杂性?
分解
- 人们面对复杂性有一个常见的做法:即分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题。
抽象
- 更高层次来讲,人们处理复杂性有一个通用的技术,即抽象。 由于不能掌握全部的复杂对象,我们选择忽视它的非本质细节, 而去处理泛化和理想化了的对象模型。
代码示例 1. 结构化 VS. 面向对象
伪代码
方式1: 分解
// shape.hclass Point{public:int x;int y;};class Line{public:Point start;Point end;Line(const Point& start, const Point& end){this->start = start;this->end = end;}};class Rect{public:Point leftUp;int width;int height;Rect(const Point& leftUp, int width, int height){this->leftUp = leftUp;this->width = width;this->height = height;}};//增加class Circle{};
// MainForm.cppclass MainForm : public Form {private:Point p1;Point p2;vector<Line> lineVector;vector<Rect> rectVector;//改变vector<Circle> circleVector;public:MainForm(){//...}protected:virtual void OnMouseDown(const MouseEventArgs& e);virtual void OnMouseUp(const MouseEventArgs& e);virtual void OnPaint(const PaintEventArgs& e);};void MainForm::OnMouseDown(const MouseEventArgs& e){p1.x = e.X;p1.y = e.Y;//...Form::OnMouseDown(e);}void MainForm::OnMouseUp(const MouseEventArgs& e){p2.x = e.X;p2.y = e.Y;if (rdoLine.Checked){Line line(p1, p2);lineVector.push_back(line);}else if (rdoRect.Checked){int width = abs(p2.x - p1.x);int height = abs(p2.y - p1.y);Rect rect(p1, width, height);rectVector.push_back(rect);}//改变else if (...){//...circleVector.push_back(circle);}//...this->Refresh();Form::OnMouseUp(e);}void MainForm::OnPaint(const PaintEventArgs& e){//针对直线for (int i = 0; i < lineVector.size(); i++){e.Graphics.DrawLine(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.Graphics.DrawRectangle(Pens.Red,rectVector[i].leftUp,rectVector[i].width,rectVector[i].height);}//改变//针对圆形for (int i = 0; i < circleVector.size(); i++){e.Graphics.DrawCircle(Pens.Red,circleVector[i]);}//...Form::OnPaint(e);}
方式2: 抽象
// shape.hclass Shape{public:virtual void Draw(const Graphics& g)=0;virtual ~Shape() { }};class Point{public:int x;int y;};class Line: public Shape{public:Point start;Point end;Line(const Point& start, const Point& end){this->start = start;this->end = end;}//实现自己的Draw,负责画自己virtual void Draw(const Graphics& g){g.DrawLine(Pens.Red,start.x, start.y,end.x, end.y);}};class Rect: public Shape{public:Point leftUp;int width;int height;Rect(const Point& leftUp, int width, int height){this->leftUp = leftUp;this->width = width;this->height = height;}//实现自己的Draw,负责画自己virtual void Draw(const Graphics& g){g.DrawRectangle(Pens.Red,leftUp,width,height);}};//增加class Circle : public Shape{public://实现自己的Draw,负责画自己virtual void Draw(const Graphics& g){g.DrawCircle(Pens.Red,...);}};
推荐public继承
// MainForm.cppclass MainForm : public Form {private:Point p1;Point p2;//针对所有形状vector<Shape*> shapeVector;public:MainForm(){//...}protected:virtual void OnMouseDown(const MouseEventArgs& e);virtual void OnMouseUp(const MouseEventArgs& e);virtual void OnPaint(const PaintEventArgs& e);};void MainForm::OnMouseDown(const MouseEventArgs& e){p1.x = e.X;p1.y = e.Y;//...Form::OnMouseDown(e);}void MainForm::OnMouseUp(const MouseEventArgs& e){p2.x = e.X;p2.y = e.Y;if (rdoLine.Checked){shapeVector.push_back(new Line(p1,p2));}else if (rdoRect.Checked){int width = abs(p2.x - p1.x);int height = abs(p2.y - p1.y);shapeVector.push_back(new Rect(p1, width, height));}//改变else if (...){//...shapeVector.push_back(circle);}//...this->Refresh();Form::OnMouseUp(e);}void MainForm::OnPaint(const PaintEventArgs& e){//针对所有形状for (int i = 0; i < shapeVector.size(); i++){shapeVector[i]->Draw(e.Graphics); //多态调用,各负其责}//...Form::OnPaint(e);}
vector<Shape*> shapeVector
- 为了支持多态性,如果不用指针,会导致对象切割
shapeVector.push_back(new Line(p1,p2))
- 不能放栈对象,而要放堆对象,注意释放
shapeVector[i]->Draw(e.Graphics)
- 多态调用,各负其责
- 根据内部存储的实际类型
方式1和方式2哪种更好?
需求变化
- 需要画一个圆形
方式2代码变更更少,重用性更高
