程序设计的要遵循的一些理论,也可以理解为程序设计的一种要求和目标,是面向对象程序设计的基石,也是面向对象程序设计的质量保障和依据。
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、提高扩展性、提高灵活性和提高开发效率,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
简单说:
模式:在某些场景下,针对某类问题的某种通用的解决方案。
场景:项目所在的环境。
问题:约束条件,项目目标等
解决方案:通用、可复用的设计,解决约束达到目标。
设计模式可以分为三种类型
创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。
结构型模式:把类或对象结合在一起形成一个更大的结构。
行为型模式:类和对象如何交互,及划分责任和算法。
1.1单一职责原则
解释:
单一职责 SRP–Single Responsibility Principle
应该有且仅有一个原因引起类的变更。
1.系统中的每个类都应该只有一个职责,而所有类所关注的就是自身之职责的完成。
2.职责是指为”变化的原因”。
3.如果能想到多个原因去改变一个类,这个类就是多个职责。
4.并不是单一功能原则,并不是每个类只能有一个方法,而是单一”变化的愿意”原则。
5.如果一个类有多个职责,这些职责就耦合在了一起,当一个职责发生变化时,可能会影响其他职责。
6.多个职责耦合在一起,会影响服用性(可能只需要服用该类的某一个功能,但是该职责和其他职责耦合在一起,很难分离出来)。
其实就是我们常说的高内聚低耦合原则,.单一职责原则是最简单也非常难实现的原则。
好处:
将功能分类,模块划分明确,修改一个模块不会造成其他模块的修改,降低模块之间的耦合度。
举例:
MVC模式下,无论是Controller层还是service亦或是DAO层,针对每个表格都有独立的实现类,不要掺杂在一起,以免因为任何一个操作的变化都引起类的修改。
图解:
饭店老板 既当厨师,又当服务员,这不合适
1.2开闭原则
解释:
开闭原则 OCP— Open Closed Principle
软件实体应该对功能的拓展开放,对修改关闭的原则, 因为需求有变化,要求我们设计程序时必须为程序功能的新增留好接口,在新增功能时,不要修改原有代码,而是新增代码.让程序实现对拓展开放,对修改关闭测设计要求。
1实现开闭原则的关键是抽象。
2定义一个抽象层,之规定功能而不提供实现,实现通过定义具体的类来完成。
3当需求变化时,不通过修改抽象层来完成,而是通过定义抽象层的新实现完成。
4通过抽象类及接口,规定了具体的类的特征作为抽象层,相对稳定,不需要修改,从而满足对修改关闭,从抽象类到处的具体类可以作为改变系统的行为,从而满足对扩展开放。
好处:
1通过扩展已有软件系统,可以提供新的行为,以满足对软件新的需求,提高了软件系统的适应性和灵活性。
2已有的软件模块,特别是重要的抽象层模块不能再修改,提高了软件系统的一定的稳定性和延续性。
3这样的设计同时也满足了可复用性和可维护性。
举例:
MVC模式下,每一层都有接口,是和其他层对接的规范,新增功能时,我们可以选择新增Controller层和service层和mapper层代码 不用去修改原有代码。
图解:
每一个层次的要求都要留好接口,作为规范,增加功能就相当于多了一个实现类去实现接口,而不是在先有代码上修改,工厂方法模式。
1.3里氏代换原则
解释
里氏代换原则 LSP------ Liskov Substitution Principle
所有引用的基类的地方必须能够透明的使用其子类的对象。
凡是父类出现的地方,其子类就可以出现,而且调用子类还不能产生多于父类的错误和异常,调用者根本就不要需要知道是子类还是父类对象.但是反过来就不行,子类出现的地方父类未必就能使用。
父类出现的地方,子类对象就能出现;子类出现的地方,父类未必能出现(泛型的向下限定)。
1.子类必须完全实现了父类的方法,具备父类完全的功能。
2.子类可以有自己的特征。
3.覆盖和实现父类方法时,输入的参数可以被放大,但是不能被缩小。
4.覆盖和实现父类方法时,输出的结果可以被缩小,但是不能被放大。。
5.覆盖和实现父类方法时,产生的异常可以被缩小,但是不能被放大
好处:
为我们如何实现良好的继承和使用多态提供了依据,也是实现开闭原则的重要保证。
举例:
白马 马也
乘白马 乘马也
黑马 马也
乘黑马 乘马也
乘的是马 什么颜色的马都是马
马 是父类 各种颜色的马是子类 要求的是父类,所有的子类对象都可以使用
娣 美人也 爱娣 非爱美人也
美人 父类 娣 子类 要求是子类的地方,父类不一定能够出现
父类作为方法参数和返回值,可以让参数更灵活
张三能开车
张三就能开奥迪
张三就能开奔驰
张三就能开宝马
图解:
1.4依赖倒置原则
解释
依赖倒置 DIP–Dependence Inversion Principle
高层模块不应该依赖底层模块,两者都应该依赖抽象,抽象不应该依赖细节,细节应该依赖抽象。
尽量依赖接口和抽象类,不要依赖具体的实现。
抽象:即是抽象类或者接口,两者是不能够实例化的。
细节:即是具体的嫌累,实现接口或者继承抽象类的类。
依赖正置就是类之间的依赖是实实在在的实现类之间的依赖,也就是面向实现编程。
依赖倒置就是通过抽象(抽象类或者接口),使各个模块之间实现彼此独立,不相互应影响。
依赖倒置有三种方式实现
1.构造函数传递依赖对象 。
2.setter方法实现。
3.接口声明实现依赖对象 ()匿名内部类。
依赖倒置原则要求
1.每个类尽量都有接口和抽象类,或者抽象类和接口都有。
2变量的表面类型应是接口和抽象类。
3任何类都不应该从具体派生(尽量避免继承,做二次开发时,无法获得高层代码时除外)。
4.尽量不要覆盖基类已经实现好的方法(尽量不要重写父类方法)。
好处:
采用依赖倒置原则可以降低模块之间的耦合性,提高系统的稳定性,减少并行开发的风险,提高代码的可读性和可维护性。
举例:
一个类组合另一个类作为属性时,应尽量选择抽象类或者是接口,尽量避免直接组合实现类。
电脑里的零件坏了,如硬盘,内存,CPU等,那么直接更换对应的配件,只要插槽一样就能互换,维护比较方便.插槽就是接口,具体的硬件就是实现类.留好接口可以随时换零件,如果零件直接焊死在电脑上,坏了就没有办法更换了。
图解:
抽象的Driver接口和抽象的Car接口 实现司机开车功能。
1.5接口隔离原则
解释
接口隔离原则 ISP Interface Segregation Principle
客户端不应该强行依赖他不需要的接口,类之间的依赖关系应该建立在最小的接口上,建立单一接口,不要建立庞大臃肿的接口,应该尽量细化接口,接口中的方法尽量少,也就是说要为各个类建立专门的接口,而不要试图去建立一个庞大的接口供所有的依赖它的类去调用。
接口隔离原则使用的一些规范
1.接口尽量小,尽量细致。
2.接口尽量高内聚,尽量和其他接口撇清关系。
3.定制服务,为调用者提供且之提供他需要的方法。
4.接口设计有限度,根据业务及经验,仔细四高筹划,适度隔离接口。
单一职责和接口隔离辨析
1.单一职责原则侧重职责,接口隔离侧重对接口的依赖的隔离。
2.单一职责原则侧重约束类,其次是接口,针对程序中实现的细节。
3.接口隔离原则侧重约束接口,主要针对抽象需求,针对程序的整体框架的构建。
好处:
防止庞大,臃肿的接口,避免接口污染,提高程序设计要求的细致划分性,降低大面积维护成本.一旦出现接口污染,会造成实现类中存在大量的不相关不需要去重写的方法。
举例:
演员和角色以及整部戏的关系。
图解:
不满足接口隔离原则的情况:
满足接口隔离原则的情况:
1.6迪米特法则
解释
迪米特法则 LOD–Law of Demeter
只与你只直接的朋友通信 ,不要和不相关的人产生大量通信,如果两个类不必彼此通信,那么两个类就不应到发生直接的相互作用,如果其中的一个类需要调用另一个类的方法的话,可以通过第三者转发这个调用。
迪米特法则的初衷是在于降低类之间的耦合.但是迪米特法则有可能造成一个后果就是程序中存在大量的中介类,这些中介类完全就是为了传递类间的相互调用关系,一定程度上增加了系统的复杂度。
好处:
降低了类和类之间的耦合。
举例:
找中介卖房子,不用我们自己直接接触大量的买房人员。
图解:
1.7合成/聚合复用原则
解释
合成/聚合复用原则 CARP Composite Aggregate Reuse Principle
当一个类想使用另一个类的功能时,优先使用对象的组合,而不是继承,尽量多使用组合。
合成聚合复用原则是值在一个新对象中通过组合关系使用原来已有的一些对象,使之成为新对象的一部分,通过使用已有对象的API完成已有功能的调用。
为什么要是用合成聚合,尽量不要使用继承?
1.继承破坏包装,把超类的实现细节直接暴露给子类,不利于信息的隐匿。
2.如果父类发生改变,会引发一系列子类的改变,类之间耦合度高。
3.继承是一种静态功能的使用,在运行的过程中不能发生改变,聚合复用可以动态传入子类对象实现功能动态改变。
好处:
非常有利于构建可维护,可复用,可扩展和灵活性好的软件系统。
举例:
汽车上如果想获得行车记录的功能,单独组合一个行车记录仪对象作为属性即可,没有必要继承行车记录仪类。
图解: