设计模式之六大设计原则
1. 单一职责原则(Single Responsibility Principle,SRP)
定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
类和接口职责单一
实例:
IPhone 这个接口可不是只有一个职责,它是由两个职责:一个是协议管理,一个是数据传送,diag()
和 huangup()两个方法实现的是协议管理,拨号接通和关闭;chat()和 answer()是数据的传送,把我们说
的话转换成模拟信号或者是数字信号传递到对方,然后再把对方传递过来的信号还原成我们听的懂人话。
2. 里氏替换原则(Liskov Substitution Principle,LSP)
定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
定义2:所有引用基类的地方必须能透明地使用其子类的对象。
问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
解决方案:当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。
里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
3. 依赖倒置原则(Dependence Inversion Principle,DIP)
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。
抽象不应该依赖于具体,具体应该依赖于抽象。
依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
4. 接口隔离原则(Interface Segregation Principle,ISP)
接口分为两种:一种是实例接口(Object Interface),一种是类接口(Class Interface)
第一种定义: Clients should not be forced to depend upon interfaces that they don't use.
客户端不应该依赖它不需用的接口。
第二种定义:The dependency of one class to another one should depend on the smallest possible interface。
类间的依赖关系应该建立在最小的接口上。
接口细化,接口纯洁
我们可以把这两个定义概括为一句话:建立单一接口,不要建立臃肿庞大的接口。再通俗的一点讲:接口尽量细化,同时接口中的方法尽量的少。
把一个臃肿的接口变更为两个独立的接口依赖的原则就是接口隔离原则
接口隔离原则是对接口进行规范约束,其包含以下四层含义:
接口尽量要小,根据接口隔离原则拆分接口时,必须首先满足单一职责原则。
接口要高内聚。
定制服务。
接口设计是有限度的。
5. 迪米特法则(Law of Demeter,LoD)
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD.
一个对象应该对其他对象有最少的了解,通俗的讲一个类对自己需要耦合或者调用的类应该知道的最少,你类内部是怎么复杂、怎么的纠缠不清都和我没关系,那是你的类内部的事情,我就知道你提供的这么多public 方法,我就调用这个;
迪米特法则包含以下四层意思:
只和朋友交流。Only talk to your immedate friends
出现在成员变量、方法的输入输出参数中的类被称为成员朋友类
朋友间也是有距离的。
因此为了保持朋友类间的距离,你需要做的是:减少 public 方法,多使用 private、package-private(这个就是包类型,在类、方法、变量前不加访问权限,则默认为包类型)protected 等访问权限,减少非 static 的 public 属性,如果成员变量或方法能加上 final 关键字就加上,不要让外部去改变它。
是自己的就是自己的。在项目中有一些方法,放在本类中也可以,放在其他类中也没有错误,那怎么去衡量呢?你可以坚持这样一个原则:如果一个方法放在本类中,即不增加类间关系,也对本类不产生负面影响,就放置在本类中。
谨慎使用 Serializable。实话说,这个问题会很少出现的,即使出现也会马上发现问题。
迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高,其要求的结果就是产生了大量的中转或跳转类,类只能和朋友交流,朋友少了你业务跑不起来,朋友多了,你项目管理就复杂,大家在使用的时候做相互权衡吧。
不知道大家有没有听过这样一个理论:“任何 2 个素不相识的人中间最多只隔着 6 个人,即只用 6 个人就可以将他们联系在一起”,这个理论的学名叫做“六度分离”,应用到我们项目中就是说我和我要调用的类之间最多有 6 次传递,呵呵,这只能让大家当个乐子来看,在实际项目中你跳两次才访问到一个类估计你就会想办法了,这也是合理的,迪米特法则要求我们类间解耦,但是解耦是有限度的,除非是计算机的最小符号二进制的 0 和 1,那才是完全解耦,我们在实际的项目中时,需要适度的考虑这个法则,别为了套用法则而做项目,法则只是一个参考,你跳出了这个法则,也不会有人判你刑,项目也未必会失败,这就
需要大家使用的是考虑如何度量法则了。
6. 开闭原则(Open Closed Principle,OCP)
一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。