六大设计原则

设计原则

了解23种设计模式之前,先要了解6大设计原则
因为设计模式都是由这些设计原则引申出来的,6大设计原则是根本的东西
当然,如果想要直接了解设计模式当然也是可以的
但,多了解一下也不是坏处,大家自行选择

1.单一职责原则

单一职责原则(Single Responsibility Principle),简称SRP

定义

顾名思义,单一职责原则的定义就是:应该有且仅有一个原因引起类的变更
平常工作中,你可能会遇到有人问你 “你设计的类符合单一职责吗”
这时就可以根据单一职责原则的定义去进行排查

优势

优势也可以理解为使用单一职责模式的好处:

  1. 类的复杂性降低:每个类实现的是什么功能都很清晰明确
  2. 可读性提高:类的复杂性降低,可读性自然提高
  3. 可维护性提高:类更加清晰,自然就更容易维护
  4. 变更可能引起的风险降低:类在维护或是版本迭代中的变更修改是必不可少的,而只修改自己不对其他类有印象自然是好的

注意:单一职责原则总的来说还是规定了一个编写代码时的标准,采用类的职责来衡量类是否优良,可是还需要知道类的职责都是不能说死的,具体还是要根据项目来定义,根据环境来定义

适用范围

单一职责原则适用于类、接口同样也适用于方法
适用于方法上,就是指一个方法尽可能只做一件事
例如:有一个方法目的是去修改排序的方法,就不要把这个方法放到总的修改方法中

每个方法的职责清晰明确,不仅会让开发简单,日后的维护更改也会简单

建议

对于单一职责原则,建议是接口一定要做到单一职责,类的设计尽量做到只有一个原因能引起变化

2.里氏替换原则

里氏替换原则(LiskovSubstitution Principle),简称LSP
在我们适用java此类面向对象的语言时,使用其继承的特性是必不可少的,也是面向对象的三大特性之一,继承的优缺点简单的总结一下:
优点:

  1. 代码共享:子类都拥有父类的方法与属性
  2. 提高代码的复用性
  3. 子类可以重写父类方法,形似父类又异于父类
  4. 提高代码的可扩展性:实现了父类方法,其他就可以随意扩展了
  5. 提高项目的开放性

缺点:

  1. 继承是侵入性的:只要使用了就必须拥有父类的所有属性与方法
  2. 降低了代码的灵活性:因为只要继承了就拥有了父类的属性方法,多了约束
  3. 增加了耦合性:如果父类想要去修改,就需要考虑子类也会变更

能看到使用继承是有利有弊的,那怎样才可以在获得利的同时减少弊呢?
就需要引入里氏替换原则!

定义

  1. 如果对每一个类型为S的对象o1都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为都没有发生变化,那么类型S就是类型T的子类
  2. 所有引用基类的地方必须能够透明的使用其子类的对象

两种定义都是差不多的意思,通俗点说就是:只要父类能出现的地方子类就可以出现,且替换为子类都不会产生任何错误或异常
使用者可能根本就不需要知道是父类还是子类,但是反过来不行,有子类出现的地方,父类未必能适应

使用

里氏替换原则为继承做了进一步的约束

  1. 子类必须完全实现父类的方法:调用类直接传接口或抽象类,如果不能使用父类或接口则说明类的设计已经违背了里氏替换原则
  2. 子类可以有自己的个性:子类除了父类的方法和属性,当然也可以有独属于自己的方法和属性,这也是为什么子类出现的地方,父类不一定能胜任的原因
  3. 覆盖或实现父类的方法时输入参数范围可以更大:这时子类与父类方法名相同但方法输入参数不同,就不再是重写而是重载
  4. 重写或实现父类的方法时返回值范围可以缩小:子类相同方法的返回值要和父类相等或是父类返回值的子类

3.依赖倒置原则

依赖倒置原则(Dependence Inversion Principle),简称DIP

定义

  1. 高层模块不应该依赖于底层模块,两者都应该依赖其抽象
  2. 抽象不应该依赖于细节
  3. 细节应该依赖于抽象

其中底层模块和高层模块指:所有的逻辑的实现都是由一个个的原子逻辑组成的,而不可分割的原子逻辑就是底层模块,原子逻辑组合在一起就是高层模块
抽象和细节指:抽象就是指接口或抽象类,两者不能实例化;细节就是实现类,可以被实例化

所以在java语言中,依赖倒置原则就是:

  1. 模块间的以来通过抽象链接,实现类之间不存在直接的依赖关系,其依赖关系是通过接口或抽象类产生的
  2. 接口或抽象类不依赖于实现类
  3. 实现类依赖于接口或抽象类

是不是看着很熟悉!
因为这也就是我们经常所提到的“面向接口编程”的精髓

优点

采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性

依赖的写法

依赖是可以传递的,但只要做到抽象依赖,即使是多层依赖也没关系
以下说三种方式来传递依赖:

  1. 构造函数传递依赖对象
  2. Setter方法传递依赖对象
  3. 接口声明依赖对象

实际使用

在实际项目中使用时,遵循以下几点:

  1. 每个类尽量都有接口或抽象类,或者两个都具备
  2. 变量的表面类型尽量是接口或抽象类
  3. 任何类都不应该从具体实现类中派生(但也要根据实际情况来说,一般不超过两层继承都可以接受)
  4. 尽量不要覆写基类的方法
  5. 结合里氏替换原则使用

扩展

说了半天的依赖倒置原则,可能还是会不太理解倒置的含义,那么我们从正置来理解一下倒置
依赖正置:就是类间的依赖是实实在在的实现类间的依赖,也就是面向实现编程
依赖倒置:就是抽象间的依赖,代替了传统的事物间的依赖
就好比:依赖正置就像我今天要开车,我就对管家说我要开夏利,管家就帮我把夏利开过来;而依赖倒置就像,我对管家说我要开车,管家自己去根据我平时的需求来选车给我开出来只要是车就可以,然后就帮我把我的劳斯莱斯开来了

