本系列所有文章来自李建忠老师的设计模式笔记,系列如下:
设计模式(一)面向对象设计原则
23种设计模式(二)组件协作之模板方法
23种设计模式(三)组件协作之策略模式
23种设计模式(四)组件协作之观察者模式
23种设计模式(五)单一职责之装饰模式
23种设计模式(六)单一职责之桥模式
23种设计模式(七)对象创建之工厂方法
23种设计模式(八)对象创建之抽象工厂
23种设计模式(九)对象创建之原型模式
23种设计模式(十)对象创建之构建器
23种设计模式(十一)对象性能之单件模式
23种设计模式(十二)对象性能之享元模式
23种设计模式(十三)接口隔离之门面模式
23种设计模式(十四)接口隔离之代理模式
23种设计模式(十五)接口隔离之适配器
23种设计模式(十六)接口隔离之中介者
23种设计模式(十七)状态变化之状态模式
23种设计模式(十八)状态变化之备忘录
23种设计模式(十九)数据结构之组合模式
23种设计模式(二十)数据结构之迭代器
23种设计模式(二十一)数据结构之职责链
23种设计模式(二十二)行为变化之命令模式
23种设计模式(二十三)行为变化之访问器
23种设计模式(二十四)领域规则之解析器

文章目录

  • 设计方法一
  • 设计方法二
  • 重新认识面向对象
  • 面向对象设计原则
  • 将设计原则提升为设计经验

  了解面向对象继承机制(封装、继承、多态)并不代表把握面向对象机制所带来的抽象意义。面向对象设计地最大优势在于抵御变化。抵御变化不意味着不变化,而是将变化降到最小。

设计方法一

  下面看一个画图的伪码:

  • Shap1.h

  首先需要设计一些形状:

class 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{};
  • MainForm1

  接下来就是如何用鼠标将其画出来:

// 假设有一个父类,这里我们只需要关注MainForm实现
class 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;// 如果是画线就建立 线的vectorif (rdoLine.Checked){Line line(p1, p2);lineVector.push_back(line);}// 如果是画矩形,就建立矩形的vectorelse 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(); // 调用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);
}

  上述代码如果需要增加圆形,也就是改变部分,需要改动的地方就比较多了。

  看一下另外一种做法,抽象的做法:

  抽象的时候做了一个Shape的父类,里面放了一个Draw的纯虚函数,和虚的析构函数(以后子类的虚构函数才会被正确的调用到)。

  子类重写父类的Draw的虚函数,自己画自己的线条。

设计方法二

  • Shape2.h
class 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,...);}};
  • MainForm2.cpp
class 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);
}

重新认识面向对象

  以上这两种做法都把功能实现了,但是如果需要新增Circle类功能,就能看到上述两者的区别了。

  1. 理解隔离变化

  从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小

  1. 各司其职

  从微观层面来看,面向对象的方式更强调各个类的“责任”

  由于需求变化导致的新增类型不应该影响原来类型的实现–是所谓各负其责。

  1. 对象是什么

  从语言实现层面来看,对象封装了代码和数据。从规格层面讲,对象是一系列可被使用的公共接口。从概念层面讲,对象是某种拥有责任的抽象。

面向对象设计原则

  • 依赖倒置原则(DIP)

  高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)

  抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)

  比如上文中的设计方法一的代码MainForm是高层模块,它依赖于LineRect这些低层模块,这样就使得一个需要稳定的模块依赖于一个不稳定的模块,这样就不太好。

  而在第二种设计方法中,MainForm依赖于ShapeLineRect也都依赖于Shape。这个Shape就是抽象。抽象不应该依赖于实现细节,也就是抽象不应该去依赖于子类,因为抽象是稳定的,不应该去依赖一个变化的东西。

  • 开放封闭原则(OCP)

  1. 对扩展开放,对更改封闭。2. 类模块应该是可扩展的,但是不可修改。

  上文中第二种设计方法就是用扩张的方式来应对需求的变化

  • 单一职责原则(SRP)

  一个类应该仅有一个引起它变化的原因。变化的方向隐含着类的责任。也就是类中的方法不要太多。

  • Liskov 替换原则(LSP)。

  子类必须能够替换它们的基类(IS-A)。继承表达类型抽象。

  • 接口隔离原则(ISP)

  不应该强迫客户程序依赖它们不用的方法。接口应该小而完备。

  • 优先使用对象组合,而不是类继承

  类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。继承在某种程度上破坏了封装性,子类父类耦合度高。而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

  • 封装变化点

  使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。

  • 针对接口编程,而不是针对实现编程

  不将变量类型声明为某个特定的具体类,而是声明为某个接口。客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案。

  比如下面这个面向具体类型的设计改为面向接口的编程就会使得程序复用性更强:

vector<Line> lineVector;
vector<Rect> rectVector;
//改变
vector<Circle> circleVector;
//针对所有形状,并不需要针对单个图形
vector<Shape*> shapeVector;

