从《深入设计模式》一书中学到的编程智慧

2024-06-24 10:20

本文主要是介绍从《深入设计模式》一书中学到的编程智慧,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


软件设计原则
 

优秀设计的特征
 

在开始学习实际的模式前,让我们来看看软件架构的设计过程,了解一下需要达成目标与需要尽量避免的陷阱。

代码复用

无论是开发何种软件产品,成本和时间都最重要的两个维度。较短的开发时间意味着可比竞争对手更早进入市场; 较低的开发成本意味着能够留出更多营销资金,因此能更广泛地覆盖潜在客户。

代码复用是减少开发成本时最常用的方式之一。其意图非常明显: 与其反复从头开发,不如在新对象中重用已有代码。

这个想法表面看起来很棒,但实际上要让已有代码在全新的上下文中工作,通常还是需要付出额外努力的。组件间紧密的耦合、对具体类而非接口的依赖和硬编码的行为都会降低代码的灵活性,使得复用这些代码变得更加困难。

使用设计模式是增加软件组件灵活性并使其易于复用的方式之一。但是有时,这也会让组件变得更加复杂。设计模式创始人之一的埃里希·伽玛 在谈到代码复用中设计模式的角色时说:

我觉得复用有三个层次。在最底层,你可以复用类: 类库、容器,也许还有一些类的“团体(例如容器和迭代器)”。

框架位于最高层。它们确实能帮助你精简自己的设计,可 以用于明确解决问题所需的抽象概念,然后用类来表示这些概念并定义其关系。例如, JUnit 是一个小型框架,也是框 架 的“Hello, world”,其 中 定 义 了 Test 、 TestCase 和 TestSuite 这几个类及其关系。

框架通常比单个类的颗粒度要大。你可以通过在某处构建子类来与框架建立联系。这些子类信奉“别给我们打电话,我 们会给你打电话的。” 这句所谓的好莱坞原则。框架让你可以 自定义行为,并会在需要完成工作时告知你。这和 JUnit 一样,对吧? 当它希望执行测试时就会告诉你,但其他的一切 都仅会在框架中发生。

还有一个中间层次。这也是我认识中的模式所处位置。设计模式比框架更小且更抽象。它们实际上是对一组类的关系及 其互动方式的描述。当你从类转向模式,并最终到达框架的过程中,复用程度会不断增加。

埃里希·伽玛谈灵活性和代码复用:
https://refactoringguru.cn/gamma-interview

  

中间层次的优点在于模式提供的复用方式要比框架的风险小。创建框架是一项投入重大且风险很高的工作。模式则让你能„ 独立于具体代码来复用设计思想和理念。

扩展性

变化是程序员生命中唯一不变的事情。

• 你在 Windows 平台上发布了一款游戏,但现在人们想要 macOS 的版本。

• 你创建了一个使用方形按钮的 GUI 框架,但几个月后圆形按钮开始流行起来。

• 你设计了一款优秀的电子商务网站构架,但仅仅几个月后,客户就要求新增接受电话订单的功能。

每位软件开发者都经历过许多相似的故事,导致它们发生的原因也不少。

首先,我们在开始着手解决问题后才能更好地理解问题。通常在完成了第一版的程序后,你就做好了从头开始重写代码 的准备,因为现在你已经能在很多方面更好地理解问题了,同时在专业水平上也有所提高,所以之前的代码现在看上去可能会显得很糟糕。

其次可能是在你掌控之外的某些事情发生了变化。这也是导致许多开发团队转变最初想法的原因。每位在网络应用中使 用 Flash 的开发者都必须重新开发或移植代码,因为不断地 有浏览器停止对 Flash 格式的支持。

第三个原因是需求的改变。你的客户之前对当前版本的程序 感到满意,但是现在希望对程序进行 11 个“小小”的改动,使其可完成原始计划阶段中完全没有提到的功能。

这也有好的一面: 如果有人要求你对程序进行修改,至少说明还有人关心它。

因此在设计程序架构时,所有有经验的开发者会尽量选择支 持未来任何可能变更的方式。


设计原则

