设计原则简析

单一职责

一句话描述】:
一个类或一个模块只负责完成一个职责(或功能),即保持“高内聚、低耦合”。

详解
不要设计大而全的类,要设计粒度小、功能单一的类。换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。 但是在判断功能是否单一时,需要根据实际使用情况做出判断。

这里有一些可以参考的原则,来判断类是否满足单一职责:

  • 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
  • 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  • 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
  • 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
  • 类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来。

开闭原则

一句话描述】:
一个类或者模块在实现的时候需要保持对扩展开放、对修改关闭。

详解
这个原则看起来很简单,但是实践时并不容易,因为编写代码时总是会有地方需要修改。在应用这个原则的时候,要修改影响的范围具体分析。比如增加了一个类的字段,对这个类而言是一处修改;但是如果这种修改没有改变这个类本身的其它方法、或者依赖这个类的其它类在使用这个类时并没有改动,可以算作是扩展。这里可以根据是否有破坏原有代码的正常使用,破坏原有的单元测试来分析。

开闭原则本质上是讲代码的扩展性,如果一个代码在编写时就充分考虑了未来的扩展性,进行了一定的抽象、预先留好了扩展点,那么在未来需求变动时,能够不修改代码的整体接口、只在特定地方进行增量修改,那么可以认为这部分代码是符合了对扩展开放、对修改关闭。


里式替换

一句话描述
子类在重写父类函数时,可以改变内部逻辑,但是不能改变父类函数原有的约定。

详解
里式替换是用来指导继承关系中,子类该如何设计的一个原则,先前所说的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。

子类改变父类函数约定的一些情况如下:

  • 子类违反父类的功能逻辑

    比如父类的一个排序方法、是按照创建时间升序排列的,但是子类重写时改成了按照更新时间降序排列的

  • 子类违背父类对于入参、出参、异常的约定

    比如对于同一函数,父类在输入为空时,抛异常;而子类在输入为为空时,返回空。

  • 子类违背父类的特殊说明

    比如父类定义的函数规定只能查询余额,但是子类在实现时除了可以查询余额、还能更新余额。


接口隔离

一句话描述
接口隔离是指调用方在使用接口时,不需要去依赖它本身不需要的接口。

详解
接口隔离的英文解释是“Client should not be forced to depend upon interfaces that they do not use.”,即客户端不应该被依赖它不需要的接口。这里可以从三个方面来进行理解,一是对一个接口而言、二是对接口中的某个函数而言、三是对于OOP中的接口概念。

  • 把接口理解为一组API接口集合
    对于一个接口而言,如果这个接口里面有部分函数、只有部分情况才会被调用方使用,那么可以考虑将这部分函数抽取出来作为另外一个接口,让两者隔离开。
  • 把接口理解成一个接口函数
    对于一个接口的函数而言,有些函数的功能大而全,但是调用方多数情况下只使用其中的一部分功能,这种情况下可以把原函数拆分成几个函数,让每个函数的功能单一。
  • 把接口理解成OOP中的接口
    这部分和多用组合、少用继承很类似。有些时候我们会定义一个大而全的接口,让子类去实现这个大而全的接口,但是子类实际上只需要实现接口的部分函数、并不需要完整实现,因此可以根据功能将这个大而全的接口拆分成多个子接口,让子类自己去实现这些接口。

依赖反转

一句话描述
依赖反转是一个设计原则,是指高层模块不依赖底层模块,高层模块和低层模块应该通过抽象来相互依赖。

详解
高层模块是指调用者,底层模块就是被调用者,在它们两者之间需要加入一个中间层进行抽象,从而让高层和底层隔离,避免直接依赖,从而在修改时都不会对对方造成影响。这里举2个例子来说明:Java的web程序可以在Tomcat中运行,这里Tomcat就是高层模块、web应用程序是低层模块,两者没有直接依赖关系,但是都依赖"servlet"规范;Java应用程序访问数据库驱动时,并不是直接使用驱动的,而是通过JDBC这个中间层的抽象来访问数据库。
而Spring中有控制反转的功能,因此这里集中讲解一下控制反转、依赖注入。

  • 控制反转
    控制反转,英文名称为Inversion Of Control,是一种比较笼统的设计思想,一般用来指导框架层面的设计。这里所说的“控制”,指的是对程序执行流程的控制;有“反转”就有“正转”,“正转”即是之前由程序员控制程序的执行流程,“反转”后将程序的执行流程交由框架来控制。

  • 依赖注入
    依赖注入,即Dependency Injection,是控制反转的一种实现方式。Spring的IOC容器就是通过依赖注入的方式,动态创建对象。


KISS\YAGNI原则

一句话描述
kiss原则,即keep it simple and stupid,主要是在编写函数时,要尽量保持简单、提高可读性;YAGNI 原则的英文全称是:You Ain’t Gonna Need It,意思是不要去编写当前用不到的代码,不要过度设计。

【详解】
这两个原则易懂,但是评价标准比较主观,不做过多解释。


DRY

一句话描述
即do not repeat yourself,提高代码的复用性。

详解
项目中时长会发现重复的代码,根据代码重复情况分为以下几种情况:

  • 实现逻辑重复
    实现逻辑相同,但是作用的类型、对象、语义是不同的函数,可以不提取公共方法,避免后续修改的时候出问题。
  • 功能语义重复
    名字不同,但是完成的功能是一样的;这种情况要统一用一个方法,把多余的方法给去掉。
  • 代码执行重复
    代码里面有部分逻辑重复或多余,比如判断语句在函数及其子函数被重复调用;这种情况是需要合并方法、修改的。

提高代码复用性的方法
  • 减少代码耦合

  • 满足单一职责原则

    让类、接口能够提高高内聚、低耦合特性。

  • 模块化

    要将功能独立的代码,封装成模块

  • 业务与非业务逻辑分离

    将与业务无关的代码抽取出来,与业务代码分离,抽取成类库、公共组件。

  • 通用代码下沉

    通用的代码应该尽量放在底层

  • 继承、多态、抽象、封装

    通过继承来复用父类的公共方法,通过多态来动态替换一段代码中的逻辑、让代码复用。抽象与封装更多是一种思路,越抽象、越不依赖具体实现的代码,越容易复用。

  • 应用模板等设计模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值