Skip to content
快速预览

前言

✍️ w 🕒 2023-08-01 14:34:55(2 months ago) 🔗 G.设计模式

我们需要先搞清楚设计模式能解决哪些范围的问题后,才能正确使用设计模式。设计模式解决的是可复用的设计问题,而类似可靠性设计、性能设计、安全性设计、可服务性设计等都不是设计模式能够解决的

设计模式从不同项目中总结出来的通用经验,是为了帮助我们快速理解现有的系统,并从中找出共性规律,如果没有足够的经验或者思考,反而容易引入错误的设计,造成更多的麻烦

好的设计从来不是看用的模式有多少,而是看如何合理利用模式的设计思想,以及如何利用模式解决真实的问题

降低软件复杂度

一个程序只做一件事,并做得很好,代码之间的相互影响越多,软件越复杂。比如,A 依赖 B,B 依赖 C……一直这样循环下去,程序就会变得非常复杂,也就是我们编程中常说的,如果一个类文件写了上万行代码,那么代码逻辑将会非常难理解。

减少代码复杂度,通过减少硬编码来控制代码量,对于技术复杂度来说,要想在整体上保持简单性,需要在设计时就做好技术选型,降低实现复杂度而言,可以使用统一的代码规范

把软件设计成独立组件并能随意地组合,才能真正应对更多变化的需求,不要做定制功能驱动开发比如,用户需要一个 上传文件的红色按钮,你就实现了一个叫 红色上传按钮功能 的组件,过几天变为需要一个 上传文件的绿色按钮时,你再修改代码满足要求……这不是组合设计,而是直接映射设计,看似用户是需要 上传 这个功能,但实际上用户隐藏了对 不同颜色 的需求,解决思路

  • 解耦,代码与代码之间的依赖关系越多,程序就越复杂,只有将大程序拆分成小程序,才能让人容易理解它们彼此之间的关系。也就是我们常说的在设计时应尽量分离接口与实现,程序间应该耦合在某个规范与标准上,而不是耦合在具体代码实现逻辑上

  • 模块化,可替换的一致性,是将程序拆分成模块,每个模块都应该有一个清晰的接口,模块之间的关系应该通过接口去耦合,而不是通过具体的代码实现逻辑去耦合。这样可以使得模块之间的关系更加清晰,易于替换和重用。

高内聚指的是模块内部的各个元素(如函数、类等)之间的联系紧密,功能相关性强,模块内部的元素应该尽量聚合在一起,以保持功能的一致性。这样可以使得模块的设计更加清晰,易于维护和修改。

低耦合指的是模块之间的联系尽量松散,模块之间的依赖关系应该通过标准去耦合,而不是通过具体的代码实现逻辑去耦合。这样可以使得模块之间的关系更加清晰,易于扩展和替换。

举个例子 假设我们正在设计一个电商网站,其中有一个购物车模块。购物车模块需要实现以下功能:

  • 添加商品到购物车
  • 从购物车中删除商品
  • 修改购物车中商品的数量
  • 计算购物车中商品的总价

为了实现这些功能,我们可以将购物车模块拆分成以下几个小模块:

  • 商品模块:负责定义商品的属性和方法
  • 购物车模块:负责管理购物车中的商品,包括添加、删除、修改数量和计算总价等功能
  • 数据库模块:负责将购物车中的商品信息存储到数据库中,以便下次用户登录时可以恢复购物车中的商品信息

在设计这些模块时,我们需要遵循高内聚、低耦合的原则。例如,购物车模块内部的各个函数之间应该紧密联系,功能相关性强,这样可以使得购物车模块的设计更加清晰,易于维护和修改。而购物车模块和商品模块之间的关系应该通过标准去耦合,例如定义一个商品接口,购物车模块只依赖于商品接口,而不依赖于具体的商品实现。这样可以使得购物车模块更加灵活,易于扩展和替换。

总结高内聚、低耦合原则:模块内部尽量聚合以保持功能的一致性,模块外部尽量通过标准去耦合

代码分层

  • 软件分层架构是通过层来隔离不同的关注点(变化相似的地方),以此来解决不同需求变化的问题,使得这种变化可以被控制在一个层里

  • 代码分层架构就是将软件“元素”(代码)按照“层”(代码关系)的方式组织起来的一种结构

  • 分层架构核心的原则是:当请求或数据从外部传递过来后,必须是从上一层传递给下一层

不采用分层,直接通信会造成新的代码耦合,增加代码的复杂分层的本质就是为了让相似变化在各自的层内变化,而不造成层与层之间的相互影响

