- 单一职责原则(Single Responsibility Principle)
- 开闭原则(Open Closed Principle)
- 里氏替换原则(Liskov Substitution Principle)
- 迪米特法则(Law of Demeter),又叫“最少知道法则”
- 接口隔离原则(Interface Segregation Principle)
- 依赖倒置原则(Dependence Inversion Principle)
把这 6 个原则的首字母(里氏替换原则和迪米特法则的首字母重复,只取一个)联合起来就是:SOLID(稳定的),其代表的含义也就是把这 6 个原则结合使用的好处:建立稳定、灵活、健壮的设计。
单一职责
单一职责原则要求一个接口或一个类只能有一个原因引起变化,也就是一个接口或者类只能有一个职责,只负责一件事情。
优势:
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性高,复杂性降低,可读性自然就提高了;
- 可维护性提高,可读性提高了,那自然更容易维护了;
- 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
里氏替换原则
在面向对象的语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点:
- 代码共享,减少创建类的工作量,每个子类都拥有父类的属性和方法;
- 提高代码的重用性;
- 子类可以形似父类,但又异于父类;
- 提高代码的可扩展性;
- 提高产品或项目的开放性。
有优点就必然存在缺点:
- 继承是侵入性的。只要继承,就必须拥有父类的属性和方法。
- 降低代码的灵活性。子类会多一些父类的约束。
- 增强了耦合性。当父类的常量、变量、方法被修改时,需要考虑子类的修改。
为了让“利”的因素发挥最大的作用,同时减少“弊”带来的麻烦,引入了里氏替换原则(LSP),即只要父类能出现的地方,子类就可以出现,而且替换为子类也不会产生任何错误或异常。一句简单的定义包含了4层含义:
- 子类必须完全实现父类的方法
- 子类可以有自己的个性
- 覆盖或实现父类的方法时,输入参数可以被放大
- 覆盖或实现父类的方法时,输出结果可以被缩小
依赖倒置原则
- 模块间的依赖通过抽象发生,实现类之间不直接发生依赖关系,其依赖关系是通过接口或抽象类产生的
- 接口或抽象类不依赖于实现类
- 实现类依赖接口或抽象类
优点:
依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性
接口隔离原则
接口隔离原则就是客户端不应该依赖它不需要的接口,或者说类间的依赖关系应该建立在最小的接口上,接口隔离原则是对接口进行规范约束。
其实通俗来理解就是,不要在一个接口里面放很多的方法,这样会显得这个类很臃肿不堪。接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加轻便灵活。或许看到接口隔离原则这样的定义很多人会觉得和单一职责原则很像,但是这两个原则还是有着很鲜明的区别。接口隔离原则和单一职责原则的审视角度是不同的,单一职责原则要求类和接口职责单一,注重的是职责,是业务逻辑上的划分,而接口隔离原则要求方法要尽可能的少,是在接口设计上的考虑。例如一个接口的职责包含10个方法,这10个方法都放在一个接口中,并且提供给多个模块访问,各个模块按照规定的权限来访问,并规定了“不使用的方法不能访问”,这样的设计是不符合接口隔离原则的,接口隔离原则要求“尽量使用多个专门的接口”,这里专门的接口就是指提供给每个模块的都应该是单一接口(即每一个模块对应一个接口),而不是建立一个庞大臃肿的接口来容纳所有的客户端访问。
使用原则
- 根据接口隔离原则拆分接口时,首先必须满足单一职责原则: 没有哪个设计可以十全十美的考虑到所有的设计原则,有些设计原则之间就可能出现冲突,就如同单一职责原则和接口隔离原则,一个考虑的是接口的职责的单一性,一个考虑的是方法设计的专业性(尽可能的少),必然是会出现冲突。在出现冲突时,尽量以单一职责为主,当然这也要考虑具体的情况。
- 提高高内聚: 提高接口,类,模块的处理能力,减少对外的交互。比如你给杀手提交了一个订单,要求他在一周之内杀一个人,一周后杀手完成了任务,这种不讲条件完成任务的表现就是高内聚。具体来说就是:要求在接口中尽量少公布public方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险就越小,也有利于降低成本。
- 定制服务: 单独为一个个体提供优良服务(只提供访问者需要的方法)。
- 接口设计要有限度: 根据经验判断
迪米特法则
迪米特法则(LoD)也叫最少知道法则:一个对象应该对其他对象有最少的了解。
- 只和朋友交流:类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象
- 朋友间也是有距离的:一个类把太多方法暴露给另一个类,两者的朋友关系太亲密了,耦合关系变的异常牢固,如果更改聊其中一个方法,则要同时修改另一个类,增加了风险。因此,这种耦合是不合适的,我们需要对其优化。一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。所以,我们开发中尽量不要对外公布太多public方法和非静态的public变量,尽量内敛
- 是自己的就是自己的:在实际开发中经常会出现这样一种情况:一个方法放在本类中也可以,放在其他类中也没有错。那这时,我们只需要坚持一个原则:如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。
开闭原则
开闭原则是指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。也就是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。
为什么要用开闭原则
- 开闭原则非常著名,只要是做面向对象编程的,在开发时都会提及开闭原则。
- 开闭原则是最基础的一个原则,前面介绍的5个原则都是开闭原则的具体形态,而开闭原则才是其精神领袖。
- 开闭原则提高了复用性,以及可维护性。
总结:
总结六大设计原则
单一职责原则:一个类或接口只承担一个职责。
里氏替换原则:在继承类时,务必重写(override)父类中所有的方法,尤其需要注意父类的protected方法(它们往往是让你重写的),子类尽量不要暴露自己的public方法供外界调用。
依赖倒置原则:高层模块不应该依赖于低层模块,而应该依赖于抽象。抽象不应依赖于细节,细节应依赖于抽象。
接口隔离原则:不要对外暴露没有实际意义的接口。
迪米特法则:尽量减少对象之间的交互,从而减小类之间的耦合。
开闭原则:对软件实体的改动,最好用扩展而非修改的方式。