六大设计原则

六大设计原则

  • 单一职责原则
  • 里式替换原则
  • 依赖倒置原则
  • 接口隔离原则
  • 迪米特法则
  • 开闭原则


    一、单一职责原则(SRP)

    1. 单一职责原则:英文名为(Single Responsibility Principle),简称 SRP。

    2. SRP 的定义是:应该有且仅有一个原因引起类的变更。

    3. SRP 的好处:
      ①. 类的复杂性降低,实现什么职责都有清晰明确的定义。
      ②. 可读性提高,复杂性降低。
      ③. 可维护性提高。
      ④. 变更引起的风险降低,变更必不可少,如果接口的单一职责做的好,一个接口修改只对响应的实现类有影响,对其他的接口无影响,这对系统的扩展性,可维护性都有很大帮助。

    4. SRP的缺点:
        SRP 最难的就是职责划分。一个职责一接口,但没有划分职责的标准;需要因项目而异,因环境而异。

    5. 最佳实战:
        对于单一职责原则,我的建议是接口一定要做到单一职责原则,类的设计尽量做到只有一个原因引起其变化。

    二、里式替换原则(LSP)

    1. LSP 主要解决继承带来的问题。
      继承的优点:
        a. 代码共享,提高代码的重用性,
        b. 提高代码的可扩展性,君不见很多开源框架的扩展接口都是通过继承父类完成的。
        c. 提高项目或产品的开放性。
      继承的缺点:
        a. 继承是入侵性的。
        b. 降低代码的灵活性。
        c. 增强代码的耦合性。

    2. LSP 的定义(有两种定义方式):
      第一种:在使用 T的对象的程序中,将类型T 的对象换成类型S 的对象后程序不发生变化,说明 S对象可以完成 T对象的职责,则类型S 是类型T 的子类型。
      第二种:所有引用基类的地方必须能透明的使用其子类对象。通俗的说:父类出现的地方子类就能出现,不会出现任何错误和异常,使用者不需要知道是父类还是子类。但是,有子类出现的地方,父类就不一定可以出现。

    3. LSP 为继承定义了一个规范,该规范有4层含义:

      ①. 子类必须完全实现父类的方法
        a. 在类中调用其他类时务必要试用其父类或接口,否则会违反 LSP原则。
        b. 如果子类不能完整的实现父类方法,或者父类的某些方法功能在子类中发生“畸变”,则建议断开父子关系,采用依赖、聚集、组合等关系代替继承。

      ②. 子类可以有自己的个性
        子类出现的地方父类不一定可以出现,子类可以完成的功能父类不一定可以完成。

      ③. 覆盖或实现父类方法时输入参数可以放大
        a. 当子类方法的输入参数类型比父类的大,这会与继承到的父类方法构成重载(Overload),而不是覆写(Override)!子类代替父类传递到调用者中,子类的方法永远都不会执行。。
        b. 如果想让子类的方法执行,就必须覆写父类的方法,此时子类方法的输入参数类型要和父类一致。

      ④. 覆写或实现父类的方法时输出结果可以被缩小
        a. 如果是覆写,父类和子类的两个同名方法的输入参数类型一定是相同的,子类方法的范围值小于等于父类,这整是覆写的要求,子类覆写父类的方法,天经地义。
        b. 如果是重载,则要求方法的输入参数类型或数量不同,在 LSP 下,子类的方法输入参数大于等于父类的方法输入参数,子类的这个方法不会被调用。

    4. 目的:LSP 的目的就是增强程序的健壮性,升级版本时也可以保存良好的兼容性。既是增加子类,原有的子类还可以运行。实际项目中,每个子类对应不同的业务含义,传递不同子类完成不同的业务逻辑。

    三、依赖倒置原则(DIP)

    1. DIP 原始定义有三层含义:
      ①. 高层模块不应该依赖于低层模块,两者都应该依赖其抽象。
      ②. 抽象不应该依赖细节。
      ③. 细节应该依赖抽象。
        每一个逻辑的实现都是有原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑在组装就是高层模块。Java中,抽象指接口或抽象类,两者都不能直接被实例化;细节就是实现类,实现接口或抽象类产生的类就是细节,细节可以被实例化,即 new一个对象。DIP 在Java中的表现是:
      ①. 模块间的依赖通过抽象产生,实现类之间不直接发生依赖关系。
      ②. 接口或抽象类不依赖实现类。
      ③. 实现类应该依赖接口或实现类。
      更加精简的定义就是:”面向接口编程”。

    2. 采用 DIP 可以减少类间的耦合性,提高系统的稳定性,降低并行开发带来的风险,提高代码的可读性和可维护性。

    3. 依赖的三种传递方法
      ①. 构造函数传递依赖对象。
      ②. Setter 方法传递依赖对象。
      ③. 接口声明依赖对象:
         例如:ICar car = new Car();

    4. 最佳实践
        依赖倒置原则的本质就是通过抽象使各个类或模块的实现彼此独立,不相互影响,实现模块的松耦合。使用该规则要遵循以下几点:
      a. 每个类都尽量有接口或抽象类,或者两者都具备。
        这是 DIP 的基本要求。

      b. 变量的表面类型尽量是接口或抽象类。

      c. 任何类都不应该从具体类中派生。(除非不了解父类或无法获得父类代码,并且继承不要超过两层)

      d. 尽量不要覆写基类的方法
        如果基类是一个抽象类,而且个别方法已经实现了,子类尽量不要覆写父类以实现的方法。类间依赖的是抽象。

      e. 结合 里氏替换原则使用
        我们可以得到这样一个结论:接口负责定义 public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确实现业务逻辑,同时在适当的时候对父类进行细化。

    5. 依赖倒置原则是六大原则中最难实现的原则,它是实现开闭原则的重要途径,依赖倒置原则没有实现,就别想实现对扩展开放,对修改关闭。依赖倒置原则的核心就是:面向接口编程。

    四、接口隔离原则

    1. a. 首先明确一下接口隔离原则的主角 — 接口,接口分两种:
        实例接口:指的是用来 new对象的类。
        类接口:指用 interface 定义的接口。
      b. 再明确一下此处的隔离:
        接口尽量细化,同时接口中的方法应该尽量少。这与单一职责原则有点像,但审视的角度不同,单一职责原则要求接口和类职责单一,蜘蛛职责,这是业务逻辑上的划分;而接口隔离原则要求接口的方法尽量少。

    2. 接口隔离原则是对接口的约束,包含四层含义:
      ①. 接口要尽量小
      这是接口隔离原则的核心,首先这个“小”就不能违背单一职责原则,即根据接口隔离原则拆分臃肿接口时,不能违反单一职责原则。

      ②. 接口要高内聚
      高内聚就是提高接口、类、模块的处理能力,减少对外的交互。具体到接口隔离原则就是:要求接口中尽量少的想外公布 public方法,接口是对外的承诺,承诺越少,对开发越有利。

      ③. 定制服务
      只提供访问者需要的方法,不要提供多余的方法。

      ④. 接口的设计是有限度的
      接口的设计粒度需要根据常识和经验判断,没有一个固化或可测量的标准。

    3. 最佳实践
        接口隔离原则是对接口的定义,同时也是对类的定义,接口和类尽量使用原子接口或原子类来组装。可以根据以下几个规则来衡量一个原子:
      ①. 一个接口只服务与一个子模块或业务逻辑。
      ②. 通过业务逻辑去压缩接口中的 public 方法,接口时常去回顾。
      ③. 已经被污染的接口,尽量去修改,若变更风险太大,则采用适配器模式进行转化处理。
      ④. 了解环境,拒绝盲从。根据具体的项目区拆分设计接口。

    五、迪米特法则

    1. 定义:迪米特法则(LOD)也称最少知识原则(LKP),描述了一个规则:一个对象应该对其他对象有最少的了解。
      通俗的说,一个类应该对自己需要耦合或调用的类知道的最少,只需要知道提供的 public 方法就好。

    2. 迪米特对类的低耦合提供了明确的要求,包含 4层原则。
      ①. 只和朋友类交流。
        什么叫朋友?两个对象之间的耦合就称为朋友关系,这种关系的类型有很多,例如组合,聚合,依赖等。
        什么是朋友类?出现在成员变量,方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不是朋友类。

      ②. 朋友间是有距离的。
        迪米特法则要求类尽量“羞涩”一点,不要对外公布太多的 public方法和非静态的变量,尽量内敛,多使用 private、default、protected等访问权限。

      ③. 是自己的就是自己的。
        如果一个方法放在本类中既不增加类间关系,也不对本类产生负面影响,则就放在本类中。

      ④. 谨慎使用 Serializable
        客户端对 VO的改变,服务器端应及时作出相应的改变。

    3. 最佳实战
        迪米特法则的核心就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。其产生的结果就是大量的产生中转类或调转类,导致系统的复杂性
        提高,如果一个类要跳转两次才能访问到另一个类,则就要进行重构了。因此使用迪米特法则进行类间解耦要反复衡量,既做到结构清晰,又高内聚低耦合。

    六、开闭原则

    1. 定义:一个软件如实体、模块和函数应该对扩展开放,对修改关闭。接下来看看什么是开闭原则,为什么要用开闭原则,怎么使用开闭原则。

    2. 开闭原则的真面目
      2.1 开闭原则定义说:软件应该对扩展开放,对修改关闭,其含义就是软件实体应该通过扩展来实现变化,而不是通过修改已有代码来实现变化。
      什么是软件实体呢?软件实体包括以下三个部分:
       – 项目或产品中按照一定逻辑划分的模块。
       – 抽象(抽象类或接口)和类。
       – 方法。
      开闭原则实质上是为软件实体的未来事件而制定的对现行开发设计进行约束的一个原则。
      2.2 一般处理一个需求变更有三种方式:
      ①. 修改接口。
        作为项目最初设计的接口,不应该经常发生变化,否责接口作为契约的作用就失去了效用。否定!

      ②. 修改实现类:
        使用该方案的前提条件是:所有的依赖或关联类都按照相同的逻辑处理。否则修改实现类不是最好的办法。

      ③. 通过扩展实现类来实现变化
        这样不修改原来的代码,能够完成业务变化对系统的最小化开发。
        开闭原则对扩展开放,对修改关闭,这并不意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合。
      2.3 我们再将变化归纳为三种类型:
      ①. 逻辑变化
        只变化一个逻辑,而不涉及其他模块,比如原有一个算法a*b+c,现在要改为a+b+c,可以通过修改类中原有方法的方式完成,前提条件是所有依赖或关联的
      类都按照相同的逻辑处理。
      ②. 子模块变化
        一个模块的变化,会对其他模块产生影响,特别是一个低层次的模块变更,必然会引起高层模块的变化。因此在通过扩展完成变化时,高层模块的修改是必然的。
      ③. 可见视图变化
        如果系统设计灵活,通过扩展来完成变化时最好的。

    3. 为什么要采用开闭原则
      3.1 存在即合理,只要是面向对象编程,开发时都会提及开闭原则。开闭原则是一个最基础的原则,前面五个原则都是开闭原则的具体形态,也就是说前免
        五个原则就是指导设计的工具和方法,而开闭原则才是其精神领袖。按Java中来说,开闭原则就是抽象类,其他五大原则就是实现类,开闭原则非常重要,可以从以下几个方面理解其重要性:
      ①. 开闭原则对测试的影响。
        通过开闭原则实现业务逻辑的变化,对新增加的类,新增加的测试方法,保证新增加的类是正确的就行了。
      ②. 开闭原则可以提高复用性
        面向对象设计中,所有逻辑都是从原子逻辑组成,粒度越小,被复用的可能就越大。
      ③. 开闭原则可以提高可维护性
        让维护人员对程序进行扩展时,维护人员最乐意做的就是扩展一个类,而不是修改一个类。
      ④. 面向对象的开发要求
        尽量在设计之初就考虑到所有可能的变化,然后留下接口,等待“可能”转变为“现实”。

    4. 如何使用开闭原则
      4.1 开闭原则是比较虚的,相当于一个口号一样。怎么将这个口号应用到实际呢《
      ①. 抽象约束
        通过抽象类或接口可以约束一组可能变化的行为,并且能够实现对扩展开放,其包含三个含义:第一,通过接口或抽象类进行约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的方法。第二,参数类型,引用对象尽量使用接口或抽象类,而不是实现类。第三,抽象层尽量保持稳定,一旦确定既不允许修改,但是可以进行接口的扩展,添加接口的子接口,然后再扩展新的实现类。
      ②. 元数据控制模块行为
        什么是元数据?元数据就是用来描述环境和数据的数据,通俗的说就是配置参数,参数可以从文件中获得,也可以从数据库中获得。
        使用框架的项目中,通过扩展一个子类、修改配置文件,完成业务变化,也是使用框架的好处。
      ③. 指定项目章程
        约定优于配置。
      ④. 封装变化
        两层含义:第一,将相同的变化封装到一个接口或抽象类中。第二,将不同的变化封装到不同的接口或抽象类中。一旦预测某个点可能有变化,就为这些变化点创建稳定的接口,也就是封装可能发生的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值