前言
为什么要进行面向对象设计?
抵御变化,变化是复用的天地,面向对象的最大优势就是抵御变化
什么是对象?
从语言层面上看,对象封装了代码和数据
从规格层面上看,对象是一系列可被使用的公共接口
从概念层面上看,对象是拥有某些责任的抽象
依赖倒置原则(DIP)
高层模块(稳定)不应当依赖于低层模块(变化),二者都应该依赖于抽象(稳定)
抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)
举一个例子:
来解读一下这两句话。
第一句话。高层(MainForm)不依赖于低层(Rectangle、Line、Circle),而是两者都依赖于抽象(Shape),为什么呢,你在MainForm里定义一个Shape数组,低层继承自Shape,重写相应的虚函数。这样当低层模块改变了行为,不会影响Shape,更不会影响MainForm。
第二句话。低层模块依赖于的实现细节继承自(依赖于)Shape,Shape影响低层模块,低层模块不影响Shape。
开闭原则(OCP)
对扩展开发,对修改封闭。
类模块应该是可扩展的,但是不可修改。
还是上边那个场景。你要新画一个图形,比如说三角形。修改的做法是定义一个新的三角形类,在MainForm中画。扩展的做法是继承自Shape,然后Shape *tri=new TriAngle();tri.paint()即可。
单一职责原则(SRP)
一个类应该仅有一个引起它变化的原因。
变化的方向隐含着类的责任。
笔者本人做过一个远控木马软件的项目,因为有很多功能嘛,笔者对文件管理,CMD管理,注册表管理等不同功能各创建了相应的Manager对象,里边有对数据包拆解之后的处理函数。在一次求职面试中面试官问我为什么要这么设计,为什么属于不同功能的数据包不放在一起处理呢?笔者当时答得不算好,回答的是这是属于不同的功能,放在一块处理不好,也不应该放在一起。现在回忆起来应该直接回答一句“要遵循单一职责原则”。可惜可惜。
里氏替换原则(LSP)
子类必须能够替换他们的积累
继承表达类型抽象
这个没啥可说的
接口隔离原则(ISP)
不应该强迫客户程序依赖他们不用的方法
接口应该小而完备
也就是该Public就Public,该Private就Private
优先使用组合,而不是继承
类继承为“白盒复用”,组合为“黑盒复用”
继承在一定程度上破坏了封装性,子父类耦合度过高。父类若是修改了什么会对子类产生很大的影响。
组合只要求被组合的对象有良好的定义,你内部怎么实现的我不管,我只要知道这个接口能用就行。
迪米特原则(LOD)
也就是最少知道原则,也就是两个模块不需要直接通信就不要在两者之间设置相应的接口。
举个例子:
健身房Gym,客户Client,银行卡Card。这三者的关系是Gym和Client可以有交互,因为Gym要服务于Client。当客户要付钱的时候,要从Card中付钱,这个时候Gym可以直接和Card交互,从Card中刷钱吗?那就出大问题了,你卡还想要了?正确的场景是Gym和Client说要给钱了,然后Client从Card中划钱。也就是说Card是Client的东西,你Gym别来沾边。即最少知道原则。
针对接口编程,而不是针对实现编程
不将变量类型声明为某个特定的具体类,而是声明为某个接口。
客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
减少系统各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案