六大原则之外的设计原则|设计原则

简介: 在前面的几篇设计原则文章中,我们分别讲述了经典的六大设计原则。但是事实上,我们在开发中还有几个重要的设计原则,在这篇文章中,一并给大家讲述。

前置知识

  • 已学习六大设计原则
  • 了解设计模式基础
  • 有项目经历

前言

在前面的几篇设计原则文章中,我们分别讲述了经典的六大设计原则。但是事实上,我们在开发中还有几个重要的设计原则,在这篇文章中,一并给大家讲述。

这几个原则分别是 KISS 原则YAGNI 原则DRY 原则

下面,本文将逐个阐述这几个原则的重点,以及不同。

KISS 原则

KISS 原则,其英文阐述是:Keep It Simple and Stupid.

译为中文就是:尽量保持简单

什么尽量保持简单?如何界定简单?

事实上,KISS 原则是较为广泛的原则,譬如其应用在建筑上面叫做轻奢,而应用在手机设计上面叫做极简风,他们所遵循的都是简单原则。

那么在编程中的 KISS 原则,具体讲的是什么的简单呢?

可能有人对此做严格定义,代码行数少的、或者说逻辑简单的,就是符合KISS 原则。但实际上,KISS 原则和之前的设计原则一样,都是 具体问题具体分析的,在不同的业务场景下判定是否符合 KISS 原则的标准也是不同的

而判断是否符合 KISS 原则,有以下两条标准

  • 一般情况下,逻辑简单、实现难度小、可读性好的代码,符合 KISS 原则。由于软件开发往往是会有时限的,所以我们都会需要追求性能和时间的平衡。那么基于这一点,我们认为业务代码的实现有 Java包 或者 现成库 的,直接调用来实现业务即可;这样子的代码便是逻辑简单,实现难度小的代码,同时使用这些方法也会让我们的代码可读性较好。若没有可调用的类库,我们再做简单的处理即可。
  • 而当业务问题本身就复杂且重要的时候,我们的代码逻辑复杂视为是遵循 KISS 原则的。因为复杂的问题就是要用复杂的方法来解决。但是我们要界定清楚该问题是否为影响系统的主要问题,用复杂的方法解决该问题是否能大幅提高我们系统的性能;如果不能,那使用复杂的逻辑来解决这个问题就不符合 KISS 原则了,那倒不如使用快捷的类库性价比高,而不是自己编写的某种高性能的算法。

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

那么我们应该如何编写满足 KISS 原则的代码呢?我们在日常开发中注意以下几点即可。

  1. 不要重复造轮子。使用语言自带的类库或者业内著名的开源库,实现业务的性价比是最高的,出错几率小也易于维护。
  2. 统一开发的技术栈。不要使用合作者不熟悉的技术或者领域开发,要统一团队的开发水平和能力范围,才不会出现你认为简单,我认为复杂、可读性差的问题。
  3. 不要过度优化。过度优化会造成代码可读性变差,且浪费时间。
  4. Code Review 。团队内定期 Code Review 可以发现很多问题。合作者认为你的代码不简单,难以阅读。那99%确定你的代码不符合 KISS 原则,不够简单了。

YAGNI 原则

YAGNI 原则,其英文阐述是:You Ain’t Gonna Need It

直译是:你不需要它。其意义是:不要去编码设计目前用不到的功能或代码,其核心意思是:不要过度设计

这个含义,好像和上面的 KISS 原则含义接近,那他们之间有何不同呢?

这两个含义其实是不同的。KISS 原则讲的是 “如何做”,如何尽量保持简单;而 YAGNI 原则讲的是“要不要做”,目前不需要的就不要做

这个原则**如何应用呢?**例如,我在建立项目框架的时候,会提前引入大量的开源库,我是凭借经验引入的,这样子是违背 YAGNI 原则了。但当设计 EventBus 消息类的时候,我预测以后会有不同的消息类需要发送;虽然我不清楚是什么消息类,但是我对目前的功能做了抽象,预留了拓展接口。这样子是遵守了 YAGNI 原则。我们可以看到,前者属于过度设计,但是后者却并未过度设计。

DRY 原则

DRY 原则,其英文阐述是:Don’t Repeat Yourself

直译过来是:不要重复你自己。意思就是:不要写重复的代码

那么问题来了,哪种算重复的代码 ?

不要写哪种重复的代码?

