总结6大设计原则
看了设计模式这本书,简单的记录一下吧!
1.单一职责原则(Single Responsibility Principle 简称:SRP):应该有且仅有一个原因引起类的变更。
举个简单的例子,比如说我们要对用户进行操作,按照面向接口的思想,可以让IUserA接口来负责用户的属性,IUserB来负责用户的行为,而我们只需要实现子接口IUserInfo就可以了。要获得用户信息时就当做IUserA的实现类,要是希望维护用户的信息就当做IUserB的实现类就行了。
单一职责原则要求每个接口职责分明、结构清晰;这样一来,类的复杂性和接口变更引起的风险就降低了,也提高了可读性,更容易维护了。
单一职责不仅适用与接口和类,也可以体现在方法里面,一个方法尽可能的做一件事情,当然我们实际开发中也不会将方法的颗粒度分得很细。
2.里氏替换原则(Liskov Substitution Principle 简称:LSP):所有引用基类的地方必须能透明的使用其子类对象。
假设有这样一个场景:战争中士兵们都需要武器上阵杀敌,那我们就定义War为战争类,Solider为士兵类,AbstractGun为武器的抽象类,然后又分别有手枪类、步枪类和机枪类来继承武器类,关系结构如下图所示:
枪的主要职责是射击,如何射击在各个具体的子类中定义。在士兵类中定义了一个方法killEnemy(),使用武器来杀敌,那么具体使用什么武器,我们在调用的时候就可以通过setGun(AbstractGun gun)方法来设置具体是什么枪了。可以看到在设置士兵使用什么武器的时候,我们并没有设置参数类型为具体的哪一种枪,只要是武器都可以上阵杀敌就行了。总结一下就是,在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。
有一种情况:如果是仿真枪呢,它并不是真枪实弹,也就不能实现父类的shoot()方法完成杀敌操作了,那怎么办呢?
一种就是在Solider类中增加instanceof来判断是不是仿真枪玩具枪之类的,但是在程序中每增加一个类,所有与这个父类有关系的类都必须加判断,很显然是不可取的。那我们就可以让仿真枪脱离继承,建立一个独立的父类与AbstractGun建立关联委托关系,将形状、声音都委托给AbstractGun处理:
总结一下就是:如果子类不能完整的实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子关系,采用依赖、聚合、组合等关联关系代替继承。
3.依赖倒置(Dependence Inversion Principle 简称: DIP):
依赖倒置原则的原始定义包含三层:
1.高层模块不应该依赖低层模块,两者都应该依赖其抽象;
2.抽象不应该依赖细节;
3.细节应该依赖抽象。
高层模块和低层容易理解,每个逻辑的实现类都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。在java语言中,抽象就是指接口和抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以加上一个关键字new产生一个对象。简而言之,就是面向接口编程,模块间的依赖通过抽象发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的,接口或抽象类不依赖于实现类,而实现类依赖于接口或抽象类。
在java中,只要定义变量就必然要有类型,一个变量的类型可以有两种类型:表面类型和实际类型,表面类型是在定义 的时候赋予的类型,实际类型是对象的类型。
依赖的三种写法:
1.构造函数传递依赖对象,在类中通过构造函数声明依赖对象。
2.setter方法传递依赖对象,在抽象中设置serter方法声明依赖关系。
3.接口声明依赖对象。
依赖倒置原则的本质就是通过抽象(接口或实现类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。依赖倒置原则的优点在小型的项目中很难体现出来,其实只要记住“面向接口编程”就基本上抓住了依赖倒置原则的核心。依赖倒置原则是6个设计原则中最难实现的,它是开闭原则的重要途径。
4.接口隔离(Interface Segreation Principle):建立单一接口,不要建立臃肿庞大的接口。
接口隔离原则和单一职责原则的审视角度是不相同的,单一职责要求的是类和接口的职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少。比如把一个臃肿的接口变更为两个独立的接口就可以体现为接口隔离原则。通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。
使用接口隔离原则拆分接口时,首先必须满足单一职责原则:
1.接口要求高内聚:提高接口、类、模块的处理能力,减少对外的交互,要求在接口中尽量少公布public方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。
2.定制服务:单独为一个个体提供优质的服务。
3.接口设计限度控制:接口的设计粒度越小,系统越灵活,但是灵活的同时也带来了结构的复杂化,导致开发难度增加,可维护性降低。所以接口设计一定要注意适度,这也是根据自己的设计经验和常识来判断的。
5.迪米特法则(Law of Demeter, LOD)也称为最少知识原则(Least Knowledge Principle, LKP):一个对象应该对其他对象有最少的了解。
朋友类:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法内部的类不属于朋友类。
类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不要引入一个类中不存在的对象,当然JDK API提供的类除外。一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此为了保持朋友类间的距离,在设计时需要反复衡量:是否还可以再减少public属性或方法,是否可以加上final关键字等。迪米特法则要求,尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private、package-private(包类型,在类、方法、变量前不加访问权限,则默认为包类型)、protected等访问权限。
迪米特法则的核心就是类间的解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。
6.开闭原则(Open Closed Principle):一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
前5个原则都是指导设计的工具和方法,是开闭原则的具体形态,而开闭原则才是其精神领袖。
什么是开闭原则:一个软件实体应该通过扩展来其行为来实现变化,而不是通过修改已有的代码来实现变化。举个例子吧,比如说想要在一个原有的接口基础上,添加新的业务逻辑,那么我们不能去更改这个接口给它加一个新的方法,会影响到现有的实现类,这时候我们就可以新建一个类去继承接口的某一个实现类,并重写相关业务方法处理新的业务逻辑。
采用开闭原则的好处:
1.开闭原则对测试的影响:比如说我们在系统中开发了一个新的功能,那我们测试这个功能时需要重新写一个测试这个功能的方法或类,而不是在原有其他功能的测试方法或类上面修改。
2.开闭原则可以提高复用性:在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。只有这样代码才可以复用,粒度越小,被复用的可能性就越大。
3.开闭原则可以提高可维护性:当我们维护代码时,最乐意做的是扩展一个类,而不是修改一个类,关键是那还不是你自己写的,想想都很痛苦。
如何使用开闭原则:
1.对象约束:第一,通过接口或抽象类约束扩展,对扩展的边界限定,不允许出现在接口或抽象类中不存在的public方法;第二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;第三,抽象层尽量保持稳定,一旦确定即不允许修改。
2.元数据控制模块行为:元数据就是用来描述环境和数据的数据,就好比我们配置文件中配置的参数。我觉得使用最多的就是spring了,因为spring的配置文件真的很多(但是现在普遍使用springboot了)。
3.制定项目章程:每个公司都会有自己的代码开发规范。
4.封装变化:第一,将相同的变化封装到一个接口或抽象类中;第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。
综上所述,原则只是提供参考,如果违背了这些原则,项目也未必会是失败,这就需要大家在采用原则时反复度量,不遵循是不对的,但也不要为了套用原则而做项目。
可能理解不深刻,望指正!!