什么是优秀的软件设计? 如何对其进行评估? 你需要遵循哪些实践方式才能实现这样的方式? 如何让你的架构灵活、稳定且易于理解?

这些都是很好的问题。但不幸的是,根据应用类型的不同,这些问题的答案也不尽相同。不过对于你的项目来说,有几个通用的软件设计原则可能会对解决这些问题有所帮助。本书中列出的绝大部分设计模式都是基于这些原则的。

封装变化的内容

找到程序中的变化内容并将其与不变的内容区分开。

该原则的主要目的是将变更造成的影响最小化。
 

假设你的程序是一艘船,变更就是徘徊在水下的可怕水雷。如果船撞上水雷就会沉没。

了解到这些情况后,你可将船体分隔为独立的隔间,并对其进行安全的密封,以使得任何损坏都会被限制在隔间范围内。现在,即使船撞上水雷也不会沉没了。

你可用同样的方式将程序的变化部分放入独立的模块中,保护其他代码不受负面影响。最终,你只需花较少时间就能让程序恢复正常工作,或是实现并测试修改的内容。你在修改 程序上所花的时间越少,就会有更多时间来实现功能。

方法层面的封装

假如你正在开发一个电子商务网站。代码中某处有一个 getOrderTotal 获取订单总额 方法,用于计算订单的总价 (包括税金在内)。

  

我们预计在未来可能会修改与税金相关的代码。税率会根据客户居住的国家/地区、州/省甚至城市而有所不同; 而且一段时间后,实际的计算公式可能会由于新的法律或规定而修改。因此,你将需要经常性地修改 getOrderTotal 方法。不过仔细看看方法名称,连它都在暗示其不关心税金是如何计 算出来的。

method getOrderTotal(order) is total = 0foreach item in order.lineItems    total += item.price * item.quantityif (order.country == "US")  total += total * 0.07 // 美国营业税else if (order.country == "EU"): total += total * 0.20 // 欧洲增值税return total

修改前: 税率计算代码和方法的其他代码混杂在一起。

你可以将计算税金的逻辑抽取到一个单独的方法中,并对原始方法隐藏该逻辑。

method getOrderTotal(order) is   total = 0    foreach item in order.lineItems    total += item.price * item.quantity  total += total * getTaxRate(order.country)      return totalmethod getTaxRate(country) is   if (country == "US")    return 0.07 // 美国营业税   else if (country == "EU")     return 0.20 // 欧洲增值税  else return 0

修改后: 你可通过调用指定方法获取税率。

这样税率相关的修改就被隔离在单个方法内了。此外,如果税率计算逻辑变得过于复杂,你也能更方便地将其移动到独 立的类中。

类层面的封装

一段时间后,你可能会在一个以前完成简单工作的方法中添加越来越多的职责。新增行为通常还会带来助手成员变量和方法,最终使得包含接纳它们的类的主要职责变得模糊。将所有这些内容抽取到一个新类中会让程序更加清晰和简洁。

 修改前: 在 订单 Order 类中计算税金。

订单类的对象将所有与税金相关的工作委派给一个专门负责的特殊对象。

 修改后: 对订单类隐藏税金计算。

以上内容摘自《深入设计模式》一书的预览章节,想了解更多内容,大家可以购买本书的完整版:
https://refactoringguru.cn/design-patterns/book

 欢迎关注公众号:清晰编程,获取更多精彩内容

这篇关于从《深入设计模式》一书中学到的编程智慧的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089870

相关文章

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

《纳瓦尔宝典》是纳瓦尔·拉维坎特(Naval Ravikant)的智慧箴言

《纳瓦尔宝典》是一本由埃里克·乔根森(Erik Jorgensen)编著的书籍,该书于2022年5月10日由中信出版社出版。这本书的核心内容围绕硅谷知名天使投资人纳瓦尔·拉维坎特(Naval Ravikant)的智慧箴言,特别是关于财富积累和幸福人生的原则与方法。 晓北斗推荐 《纳瓦尔宝典》 基本信息 书名:《纳瓦尔宝典》作者:[美] 埃里克·乔根森译者:赵灿出版时间:2022