重复包括 实现逻辑的重复功能语义的重复代码执行的重复

  • 实现逻辑的重复
    实现逻辑的重复指的是:两段代码实现的业务功能不同,但是他们内部实现的代码是一致的。
//姓名检查
public static void checkNameInput(TextInputLayout textInputLayout) {
    if (textInputLayout != null) {
        if (textInputLayout.getEditText().getText().length() > 10) {
            textInputLayout.setError("输入内容超过上限");
        } else if (textInputLayout.getEditText().getText().length() < 2) {
            textInputLayout.setError("最少2位");
        } else {
            textInputLayout.setError(null);
        }
    }
}
//Id检查
public static void checkIdInput(TextInputLayout textInputLayout) {
    if (textInputLayout != null) {
        if (textInputLayout.getEditText().getText().length() > 10) {
            textInputLayout.setError("输入内容超过上限");
        } else if (textInputLayout.getEditText().getText().length() < 2) {
            textInputLayout.setError("最少2位");
        } else {
            textInputLayout.setError(null);
        }
    }
}
复制代码
  • 例如上方的两端代码,他们内部实现的逻辑是一样的,但是他们不违背 DRY 原则 。因为他们的功能是不同的,一个是姓名检查,一个是Id检查。我们也无需将他们合并为一个函数,因为我们的业务随时可能变化,Id检查功能检测的输入上线有可能是要变化的,现在将其合并之后很可能还要重新拆解他们。而想减少重复的代码,做其他更细粒度的功能拆解,再使得他们组合完成一个大功能即可。
  • 功能语义的重复
    功能语义的重复指的是:虽然他们内部实现的代码是不同的,但是两段代码实现的业务功能是相同的。
//姓名检查
public static void checkNameInput(TextInputLayout textInputLayout) {
    if (textInputLayout != null) {
        if (textInputLayout.getEditText().getText().length() > 10) {
            textInputLayout.setError("输入内容超过上限");
        } else if (textInputLayout.getEditText().getText().length() < 2) {
            textInputLayout.setError("最少2位");
        } else {
            textInputLayout.setError(null);
        }
    }
}
//姓名检查
public static void nameInputCheck(TextInputLayout textInputLayout) {
    if (textInputLayout != null) {
        return;
    }
    if (textInputLayout.getEditText().getText().length() < 2) {
        textInputLayout.setError("最少2位");
    } else if (textInputLayout.getEditText().getText().length() > 10) {
        textInputLayout.setError("输入内容超过上限");
    } 
}
复制代码
  • 上面两个函数的名称和实现逻辑都不同,但是他们违背了 DRY 原则。由于他们实现的是同一个功能,也许是出自两个工程师之手,但是我们必须更改,将其统一为使用同一个函数。因为也许后续业务需求的更改,需要更改这个功能,但是由于有两个不同的实现,我们很可能忘记某个更改某个实现,导致出现业务更替不全面出现bug
  • 代码执行的重复
    代码执行的重复指的是:原本只需要某个功能一次的代码,出现了重复执行的问题。
    这个重复就不做代码解释了,相信大家都容易理解。这种重复也算违背了 DRY 原则,因为重复的代码如果是IO操作或者说网络操作,那么对系统执行时间的影响就会非常大,这时候我们就需要重构除去这种重复

由上述可知,功能语义的重复、代码执行的重复 都是违反 DRY 原则的

何为代码复用?

我们上面说了那么多代码重复,那么和我们常说的 代码复用 又冲突么?还有,代码复用性又是什么呢?

代码复用:一种行为,在开发新功能的时候,尽量复用已经存在的代码

代码复用性:表示一段代码可被复用的特性或能力,我们编码时候需要让代码尽量可复用

复用性和复用是不同的,前者是从代码开发者的角度来看的,后者是从使用者角度来看的。

“不重复”并不代表“可复用“。DRY 中强调不重复,但不一定就说里面的代码一定可复用。所以说这是两个概念的东西

