当前位置: 首页 > news >正文

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


软件设计原则
 

优秀设计的特征
 

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

代码复用

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

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

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

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

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

框架位于最高层。它们确实能帮助你精简自己的设计,可 以用于明确解决问题所需的抽象概念,然后用类来表示这些概念并定义其关系。例如, 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

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

相关文章:

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

软件设计原则 优秀设计的特征 在开始学习实际的模式前,让我们来看看软件架构的设计过程,了解一下需要达成目标与需要尽量避免的陷阱。 代码复用 无论是开发何种软件产品,成本和时间都最重要的两个维度。较短的开发时间意味着可比竞争对…...

Redis 基本配置

Redis的配置文件通常位于 /etc/redis/redis.conf。以下是一些常见的Redis配置选项和它们的说明: 基本配置 1. 绑定地址 bind 127.0.0.1默认情况下,Redis只监听本地接口。如果需要远程访问,可以修改成bind 0.0.0.0,不过这会带来…...

【C++庖丁解牛】函数栈帧的创建与销毁

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 1. 寄存器2. ebp和esp是如…...

Java基础16(集合框架 List ArrayList容器类 ArrayList底层源码解析及扩容机制)

目录 一、什么是集合? 二、集合接口 三、List集合 四、ArrayList容器类 1. 常用方法 1.1 增加 1.2 查找 int size() E get(int index) int indexOf(Object c) boolean contains(Object c) boolean isEmpty() List SubList(int fromindex,int …...

数组:移除元素

参考资料&#xff1a;代码随想录 本题思路&#xff1a;通过快慢指针将两次循环减少到一次 class Solution {public int removeElement(int[] nums, int val) {//0 1 2 2 2 2 3int fast 0;int slow 0;while(fast < nums.length){if(nums[fast] ! val){nums[slow] nums[f…...

胡说八道(24.6.22)——通信杂谈(完结)

上回书说到雷达和香农的信息论&#xff0c;今天来进行完结。 数字幅值调制或幅值键控&#xff08;ASK&#xff09;调制方式是指载波信号的幅值随数字基带信号而变化&#xff0c;因此可以实现将基带信号搬移到载波频段。2ASK是利用代表数字信息0或1的基带矩形脉冲去键控一个连续…...

设计模式原则——里氏替换原则

设计模式原则 设计模式示例代码库地址&#xff1a; https://gitee.com/Jasonpupil/designPatterns 里氏替换原则 继承必须确保父类所拥有的性质在子类中依然成立 与开闭原则不同的是开闭原则可以改变父类原有的功能&#xff0c;里氏替换原则不能修改父类的原有的性质&#…...

详解 ClickHouse 的 SQL 操作

传统关系型数据库&#xff08;以 MySQL 为例&#xff09;的 SQL 语句&#xff0c;ClickHouse 基本都支持 一、插入 --语法&#xff1a; insert into table_name values(xxxxxx),(yyyyyyy),...;insert into table_name select xxxxx from table_name2 where yyyyy;二、更新和删…...

WPF与Winform,你的选择是?

概述 在桌面应用的发展历程中&#xff0c;Winform和WPF作为微软推出的两大框架&#xff0c;各自承载着不同的设计理念和技术特色。Winform以其稳定、成熟的技术基础&#xff0c;长期占据着企业级应用开发的重要地位。而WPF&#xff0c;作为后来者&#xff0c;以其现代化的UI设计…...

基于SpringBoot的实习管理系统设计与实现

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; SpringBoot框架&#xff0c;B/S模式 工具&#xff1a; MyEclipse&#xff0c;Tomcat 系统展示 …...

编程用什么电脑不卡的:深度解析与推荐

编程用什么电脑不卡的&#xff1a;深度解析与推荐 在编程的世界里&#xff0c;一台流畅不卡的电脑无疑是每个开发者的得力助手。然而&#xff0c;面对市场上琳琅满目的电脑品牌和型号&#xff0c;如何选择一台适合编程的电脑却成为了一个令人困惑的问题。本文将从四个方面、五…...

优先级队列模拟实现

目录 1.堆的概念 2.堆性质堆中的某个元素小于或大于他的左右孩子 3.小根堆实例 4.堆创建 4.1调整思路 4.2向下调整思路 4.3代码实现&#xff08;大根堆&#xff09; 5.堆的删除 6.堆的插入 7.常用接口 7.1PriorityQueue和PriorityBlockingQueue 1.堆的概念 如果有一…...

记一次服务器崩溃事件

今天在安装Jenkins的时候&#xff0c;进行到插件安装这一步&#xff0c;本来一切顺利&#xff0c;结果最后安装完成之后一直进不去网页&#xff0c;显示连接超时&#xff0c;网上搜索了一圈也没发现什么相似的情况&#xff0c;当我疑惑的时候回到Linux控制台&#xff0c;发现命…...

神经网络 #数据挖掘 #Python

神经网络是一种受生物神经元系统启发的人工计算模型&#xff0c;用于模仿人脑的学习和决策过程。它由大量互相连接的节点&#xff08;称为神经元&#xff09;组成&#xff0c;这些节点处理和传递信息。神经网络通常包含输入层、隐藏层&#xff08;可有多个&#xff09;和输出层…...

营销复盘秘籍,6步法让你的活动效果翻倍

在营销的世界中&#xff0c;每一次活动都是一次探险&#xff0c;而复盘就是探险后的宝藏图&#xff0c;指引我们发现问题、提炼经验、优化策略。 想要学习如何复盘&#xff0c;只要了解以下复盘六大步骤&#xff0c;即可不断总结&#xff0c;逐渐走向卓越。 第一步&#xff1…...

Linux下命令行文件创建删除、目录创建删除

在Linux命令行下&#xff0c;文件和目录的创建与删除是通过一系列基础命令完成的&#xff0c;这些命令对于日常的系统管理和文件操作至关重要。 下面将详细介绍这些命令的功能和使用方法。 普通文件的创建与删除 创建文件 touch命令&#xff1a;主要用于创建一个空文件&…...

数字排列问题

题目&#xff1a;有1、2、3、4个数字&#xff0c;能组成多少个互不相同且无重复数字的三位数&#xff1f;都是多少&#xff1f; 代码&#xff1a; #include <stdio.h> int main() { int count 0; // 计数器&#xff0c;记录生成的三位数的数量 // 使用三个嵌套的fo…...

CentOS Linux 7系统中离线安装MySQL5.7步骤

预计数据文件存储目录为&#xff1a;/opt/mysql/data 1、文件下载&#xff1a; 安装文件下载链接&#xff1a;https://downloads.mysql.com/archives/community/ 2、检查当前系统是否安装过MySQL [rootcnic51 mysql]# rpm -qa|grep mariadb mariadb-libs-5.5.68-1.el7.x86_6…...

XSS跨站攻击漏洞

XSS跨站攻击漏洞 一 概述 1 XSS概述 xss全称为&#xff1a;Cross Site Scripting&#xff0c;指跨站攻击脚本&#xff0c;XSS漏洞发生在前端&#xff0c;攻击的是浏览器的解析引擎&#xff0c;XSS就是让攻击者的JavaScript代码在受害者的浏览器上执行。 XSS攻击者的目的就是…...

PMP到底值不值得考?

首先&#xff0c;咱们得明白PMP是个啥。 PMP&#xff0c;全称Project Management Professional&#xff0c;是美国项目管理协会PMI颁发的一个项目管理专业人士资格认证。 PMP证书在项目管理领域可是有着举足轻重的地位&#xff0c;与MBA、MPA并驾齐驱&#xff0c;被称为“全球…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...