将设计原则提升为设计经验

  1. 设计习语Design Idioms

  Design Idioms 描述与特定编程语言相关的低层模式,技巧,惯用法。

  1. 设计模式Design Patterns

  Design Patterns主要描述的是“类与相互通信的对象之间的组织关系,包括它们的角色、职责、协作方式等方面。

  1. 架构模式Architectural Patterns

  Architectural Patterns描述系统中与基本结构组织关系密切的高层模式,包括子系统划分,职责,以及如何组织它们之间关系的规则。

设计模式(一)面向对象设计原则相关推荐

  1. 代码质量评判标准、设计模式、面向对象设计原则速查表

    文章目录 代码质量评判标准 软件腐化的原因 提高系统可复用性的几点原则 可维护性与可复用性并不完全一致 面向对象设计原则 1. 面向对象设计的六大设计原则表 2. 图解面向对象涉及的六大原则 1. 开 ...

  2. 设计模式之面向对象设计原则

    单一职责模式 一个对象应该包含单一的职责,并且该职责被完整地封装在一个类中: 最简单的面向对象设计原则,它用于控制类的粒度大小: 单一职责原则是实现高内聚,低耦合的指导方针.它是最简单但又最难运用的原 ...

  3. C++设计模式(李建忠主讲) 2.面向对象设计原则

    C++设计模式 2.面向对象设计原则 重新认识面向对象 面向对象设计原则 依赖倒置原则(DIP) 开放封闭原则(OCP) 单一职责原则(SRP) Liskov替换原则(LSP) 接口隔离原则(ISP) ...

  4. uml图中的各种箭头_设计模式学习笔记(二):UML与面向对象设计原则

    1 UML 1.1 UML UML(Unified Modeling Language)是统一建模语言,1997年11月UML1.1版本提交给OMG并正式通过,成为建模语言的个那个也标准.2003年6 ...

  5. 设计模式-合成复用原则-》面向对象设计原则

    合成复用原则是面向对象设计原则的 7 条原则中剩下的最后一条,下面我们将对其进行详细地介绍. 合成复用原则的定义 合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚 ...

  6. 《设计模式:可复用面向对象软件的基础》——面向对象设计原则、创建型模式(笔记)

    文章目录 二.面向对象设计原则(补充) 2.1 重新认识面向对象 2.2 面向对象设计原则 2.2.1 依赖倒置原则(DIP) 2.2.2 开放封闭原则(OCP) 2.2.3 单一职责原则(SRP) ...

  7. 实例图解设计模式之面向对象设计七大原则

    文章目录 面向对象设计原则详解 面向对象设计原则概述 常用的面向对象设计原则 单一职责原则(Single Responsibility Principe)SRP ★★★★☆ 开闭原则(Open-Clo ...

  8. 设计模式(三)——面向对象设计原则

    设计模式需要遵循基本的软件设计原则.可维护性(Maintainability)和可复用性(Reusability)是衡量软件质量的重要的两个属性: 可维护性:软件能够被理解.改正.适应及扩展的难易程度 ...

  9. 面向对象设计原则与23种设计模式

    面向对象概述 1.万物皆对象(Java反射原理-都是借助java.lang.class这个对象的属性.定义等实现) 2.面向对象的特征:封装(对象之间的隔离性.对象内部的属性封装).继承(类的重用,耦 ...

  10. 设计模式---面向对象设计原则之单一职责原则

    单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小.单一职责原则定义如下: 单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领 ...

最新文章

  1. Java接地气日常编码技巧
  2. Eclipse创建的Maven项目报错Could not calculate build plan: Plugin
  3. 2021年春季学期-信号与系统-第四次作业参考答案-第六小题
  4. 在WinForm应用程序中嵌入WPF控件(转)
  5. 转载:CRONTAB格式,命令
  6. 【物联网】QCA4010之SNTP协议
  7. css_04 | CSS——CSS 值和单位
  8. android 通知垃圾回收,Android中垃圾回收日志信息
  9. moodle升级完整过程
  10. [转]Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法
  11. LC-130 被环绕区域
  12. C++ select模型聊天室初版
  13. Android 仿京东分类功能实现
  14. Android-MeasureSpec那些事 1
  15. 长沙理工大学本科毕业论文答辩和论文选题PPT模板
  16. python全栈开发工程师招聘_浅谈Python全栈开发工程师,让程序员都眼红的职业!...
  17. JVM命令与调优工具的使用(OOM与GC回收例)
  18. 在Matlab R2019版本上分析simulink模型的BODE图、阶跃响应、单位脉冲响应
  19. Windows10自带Ubuntu更换UI主题
  20. Leetcode 590: N-ary树的后序遍历

热门文章

  1. Coherence Step by Step 第一篇 入门(三)配置(翻译)
  2. [Android]Hello, Android!
  3. 东北三省计算机专业好的学校,东北地区哪个大学比较好 各自的王牌专业是什么...
  4. thinkphp路由配置 php7.0,Thinkphp url路由配置
  5. 基于Cocos2d-x开发guardCarrot--6 《保卫萝卜2》解锁天天向上玩法
  6. Android开发过程中的坑及解决方法收录
  7. 证明LDU分解的唯一性
  8. GNU make manual 翻译(六十六)
  9. Django 查询表的几种方式
  10. Java语言中的----条件循环