依赖倒置原则是六大设计原则中较难以实现的一个,但是非常重要,是实现开闭原则的重要途径,如果依赖倒置原则没有实现,开闭原则也就不要想了,要记住,“面向接口”就是依赖倒置原则的核心

4.接口隔离原则

定义

首先明确一下接口的定义:

  1. 实例接口:在java中声明了一个类,如Person ren = new Person(),这样用new关键字产生的一个实例,是对一类事物的描述,这是一种接口
  2. 类接口:就是java中经常所使用的interface关键字定义的接口

再说一下隔离的定义:

  1. 客户端不应该依赖他不需要的接口
  2. 类间的依赖关系应该建立在最小的接口上

总结来说就是:建立单一的接口,不要建立臃肿庞大的接口或是接口尽量细化,同时接口中的方法尽量少

这样看来是不是和单一职责原则有些类似,但其实并不相同,单一职责原则注重的是职责,是业务逻辑上的问题,而接口隔离原则也是要求接口中的方法尽量少

优点

接口是我们设计时对外提供的“契约”,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性

约束

接口隔离原则对于接口的约束有以下几个:

  1. 接口尽量小:根据接口隔离原则拆分接口时,首先满足单一职责原则
  2. 接口要高内聚:提高模块处理能力,减少对外的交互
  3. 定制服务:即为一个个体提供好的服务,只提供访问者需要的方法
  4. 接口设计是有限度的:接口设计的越细,结构就越负载,开发难度、不可维护性都会增加,所以也要掌握好“度”

实际使用

按照上面所说,贯彻使用接口隔离原则最好的方法就是一个接口一个方法,保证绝对符合接口隔离原则
但是我们在实际使用中绝不会这样使用,那么到底要怎么去使用呢
总的来说,只能通过每个人的实践、经验去帮助你更好的使用

5.迪米特法则

迪米特法则(Law of Demeter),简称LoD,也称最少知识法则

定义

一个对象应该对其他对象有最少的了解
通俗的说:一个类应该对自己需要耦合或者调用的类知道的最少,不需要知道你内部多少复杂的东西,只要知道你提供了多少public的方法我就调用这么多,其他的不关心

约束

迪米特法则对于类的低耦合提出了要求,有以下几点:

  1. 只和朋友交流:两个对象之间的耦合就称为朋友关系,朋友类指出现在成员变量、方法输入输出参数中的类为朋友类,方法体内部的类不属于朋友类

特别不要出现getA().getB().getC()...这种情况,类与类之间的关系是建立在类间的,而不是在方法间的

  1. 朋友间也是有距离的:就和我们生活中类似,距离产生美,迪米特法则就对这个距离进行了描述,朋友类间也不能无话不说、无所不知;一个类公开的public属性、方法越多,变更引起的风险就越大,迪米特法则就要求类间“羞涩”一些,尽量不要太开放,少一些public
  2. 是自己的就是自己的:如果一个方法方在本类中,既不增加类间关系,也不产生负面影响,那就可以放置在类里
  3. 谨慎使用Serializable

实际使用

迪米特法则的核心观点就是类间解耦,弱耦合,之后类的复用率才会上升,但是这样的结果就会导致系统更加的复杂,维护也带来了难度,所以还是要看使用的环境,既要做到让结构清晰,又能做到高内聚低耦合

6.开闭原则

定义

开闭原则是java里最基础的设计原则,他告诉我们如何构建一个稳定、灵活、易扩展的系统
定义:一个软件实体如类、模块、函数应该对扩展开放,对修改关闭
其意思是:一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化

开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,底层模块的变更,必然会与高层模块进行耦合,否则就是一个孤立无援的代码块

而变化可以分为三类:

  1. 逻辑变化:只变化一个逻辑,而不涉及其他模块,可以直接修改类中方法,前提是所有关联类都做相同逻辑处理
  2. 子模块变化:一个模块变化会对其他模块产生影响,特别是低层次模块必然会引起高层次模块变化,因此在扩展发生变化时,高层次模块的引入模块必然也会发生变化
  3. 可见视图变化

采用开闭原则的原因

只要是面向对象的语言都会涉及开闭原则,开闭原则也是最基础的一个原则,可以说前边几个原则都是开闭原则的具体形态

  1. 开闭原则对测试的影响:扩展对于测试的优势不言而喻,新增加的类,新增加的测试方法
  2. 开闭原则可以提高复用性:所有逻辑都是组合而来,而不是一个类中独立实现一个逻辑,这样代码才可以复用,粒度越小,复用的可能性就越大
  3. 开闭原则可以提高可维护性
  4. 面向对象开发的要求:我们在设计一个系统时,可能在初设计时就考虑到所有可能变化的因素,留下接口,等待实现

如何使用开闭原则

  1. 抽象约束:通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放,通过抽象约束,对扩展进行了边界限定,不允许出现接口或抽象类中不存在的public方法;参数类型、引用对象尽量使用接口或抽象类而不是实现类
  2. 元数据控制模块行为:元数据(就是描述环境和数据的数据,例如配置参数),例如使用框架时,在框架的配置文件中我们进行的配置,这时需要完成一个新的业务,我们通过扩展一个子类修改配置文件,就完成了业务变化
  3. 制定项目章程
  4. 封装变化:将相同的变化封装到一个接口或抽象类中;将不同的变化封装到不同的接口或抽象类中

总结

将上述这六大原则结合使用,就可以建立稳定、灵活、健壮的设计

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值