编码遵循五大设计原则创建出更加健壮、可维护和可扩展的软件系统
一、单一职责原则(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…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...