设计模式——六大原则

1.总原则-开闭原则

定义
对扩展开放,对修改封闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔( 即带电插拔,指的是在不关闭系统电源的情况下,将模块、板卡插入或拔出系统而不影响系统的正常工作)的效果。意思就是当程序需要变化时,通过继承原有类进行重写或者新增完成功能拓展

开闭原则是其他五种原则的抽象,其他五种原则是围绕开闭原则的具体设计

想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。

2、单一职责原则

定义:

一个方法/类应只负责一个功能,包含多个不同功能的类/方法会使得该方法/类过于臃肿,职责划分不清淅

那么如何区别职责是否单一

举个例子:我们现在模拟一个手机的类。我们手机最核心的功能为打电话,所以我们需要将打电话的几个步骤分为若干方法——拨号、通话以及挂断。其中拨号和挂断属于连接协议的管理,而通话属于数据的传输,这两者的职责是不同的。其中连接协议的变化会引起手机类的变化而不会引起通话的变化,所以这两者需要分离接口。

注意:上述例子只是做一个举例的作用,而不是标准,关于区分职责是否单一其实还是要根据你所站的角度去看,根据实际的开发环境去进行职责分离。因为过度追求职责的单一会将过于细化程序的粒度,产生过多的类,反而增加了程序的复杂性。所以适度即可,这个度需要自己把握。不够尽量保证一个前提,一个类一个方法只做一件事,方法需要在类粒度上再进行细化

单一职责的好处

  • 降低了类的复杂度,因为对职责进行了分离
  • 提升了可读性,因为复杂度下降
  • 提升了可维护性
  • 降低了变更引起的风险,一个接口的变化只会引起该接口对应类的变化,其他职责的接口和类并不会发生变化

3、里氏替换原则(Liskov Substitution Principle)

定义

任何父类/接口可以出现的地方,子类一定可以出现。且子类的出现不会引起程序的变化(这种比变化是指逻辑上的,而非运行结果上的),也就是说,变化后,除了替换的部分,其他部分不需要发生修改。

里氏替换原则有下面四层含义

  • 子类必须完全实现父类的方法,如果不能完整地实现父类的方法,或者父类的某系方法再子类中发生畸变,则建议断开父子继承关系,采用依赖、集合、组合等关系代替继承
  • 子类可以有自己的个性(新方法、变量的拓展),但应当避免个性。里氏替换原则希望子类是父类的增强,而不具有过多的个性,因为子类具有个性后,在使用上我们很多地方或需要依赖于子类而非父类(这里的个性指在父类上拓展的一些方法/成员变量)
  • 如果重载父类方法,则需要放大方法的输入参数
  • 如果重载父类方法、则需要缩小方法的输出参数

里氏替换原则的目的

  • 增强程序的健壮性
  • 版本升级时保证兼容性,即增加了子类,原有的类还能保持运行
  • 通过将父类作为参数传递不同的实现子类可以完成不同的业务逻辑

注意

里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。

4、依赖倒转原则(Dependence Inversion Principle)

定义

  • 高层次模块不应该依赖于低层次模块,两者都应该依赖于抽象
  • 抽象不应该依赖于细节
  • 细节应该依赖于抽象

依赖倒置在java中的体现

  • 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系通过接口/抽象类产生
  • 抽象类/接口不应该依赖于实现类
  • 实现类依赖接口/抽象类

简单来说就是面向接口编程

依赖倒置的好处

  • 减少类之间的耦合
  • 提升系统的稳定性
  • 降低并行开发引起的风险
  • 提高代码可读性和可维护性

业务需求变更永无休止,技术前进就永无止境,在发生变更时才能发觉我们的设计/程序是否松耦合

实际操作中如何遵守这条原则

  • 每个类尽量都有接口或抽象类,或者抽象类与接口两者都有
  • 变量的表面类型尽量是抽象类(当然不绝对,比如我们如果需要调用clone方法,那就必须继承Object,那自然不可能是接口/抽象类)
  • 任何类都不应该从具体类中派生(这也不绝对,我们开发的时候应当遵守,但是如果我们是二次开发,我们重新继承接口去实现类的成本太高那就不值当了)
  • 尽量不要覆盖基类的方法
  • 结合里氏替换一起使用

注意

依赖倒置不是真理,因为有时候场景的不同导致我们必须要依赖于具体细节而不是抽象。引用设计模式之禅的例子,杀人偿命,这里法律,但其中的杀人属于我们认为的抽象,因为杀人涉及到怎么杀,处于什么理由杀等等。如果我们归于追求杀人偿命这一准则,那么意味着我们正方防卫杀人、战场上杀敌也都是要偿命。这并不合理。所以我们在实际项目中使用依赖转置时当审时度势,不要为了遵守准则而放弃最终目标。要灵活使用。

5、接口隔离原则(Interface Segregation Principle)

定义

  • 每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
  • 即一个类对另一个类的依赖应当保证最小接口的形式。

总结

建立单一接口,也就是尽量细化接口,减少接口中的方法数量,我们会发现这条准则与单一职责有些相似,但是这两条所站的角度不同。一个站在指责上看,一个站在是否必须使用去看待。举个例子,接口中存在多个操作方法,这几种方法属于一个职责,但是根据权限,我们提供给不同的模块不同的访问接口。从单一职责上看这个是合理的,但从接口隔离上来说,使用者可以使用到他不该或者不需要用到的接口,这不合理。

实践中如何保证接口隔离

  • 接口尽量小(适当),接口隔离原则进行拆分时首先满足单一职责
  • 接口要高内聚:只把外界需要的接口进行公布,接口是对外界的承诺,承诺越少风险越少。
  • 定制服务:对于一个目标,我们可以根据目标去指定对方应有的接口
  • 一个接口只服务于一个子模块或业务逻辑
  • 接口隔离虽可以为我们开发带来更高的灵活性,但一定要注意我们接口的粒度不要过细,不然你会被海洋一般的接口给淹没

6、迪米特法则(最少知道原则)(Demeter Principle)

定义

一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

迪米特法则对类之间耦合的要求

  • 只与直接朋友交流:
    (类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中)类与类之间的关系应该建立在类之间,而不是方法之间
  • 朋友之间也是有距离的:
    一个类对另一个类应该不要暴露太多接口,比如A需要按顺序执行B的几个方法,只是既定的逻辑,那我们就可以把执行的顺序封装在B内的某个public方法中,私有化其他具体的执行步骤,这样当B中某个步骤需要修改时,修改的风险不会被扩散到A类中
  • 是自己的就是自己的:
    实际开发中经常会出现一个方法放在本类中也可,放在其他类也可。那我们坚持这么一个原则:如果一个方法放在本类中既不增加类之间的关系也不增加负面影响,那就放本类中就好了

最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原来是肖某人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值