使用分层解决

  • 通过分层来拆解问题,文章中的例子,在做Http 向服务端发送字符串这个过程中,需要创建连接发送字符串关闭连接,三个过程面向过程编写这三种情况中每一层都会有属于自己的一些所在层的问题,将每一层的问题拆解,分析去简化。代码分层本身就是一种拆解复杂问题的好方法
  • 通过分层来提升代码可扩展性,可以将复杂的逻辑切分为多个层,这样大问题就变成了多个小问题,组件自身的复用性也就提高了
  • 容易做服务的横向扩展

总结:实现责任分离、解耦、组件复用和标准制定,缺点,开发成本变高:因为不同层分别承担各自的责任,性能降低:请求数据因为经过多层代码的处理, 代码复杂度增加:因为层与层之间存在强耦合,所以对于一些组合功能的调用,则需要增加很多层之间的调用。

分层的设计

认知规则:其上,面向用户的体验与交互;其中,面向应用与业务逻辑;其下,面对各种外部资源与设备。在进行分层架构设计时,我们完全可以基于这个经典的三层架构,沿着水平方向进一步切分属于不同抽象层次的关注点。因此,分层的第一个依据是基于关注点为不同的调用目的划分层次

代码的工程思维

软件开发过程 = 定义与分析 + 设计 + 实现 + 测试 + 交付 + 维护,不能在软件开发时,我们总是容易太过于关注局部,而没能跳出局部去看整体软件开发 ≠ 软件编码

  • 有效沟通现在有一个问题需要解决,问题现象是 xxx,业务方的预期是 xxx,实际看到的是 xxx,不符合预期,从日志和报错看可能是 xxx 出问题了。由于 xxx 项目上线时间紧迫,急需解决,在线等

代码质量评估

为什么要学习设计模式。通过设计模式中的原则可以发现代码中的质量问题

  1. 可读性
  • 代码是否易于理解和阅读?
  • 变量、函数和类的命名是否清晰和有意义?
  • 注释是否恰当,能够解释代码的意图和逻辑?
  • 代码是否遵循编码规范和风格指南?
  1. 可扩展性
  • 代码是否易于扩展和添加新功能?
  • 模块和组件之间的接口是否设计得合理和易用?
  • 是否遵循“开闭原则”,即对扩展开放,对修改关闭?
  • 是否使用了适当的设计模式和设计原则来支持扩展性?
  1. 可维护性
  • 代码是否易于维护和修改?
  • 是否遵循“单一职责原则”,即每个模块和类只负责一项功能?
  • 是否遵循“最小知识原则”,即模块和类之间的依赖关系是否尽量减少?
  • 是否有适当的文档和注释来帮助理解和维护代码?
  1. 灵活性
  • 代码是否具有灵活性,能够适应变化的需求?
  • 是否遵循“依赖倒置原则”,即依赖于抽象而不是具体实现?
  • 是否使用了适当的设计模式和技术来支持灵活性?
  1. 简洁性
  • 代码是否简洁明了,没有冗余和重复的部分?
  • 是否遵循“不要重复自己原则”,即避免重复的代码和逻辑?
  • 是否使用了适当的抽象和封装来简化代码?
  1. 可复用性
  • 代码是否具有可复用性,能够在其他项目或场景中重用?
  • 是否避免了重复造轮子,能够使用已有的项目代码或类库?
  • 是否遵循“单一职责原则”和“最小知识原则”,使代码更易于复用?
  1. 可测试性
  • 代码是否易于测试,能够进行单元测试和集成测试?
  • 是否有适当的测试覆盖率,覆盖了各种正常和异常情况?
  • 是否遵循“单一职责原则”和“最小知识原则”,使代码更易于测试?
  1. 业务需求和非功能需求
  • 代码是否实现了预期的业务需求,逻辑是否正确?
  • 是否处理了各种异常情况,是否有适当的错误处理机制?
  • 日志打印是否得当,是否方便 debug 和排查问题?
  • 接口是否易用,是否支持幂等、事务等特性?
  • 代码是否存在并发问题,是否线程安全?
  • 性能是否有优化空间,是否可以优化 SQL、算法等部分?
  • 是否有安全漏洞,输入输出校验是否全面?

参考文档中比较好的语句

  • 代码质量既是设计出来的,也是迭代优化出来的。换句话说,无论是前期的产品需求分析、架构设计,还是后期的详细代码设计与编码,都离不开良好的设计。(趣学设计模式)

不要陷入的误区

业务复杂达到一定程度了我们才选用设计模式去解决复杂的问题,扩展性 复用性,冗余度,小的情况没必要使用在这种情况下

参考文档

《 javasprict 设计模式与开发实践 》

GoF设计模式

趣学设计模式

一篇文章读懂分层架构

Released under the MIT License.