那如何提高代码可复用性呢?

  • 减少代码耦合
    对于高度耦合的代码,当我们希望复用其中的一个功能,想把这个功能的代码抽取出来成为一个独立的模块、类或者函数的时候,往往会发现牵一发而动全身。移动一点代码,就要牵连到很多其他相关的代码。所以,高度耦合的代码会影响到代码的复用性,我们要尽量减少代码耦合。
  • 满足单一职责原则
    我们前面讲过,如果职责不够单一,模块、类设计得大而全,那依赖它的代码或者它依赖的代码就会比较多,进而增加了代码的耦合。根据上一点,也就会影响到代码的复用性。相反,越细粒度的代码,代码的通用性会越好,越容易被复用。
  • 模块化
    这里的“模块”,不单单指一组类构成的模块,还可以理解为单个类、函数。我们要善于将功能独立的代码,封装成模块。独立的模块就像一块一块的积木,更加容易复用,可以直接拿来搭建更加复杂的系统。
  • 业务与非业务逻辑分离
    越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越难复用。所以,为了复用跟业务无关的代码,我们将业务和非业务逻辑代码分离,抽取成一些通用的框架、类库、组件等。
  • 通用代码下沉
    从分层的角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。一般情况下,在代码分层之后,为了避免交叉调用导致调用关系混乱,我们只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。所以,通用的代码我们尽量下沉到更下层。
  • 继承、多态、抽象、封装
    在讲面向对象特性的时候,我们讲到,利用继承,可以将公共的代码抽取到父类,子类复用父类的属性和方法。利用多态,我们可以动态地替换一段代码的部分逻辑,让这段代码可复用。除此之外,抽象和封装,从更加广义的层面、而非狭义的面向对象特性的层面来理解的话,越抽象、越不依赖具体的实现,越容易复用。代码封装成模块,隐藏可变的细节、暴露不变的接口,就越容易复用。
  • 应用模板等设计模式
    一些设计模式,也能提高代码的复用性。比如,模板模式利用了多态来实现,可以灵活地替换其中的部分代码,整个流程模板代码可复用。
    除了刚刚我们讲到的几点,还有一些跟编程语言相关的特性,也能提高代码的复用性,比如泛型编程等。实际上,除了上面讲到的这些方法之外,复用意识也非常重要。在写代码的时候,我们要多去思考一下,这个部分代码是否可以抽取出来,作为一个独立的模块、类或者函数供多处使用。在设计每个模块、类、函数的时候,要像设计一个外部 API 那样,去思考它的复用性。

tips:第一次编写代码的时候,我们不考虑复用性;第二次遇到复用场景的时候,再进行重构使其复用。



相关文章
|
2月前
|
设计模式 缓存 安全
探索设计模式的魅力:从单一继承到组合模式-软件设计的演变与未来
组合模式:构建灵活树形结构的艺术。 组合模式旨在解决如何将对象组合成树形结构,隐藏具体实现,使客户端对单个对象和复合对象的使用具有一致性。通过将对象组合成树形结构,组合模式提供了层次化的结构,使系统更灵活、可扩展。 核心思想在于统一叶节点和组合节点。叶节点代表具体的对象,而组合节点则是其他对象的容器。该设计允许我们以统一的方式处理叶子和组合,简化了许多操作。实践中,组合模式适用于具有树形结构并且希望保持结构灵活的系统。它不仅提高了代码的可重用性和可维护性,还使得添加新功能变得简单,无需修改现有代码。...
44 0
|
10月前
面向对象七大设计原则,看了必会(代码详细版)(中)
面向对象七大设计原则,看了必会(代码详细版)(中)
|
10月前
|
关系型数据库
面向对象七大设计原则,看了必会(代码详细版)(上)
面向对象七大设计原则,看了必会(代码详细版)(上)
|
10月前
面向对象七大设计原则,看了必会(代码详细版)(下)
面向对象七大设计原则,看了必会(代码详细版)(下)
|
11月前
|
设计模式 前端开发 Java
【Java设计模式 思想原则重构】设计思想、设计原则、重构总结
【Java设计模式 思想原则重构】设计思想、设计原则、重构总结
144 0
|
设计模式 Java 关系型数据库
面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
|
设计模式
设计模式(7) -- 合成复用原则和七大原则总结
设计模式(7) -- 合成复用原则和七大原则总结
105 0
设计模式(7) -- 合成复用原则和七大原则总结
|
设计模式 uml
设计模式七大原则——合成复用原则
设计模式七大原则——合成复用原则
设计模式七大原则——合成复用原则
|
设计模式 安全 Python
设计模式中应遵循的基本原则
设计模式中应遵循的基本原则
179 0
面向对象的设计的7大原则
面向对象的设计的7大原则
196 0