设计原则

注意:本笔记整理于 极客时间 设计模式之美

SOLID原则

SOLID原则:由5个设计原则组成的,它们分别是:

O:开闭原则

L:里式替换原则

I:接口隔离原则

D:依赖反转原则

单一职责原则(SRP)

单一职责原则(Single Responsibility Principle,SRP): 一个类或者模块只负责完成一个职责(或者功能)

也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。

类(class)和模块(module)的关系: 把模块看作比类更加抽象的概念,类也可以看作模块;或者 把模块看作比类更加粗粒度的代码块,模块中包含多个类,多个类组成一个模块。

如何判断类的职责是否足够单一?
大部分情况下,类里的方法是归为同一类功能,还是归为不相关的两类功能,并不是那么容易判定的。在真实的软件开发中,对于一个类是否职责单一的判定,是很难拿捏的。

因此,评价一个类的职责是否足够单一,我们并没有一个非常明确的、可以量化的标准,可以说,这是件非常主观、仁者见仁智者见智的事情。实际上,在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构(后面的章节中我们会讲到)。

得,下面这几条判断原则,比起很主观地去思考类是否职责单一,要史有指导意义、史具有可执行性:

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

类的职责是否设计得越单一越好?
为了满足单一职责原则,是不是把类拆得越细就越好呢?答案是否定的。

实际上,不管是应用设计原则还是设计模式,最终的目的还是提高代码的可读性、可扩展性、复用性、可维护性等。我们在考虑应用某一个设计原则是否合理的时候,也可以以此作为最终的考量标准。

开闭原则

开闭原则(Open Closed Principle, OCP):软件实体(模块、类、方法等)应该**“对扩展开放、对修改关闭"**。

也就是,添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)

实际上,开闭原则讲的就是代码的扩展性问题,是判断一段代码是否易扩展的“金标准"。

问如何才能做到“对扩展开放、对修改关闭”,也就粗略地等同于在问,如何才能写出扩展性好的代码。

在讲具体的方法论之前,我们先来看一些更加偏向顶层的指导思想。为了尽量写出扩展性好的代码,我们要时刻具备扩展意识、抽象意识、封装意识。这些“潜意识”可能比任何开发技巧都重要。

里式替换原则

里式替换原则(Liskov Substitution Principle,LSP):子类对象(object of subtype/derived class)能够替换程序(program)中父类对象(object of base/parent class)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。

里式替换原则是用来指导,继承关系中子类该如何设计的一个原则。理解里式替换原则,最核心的就是理解“design by contract,按照协议来设计”这几个字。父类定义了函数的“约定”(或者叫协议),那子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”。这里的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。

从定义描述来看,里式替换原则多态看起来确实有点类似,但实际上它们完全是两回事。为什么这么说呢?

多态和里式替换有点类似,但它们关注的角度是不一样的。多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,是用来指导继承关系中子类该如何设计的,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。

几个违反里式替换原则

  1. 子类违背父类声明要实现的功能
  2. 子类违背父类对输入、输出、异常的约
  3. 子类违背父类注释中所罗列的任何特殊
    说明

接口隔离原则

接口隔离原则(Interface Segregation Principle,ISP):客户端不应该强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者。

理解“接口隔离原则”的重点是理解其中的“接口”二字。这里有三种不同的理解。

如果把“接口”理解为一组接口集合,可以是某个微服务的接口,也可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。

如果把“接口”理解为单个API接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。

如果把“接口”理解为OOP中的接口,也可以理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。

依赖反转原则

KISS原则

KISS原则: Keep It Simple and Stupid,尽量保持简单。

代码的可读性和可维护性是衡量代码质量非常重要的两个标准。

如何写出满足KISS原则的代码?

实际上,我们前面已经讲到了一些方法。这里我稍微总结一下。

  1. 不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。
  2. 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出bug的概率会更高,维护的成本也比较高。
  3. 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。

YAGNI原则

**YAGNI原则:**You Ain’t Gonna Need lt,你不会需要它。

当用在软件开发中的时候,它的意思是:不要去设计当前用不到的功能;不要去编写当前用不到的代码。实际上,这条原则的核心思想就是:不要做过度设计。但是我们还是要预留好扩展点。

DRY原则

**DRY原则:**它的英文描述为:Don’t Repeat Yourself。中文直译为:不要重复自己。将它应用在编程中,可以理解为:不要写重复的代。

只要两段代码长得一样,那就是违反DRY原则了。真的是这样吗?答案是否定的。实际上,重复的代码不一定违反DRY原则,而且有些看似不重复的代码也有可能违反DRY原则。

逻辑重复

语义重复

代码执行重复

所谓“语义不重复”指的是:从功能上来看,这两个函数干的是完全不重复的两件事情。

尽管代码的实现逻辑是相同的,但语义不同,我们判定它并不违反DRY原则。对于包含重复代码的问题,我们可以通过抽象成更细粒度函数的方式来解决。

尽管两段代码的实现逻辑不重复,但语义重复,也就是功能重复,我们认为它违反了DRY原则。

怎么提高代码复用性?

  1. 减少代码耦合
  2. 满足单一职责原则
  3. 模块化 : 我们要善于将功能独立的代码,封装成模块。
  4. 业务与非业务逻辑分离:越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越难复用。我们将业务和非业务逻辑代码分离,抽取成一些通用的框架、类库、组件等。
  5. 通用代码下沉: 从分层的角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。我们只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。
  6. 继承、多态、抽象、封装
  7. 应用模板等设计模式

LOD迪米特法则

**迪米特法则(Law of Demeter,LOD):**最小知识原则

每个模块(unit)只应该了解那些与它关系密切的模块 的有限知识(knowledge)。或者说,每个模块只和自己的朋友“说话”(talk),不和陌生人“说话”(talk)。

不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口(也就是定义中的“有限知识")。

迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。

“高内聚、松耦合"

“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。不过,这两者并非完全独立不相干。高内聚有助于松耦合,松耦合又需要高内聚的支持。

高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。

实际上,我们前面讲过的单一职责原则是实现代码高内聚非常有效的设计原则。

松耦合,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动不会或者很少导致依赖类的代码改动。实际上,我们前面讲的依赖注入、接口隔离、基于接口而非实现编程,以及今天讲的迪米特法则,都是为了实现代码的松耦合。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值