编码遵循五大设计原则创建出更加健壮、可维护和可扩展的软件系统
一、单一职责原则(SRP)
* 定义:一个类应该只有一个引起它变化的原因。
* 解释:意味着一个类应该专注于做一件事情,当需求发生变化时,只影响到一个类。这有助于降低类间的耦合,使得代码更易于理解和维护。
示例场景:图书管理系统
假设我们正在设计一个图书管理系统的后端逻辑。在这个系统中,我们需要处理图书的添加、删除、查询等功能,同时也需要记录图书的借阅信息。
* 未按单一职责原则的示例:
在这个实现中,BookManager 类不仅负责图书的管理(添加、删除、查询),还负责图书借阅的记录。这意味着如果有需求变更,比如需要改进图书借阅的逻辑,那么这个类就需要改变,同时图书的基本管理功能也可能受到影响。
public class BookManager
{private List<Book> books;private Dictionary<int, BorrowRecord> borrowRecords;public BookManager(){books = new List<Book>();borrowRecords = new Dictionary<int, BorrowRecord>();}public void AddBook(Book book){books.Add(book);}public void RemoveBook(int bookId){books.RemoveAll(b => b.Id == bookId);borrowRecords.Remove(bookId);}public Book GetBookById(int bookId){return books.FirstOrDefault(b => b.Id == bookId);}public void BorrowBook(int bookId, int userId){var book = GetBookById(bookId);if (book != null && !borrowRecords.ContainsKey(bookId)){borrowRecords.Add(bookId, new BorrowRecord(bookId, userId));}}public void ReturnBook(int bookId){borrowRecords.Remove(bookId);}
}
* 按单一职责原则的示例:
将归还和借阅的方法分解出来,单独成一个类
public class BookRepository
{private List<Book> books;public BookRepository(){books = new List<Book>();}public void AddBook(Book book){books.Add(book);}public void RemoveBook(int bookId){books.RemoveAll(b => b.Id == bookId);}public Book GetBookById(int bookId){return books.FirstOrDefault(b => b.Id == bookId);}
}public class BorrowService
{private Dictionary<int, BorrowRecord> borrowRecords;public BorrowService(){borrowRecords = new Dictionary<int, BorrowRecord>();}public void BorrowBook(int bookId, int userId){if (!borrowRecords.ContainsKey(bookId)){borrowRecords.Add(bookId, new BorrowRecord(bookId, userId));}}public void ReturnBook(int bookId){borrowRecords.Remove(bookId);}
}
二、开放封闭原则(OCP)
* 定义:软件实体(类、模块、函数等)应该是可扩展的,但是不可修改的。
* 解释:当系统的需求发生变化时,我们应该能够通过增加新的代码来扩展原有的功能,而不是修改已有的代码。这有助于保持系统的稳定性,减少因为修改现有代码带来的风险。
示例场景:计算不同类型的订单折扣
假设我们正在开发一个电子商务平台,需要为不同类型的订单计算折扣。最初,我们的系统只支持两种类型的订单:标准订单和批量订单,其中批量订单可以享受额外的折扣。
* 未遵循开放封闭原则的示例:
public enum OrderType
{Standard,Bulk
}public class OrderDiscountCalculator
{public decimal CalculateDiscount(Order order){if (order.Type == OrderType.Standard){return order.Total * 0.95m; // 标准订单95%的折扣}else if (order.Type == OrderType.Bulk){return order.Total * 0.90m; // 批量订单90%的折扣}return order.Total;}
}
在这个实现中,OrderDiscountCalculator 类直接在 CalculateDiscount 方法中根据订单类型计算折扣。如果未来需要添加新的订单类型,比如会员订单,我们需要修改这个方法,这违反了OCP。
* 遵循开放闭合原则示例:
// 折扣策略接口
public interface IDiscountStrategy
{decimal CalculateDiscount(Order order);
}// 标准订单折扣策略
public class StandardOrderDiscount : IDiscountStrategy
{public decimal CalculateDiscount(Order order){return order.Total * 0.95m;}
}// 批量订单折扣策略
public class BulkOrderDiscount : IDiscountStrategy
{public decimal CalculateDiscount(Order order){return order.Total * 0.90m;}
}// 订单折扣计算器
public class OrderDiscountCalculator
{private readonly IDiscountStrategy _discountStrategy;public OrderDiscountCalculator(IDiscountStrategy discountStrategy){_discountStrategy = discountStrategy;}public decimal CalculateDiscount(Order order){return _discountStrategy.CalculateDiscount(order);}
}
为了遵循OCP,我们可以使用策略模式,将折扣的计算逻辑封装到独立的策略类中,然后在 OrderDiscountCalculator 中使用这些策略。
现在,如果我们需要添加新的订单类型(比如会员订单),我们只需要实现 IDiscountStrategy 接口并创建一个新的策略类,然后在应用中适当地使用它,而不需要修改现有的 OrderDiscountCalculator 类。这样,我们的系统就对扩展开放,对修改封闭了。
为了在应用中使用这些策略,你可以使用依赖注入框架,根据订单类型动态地注入正确的策略实例。这种方式提高了代码的灵活性和可扩展性,同时减少了修改现有代码的风险。
* 那么我们现在用依赖注入的方式实现 【遵循OCP的策略模式】
步骤1:定义接口和策略类
我们已经定义了
IDiscountStrategy接口和具体的策略类(StandardOrderDiscount和BulkOrderDiscount),这里不再重复。步骤2:配置依赖注入容器
public void ConfigureServices(IServiceCollection services) {// 注册策略类services.AddTransient<IDiscountStrategy, StandardOrderDiscount>();services.AddTransient<IDiscountStrategy, BulkOrderDiscount>();// 可以继续注册更多策略类,如会员订单策略// 注册 OrderDiscountCalculator 类,它将依赖于策略类services.AddTransient<OrderDiscountCalculator>(); }这里我们使用了
AddTransient方法,这意味着每次请求一个新的服务实例时,都会创建一个新的实例。步骤3:选择正确的策略
为了让控制器能够根据订单类型选择正确的策略,我们需要在创建
OrderDiscountCalculator实例时传入正确的策略。这可以通过创建工厂方法或使用条件逻辑来实现。例如,你可以创建一个DiscountStrategyFactory类,它根据订单类型返回相应的策略实例。public class DiscountStrategyFactory {private readonly IServiceProvider _serviceProvider;public DiscountStrategyFactory(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public IDiscountStrategy GetStrategy(Order order){switch (order.Type){case OrderType.Standard:return _serviceProvider.GetRequiredService<StandardOrderDiscount>();case OrderType.Bulk:return _serviceProvider.GetRequiredService<BulkOrderDiscount>();// 可以添加更多case处理其他类型的订单default:throw new ArgumentException("Invalid order type.");}} }在控制器中根据订单类型选择正确的类
public class OrdersController : Controller {private readonly DiscountStrategyFactory _strategyFactory;private readonly OrderDiscountCalculator _discountCalculator;public OrdersController(IServiceProvider serviceProvider){_strategyFactory = new DiscountStrategyFactory(serviceProvider);_discountCalculator = new OrderDiscountCalculator(_strategyFactory.GetStrategy(GetOrderById(orderId)));}// ... }
-------------------------------- ^.^ 写累了,下次再写 ^.^---------------------------------
三、里氏替换原则(LSP)
* 定义:子类必须能够替换其基类。
* 解释:任何基类可以出现的地方,子类一定可以出现。这保证了在使用继承时,子类的行为与基类一致,不会破坏程序的正确性
四、接口隔离原则(ISP)
* 定义:不应该强迫客户程序依赖于它们不用的方法。
* 解释:一个类对另一个类的依赖应该建立在最小的接口上,即不应该为实现接口而实现接口,而是应该实现真正需要的方法。这有助于降低类间的耦合,使得接口更纯粹,更易于理解和使用。
五、依赖倒置原则(DIP)
* 定义:高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
* 解释:高层模块(如业务逻辑层)应依赖于抽象接口或基类,而不是具体的实现类。这有助于解耦系统中的各个部分,使得系统更灵活,更易于扩展和维护。
相关文章:
编码遵循五大设计原则创建出更加健壮、可维护和可扩展的软件系统
一、单一职责原则(SRP) * 定义:一个类应该只有一个引起它变化的原因。 * 解释:意味着一个类应该专注于做一件事情,当需求发生变化时,只影响到一个类。这有助于降低类间的耦合,使得代码更易于理…...
记录一个问题
问题描述 如果一个物料既在A总成零件号下计算为托盘库,在B总成零件号下计算为箱库,则放于箱库。 A中选择排名第21的递补进托盘库。(也需要判断递补的是否在其他总成零件中为箱库,是的话继续递补判断) 解决思路 为了…...
ONLYOFFICE 8.1版本桌面编辑器测评:重塑办公效率的巅峰之作
在数字化办公日益普及的今天,一款高效、便捷且功能强大的桌面编辑器成为了职场人士不可或缺的工具。ONLYOFFICE 8.1版本桌面编辑器凭借其卓越的性能和丰富的功能,成功吸引了众多用户的目光。今天,我们将对ONLYOFFICE 8.1版本桌面编辑器进行全…...
【shell脚本速成】python安装脚本
文章目录 案例需求应用场景解决问题脚本思路案例代码 🌈你好呀!我是 山顶风景独好 🎈欢迎踏入我的博客世界,能与您在此邂逅,真是缘分使然!😊 🌸愿您在此停留的每一刻,都沐…...
Redis报错:MISCONF Redis is configured to save RDB snapshots
错误提示内容: 2024-06-25 16:30:49 : Connection: Redis_Server > [runCommand] PING 2024-06-25 16:30:49 : Connection: Redis_Server > Response received : -MISCONF Redis is configured to save RDB snapshots, but it is currently not able to pers…...
关于使用绿联 USB-A转RJ45 2.5G网卡提速的解决问题
问题 网络下载速率低 网线是七类网线,外接的USB网卡驱动 我的自带网卡是 I219v 在嵌入了2.5G网络后一直无法到达1.5G以上。 平均测速300~500M 解决方案 更新了USB的网卡驱动 禁用了 I219-V的驱动。测速即可 USB驱动下载地址 https://download.csdn.net/downlo…...
Qt: QPushButton 按钮实现 上图标下文字
效果如下: 实现有如下几种方式: 1. 使用 QPushButton 设置 setStyleSheet 例: ui->recorder->setStyleSheet("QPushButton{"\"border: 1px solid #00d2ff; "\"min-height: 60px; "\"col…...
使用阿里云效API操作流水线
使用阿里云效(Alibaba Cloud DevOps)API操作流水线时,需要注意以下几个方面: 认证与授权 确保你已经获取了正确的访问凭证(AccessKey ID 和 AccessKey Secret),并且这些凭证具有足够的权限来执行…...
使用命令行创建uniapp+TS项目,使用vscode编辑器
一:如果没有pnpm,先安装pnpm 二:使用npx工具和degit工具从 GitHub 上的 dcloudio/uni-preset-vue 仓库克隆一个名为 vite-ts 的分支,到项目中. 执行完上面命令后,去manifest.json添加appid(自己微信小程序的Id),也可不执行直接下一步,执行pnpm install ,再执行pnpm:dev:mp-weix…...
ABC355 Bingo2
分析: 找出其中一行或列或任意对角线被全部标记,即可输出回合数,否则输出-1 如果x%n0,行是x/n,列是n 如果x%n!0,行是x/n1,列是x%n 如果行列或行列n1即为对角线。 标记行列对角线…...
Spring+Vue项目部署
目录 一、需要的资源 二、步骤 1.首先要拥有一个服务器 2.项目准备 vue: 打包: 3.服务器装环境 文件上传 设置application.yml覆盖 添加启动和停止脚本 编辑 安装jdk1.8 安装nginx 安装mysql 报错:「ERR」1273-Unknown collation: utf8m…...
【uml期末复习】统一建模语言大纲
前言: 关于uml的期末复习的常考知识点,可能对你们有帮助😉 目录 第一部分 概念与基础 第一章 面向对象技术 第二章 统一软件过程 第三章 UML概述 第四章 用例图 第五章 类图 第六章 对象图 第七章 顺序图 第八章 协作图 第九章 状态…...
Linux高级IO
高级IO 1.五种IO模型1.1 阻塞IO1.2 非阻塞IO1.3 信号驱动IO1.4 多路复用/多路转接IO1.5 异步IO1.6 小结 2.高级IO重要概念3.非阻塞IO3.1 实现函数NoBlock3.2 轮询方式读取标准输入 4.I/O多路转接之select4.1 理解select执行过程4.2 select的特点4.3 select缺点4.4 实现 5.I/O多…...
go-admin-ui开源后台管理系统华为云部署
1.华为云开通8000与9527端口 2.编译 编译成功 3.发布到远程服务器 4.登陆华为云终端 5.安装Nginx 6.查看服务启动状态 7.添加网站 添加与修改配置www-data 改为 www 自定义日志输出格式 添加网站配置文件go_admin_ui.conf 添加如下内容: location 下的root指向网站文件夹 修…...
点云入门知识
点云的处理任务 场景语义分割 物体的三维表达方法(3D representations): 点云:是由物体表面上许多点数据来表征这个物体。最接近原始传感器数据,且具有丰富的几何信息。 Mesh:用三角形面片和正方形面片拼…...
HTML静态网页成品作业(HTML+CSS+JS)——家乡莆田介绍网页(5个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,使用Javacsript代码实现图片轮播,共有5个页面。 二、作品…...
#### grpc比http性能高的原因 ####
grpc比http性能高的原因 二进制消息格式:gRPC使用Protobuf(一种有效的二进制消息格式)进行序列化,这种格式在服务器和客户端上的序列化速度非常快,且序列化后的消息体积小,适合带宽有限的场景。 HTTP/2协…...
微软Edge浏览器搜索引擎切换全攻略
微软Edge浏览器作为Windows 10的默认浏览器,提供了丰富的功能和良好的用户体验。其中,搜索引擎的切换功能允许用户根据个人喜好和需求,快速更换搜索引擎,从而获得更加个性化的搜索服务。本文将详细介绍如何在Edge浏览器中进行搜索…...
<Linux> 实现命名管道多进程任务派发
实现命名管道多进程任务派发 common文件 #ifndef _COMMON_H_ #define _COMMON_H_#pragma once #include <iostream> #include <unistd.h> #include <string> #include <sys/types.h> #include <sys/stat.h> #include <wait.h> #include &…...
BigInteger 和 BigDecimal(java)
文章目录 BigInteger(大整数)常用构造方法常用方法 BigDecimal(大浮点数)常用构造方法常用方法 DecimalFormat(数字格式化) BigInteger(大整数) java.math.BigInteger。 父类:Number 常用构造方法 构造方法:BigIntege…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
