七大设计原则

在软件开发的领域中,随着技术的不断进步和市场需求的不断变化,软件系统的设计和维护变得越来越重要。为了确保软件系统能够长期有效地运行,并且能够在未来的发展中适应新的需求和技术变化,提高软件系统的可维护性和可复用性成为了开发过程中的关键目标。此外,增加软件的可扩展性和灵活性也至关重要,因为它们可以确保软件系统能够轻松地添加新功能或者修改现有功能,而不会引发大量的结构重组或代码重写。

为了实现这些目标,程序员在开发程序时应该遵循一系列的设计原则。其中,有7条原则被广泛认为是提高软件开发效率、节约软件开发成本和维护成本的关键。
在这里插入图片描述

开闭原则

对扩展开放,对修改关闭。在程序需要进行拓展的时候,不需要去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。以下代码中ShoppingCart 类是对支付方式这一变化点开放的(可以轻易地添加新的 PaymentStrategy 实现),但对修改是关闭的(不需要为了支持新的支付方式而去修改 ShoppingCart 类的源代码)。
定义一个支付方式策略:

interface PaymentStrategy {void pay(double amount);
}// Step 2: 创建具体实现类,实现支付策略的扩展
class CashPayment implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " using cash.");}
}class CreditCardPayment implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " using credit card.");}
}

定义一个使用支付策略的上下文类,它对支付方式进行抽象:

class ShoppingCart {private List<Item> items;private PaymentStrategy paymentStrategy;public ShoppingCart(PaymentStrategy strategy) {this.paymentStrategy = strategy;}public void setPaymentStrategy(PaymentStrategy strategy) {this.paymentStrategy = strategy;}public void checkout() {double total = calculateTotal();paymentStrategy.pay(total); // 这里使用了多态,无需修改ShoppingCart就能添加新的支付方式}private double calculateTotal() {// 计算购物车总价的逻辑...return 100.0; // 示例金额}
}

里氏替换原则

任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

依赖倒转原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,相对于细节的多变性,抽象的东西要稳定的多。

// 抽象(接口)
interface MessageSender {void sendMessage(String message);
}// 具体实现(细节)
class EmailSender implements MessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending email with message: " + message);// 实现发送邮件的逻辑}
}class SMSSender implements MessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending SMS with message: " + message);// 实现发送短信的逻辑}
}// 高层模块(不依赖具体实现)
class Staff {private MessageSender sender;// 构造函数通过依赖注入设置消息发送器public Staff(MessageSender sender) {this.sender = sender;}public void receiveMessageAndSendNotification(String receivedMessage) {String notification = "Received message: " + receivedMessage;sender.sendMessage(notification);}
}// 客户端代码
public class Main {public static void main(String[] args) {// 创建高层模块实例时,可以选择不同的具体实现Staff staff = new Staff(new EmailSender()); // 或者 new SMSSender()staff.receiveMessageAndSendNotification("An important notice!");// 更改通知方式只需要更换MessageSender的实现即可staff = new Staff(new SMSSender());staff.receiveMessageAndSendNotification("An urgent alert!");}
}

Staff 类是高层模块,它依赖于 MessageSender 接口,而不是具体的发送器实现,这使得 Staff 类可以根据需要灵活切换不同的消息发送方式,而不必更改其自身的代码。这就是依赖倒转原则的体现。

接口隔离原则

接口隔离原则(Interface Segregation Principle, ISP)指出客户端不应该被迫依赖它们不使用的方法。换句话说,每个接口都应该有明确的责任,接口中的方法应该是高内聚的,避免“胖”接口。

// 未遵守ISP的“胖”接口
interface Employee {void calculateSalary();void printDetails();void sendEmailReminder();void updateWorkLog();
}// 遵守ISP,将接口拆分为多个具有单一责任的小接口
interface SalaryCalculator {void calculateSalary();
}interface EmployeeInfoPrinter {void printDetails();
}interface EmailService {void sendEmailReminder();
}interface WorkLogUpdater {void updateWorkLog();
}// 实体类分别实现所需的接口
class Developer implements SalaryCalculator, EmployeeInfoPrinter {// 实现calculateSalary和printDetails方法// ...
}class Manager implements SalaryCalculator, EmailService {// 实现calculateSalary和sendEmailReminder方法}

迪米特法则

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
不遵守迪米特法则:

class Order {private Customer customer;public Order(Customer customer) {this.customer = customer;}// 直接访问Customer的地址信息,违反迪米特法则public String getShippingAddressZipCode() {return customer.getAddress().getZipCode();}
}class Customer {private Address address;public Customer(Address address) {this.address = address;}public Address getAddress() {return address;}
}class Address {private String zipCode;public Address(String zipCode) {this.zipCode = zipCode;}public String getZipCode() {return zipCode;}
}

Order 类原本直接访问 Customer 的 Address 对象来获取邮编,这违反了迪米特法则。可以通过在 Customer 类中提供一个方法,Order 类只需与它的直接朋友 Customer 交流,从而降低了耦合度,遵循了迪米特法则。

合成复用原则

尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
原则强调了在设计中应尽量使用关联(Association)、组合(Composition)或聚合(Aggregation)关系来组合现有对象形成更复杂的结构或行为,而不是通过创建类层次结构进行功能扩展。
示例:添加日志记录功能
在一个系统中,若要为UserManager类添加日志记录功能,不是通过让UserManager直接继承具有日志功能的类,而是让它包含(组合或聚合)一个Logger对象,通过委托给这个对象来执行日志记录的操作:

public class UserManager {private Logger logger;public UserManager(Logger logger) {this.logger = logger;}// 其他业务逻辑方法...public void addUser(User user) {// 执行添加用户逻辑// ...// 委托给logger记录操作日志logger.log("User added: " + user.getUsername());}
}

过这种方式,可以轻易更换不同的Logger实现,而无需更改UserManager的结构,同时也避免了因过度使用继承带来的设计复杂性和维护困难。

单一职责原则

这一原则的核心在于控制类的粒度大小、将对象解耦、提高其内聚性。

  1. 违反单一职责原则的例子
    假设有一个Employee类,它既处理员工的工资计算也负责员工的个人信息管理,Employee类同时承担了员工信息管理和薪资计算两种职责。如果因为绩效计算规则改变需要修改calculateBonus方法,理论上不应影响到员工地址管理这部分功能:
public class Employee {private String name;private String address;private double salary;public Employee(String name, String address, double salary) {this.name = name;this.address = address;this.salary = salary;}// 负责员工个人信息管理public void updateAddress(String newAddress) {this.address = newAddress;}// 负责工资计算public double calculateBonus(double performanceRating) {return salary * (performanceRating / 10);}
}
  1. 遵循单一职责原则的例子
    我们将上述职责分开到两个类中,EmployeeInfo类专门负责个人信息管理,而EmployeeSalaryCalculator类则专注于薪资计算。这样,当任何一个职责的需求发生改变时,都不会直接影响到另一个职责的实现,从而提高了代码的可读性和可维护性:
public class EmployeeInfo {private String name;private String address;public EmployeeInfo(String name, String address) {this.name = name;this.address = address;}public void updateAddress(String newAddress) {this.address = newAddress;}
}public class EmployeeSalaryCalculator {private double salary;public EmployeeSalaryCalculator(double salary) {this.salary = salary;}public double calculateBonus(double performanceRating) {return salary * (performanceRating / 10);}
}// 员工实体可以持有这两个类的引用
public class Employee {private EmployeeInfo info;private EmployeeSalaryCalculator salaryCalculator;public Employee(String name, String address, double salary) {this.info = new EmployeeInfo(name, address);this.salaryCalculator = new EmployeeSalaryCalculator(salary);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817036.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

map与set

set使用 set在我们就是我们前面学习的k模型&#xff0c;它可以用来比对数据&#xff0c;增删查的时间复杂度都是O&#xff08;logn&#xff09;效率非常高&#xff0c;由于它底层的原因&#xff0c;它也可以实现排序&#xff0c;通过中序遍历可以输出我们的有序的数据&#xff…

安装gensim报错

安装gensim pip install --upgrade gensim装完以后注意一个 装了一堆库其实&#xff0c;看下对应的scipy版本是1.13.0 然后运行 import gensim报错&#xff1a; cannot import name ‘triu’ from ‘scipy.linalg’ https://www.soinside.com/question/brZ46N5EH7bk9xdVwXa…

华为OD机试 - 内存冷热标记(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

Springboot+Vue项目-基于Java+MySQL的高校心理教育辅导系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

【Linux网络编程】TCP协议

TCP协议 1.TCP协议段格式4位首位长度序号和确认序号16位窗口大小6个标志位 2.确认应答机制3.超时重传机制4.连接管理机制如何理解连接如何理解三次握手如何理解四次挥手 5.流量控制6.滑动窗口7.拥塞控制8.延迟应答9.捎带应答10.面向字节流11.粘包问题12.TCP异常情况13.TCP小结1…

亚马逊全球跨境市场地位稳固,武汉星起航深化服务助力合作伙伴

在全球跨境电商市场的激烈竞争中&#xff0c;亚马逊凭借其强大的市场地位和卓越的平台特点&#xff0c;持续引领着行业的发展潮流。作为行业的领军者&#xff0c;亚马逊不仅占据了全球市场的一大部分&#xff0c;还以其深厚的品牌影响力、广泛的覆盖范围和高效的物流运作&#…

雅虎、乐天、煤炉、国际站、newegg、wish等跨境卖家如何提升店铺销量?深度解析自养号测评的价值

一、如何提升销量&#xff1f; 优化产品描述和图片&#xff1a;确保您的产品描述准确、详细&#xff0c;图片清晰、美观。这将有助于提高产品的吸引力&#xff0c;增加潜在买家的购买意愿。 提供良好的客户服务&#xff1a;及时回复买家的咨询和问题&#xff0c;解决他们的疑…

【MySQL】索引篇

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8;友情提供 目录 本系列传送门 1. 什么是索引 2. 索引的特性 3. 索引的分类 4. 索引的优点及缺点 优点 缺点 5.…

我的电脑图标没了怎么办?恢复图标只需一分钟!

“我打开电脑后&#xff0c;突然发现我的电脑图标没了&#xff0c;这是为什么呢&#xff1f;有什么方法可以找回我的电脑图标吗&#xff1f;” 在日常使用电脑的过程中&#xff0c;电脑桌面的各个图标是保证我们能正常打开应用的前提。我的电脑图标没了怎么办&#xff1f;这可能…

zabbix升级后图形文字不显示

原版本升级后版本6.4.76.4.13 问题现象 更新小版本后zabbix数据图形都有&#xff0c;只有下方文字不显示 处理方式 下载win字体&#xff0c;根据自己选择&#xff0c;上传至/usr/share/zabbix/assets/fonts目录下&#xff0c;修改文件名为jianti.ttf 修改默认字体配置文件…

CSS导读 (元素显示模式 上)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 三、CSS的元素显示模式 3.1 什么是元素显示模式 3.2 块元素 3.3 行内元素 3.4 行内块元素 3.5 元素…

HashMap扩容原理(带源码分析)

HashMap的扩容原理 1.扩容流程图 注&#xff1a;拆分链表的规则 这里拆分链表时的一个比较&#xff1a;e.hash & oldCap 0 意思是&#xff1a;某一个节点的hash值和老数组容量求&运算。如果等于0&#xff0c;当前元素在老数组中的位置就是在新数组中的位置。如果不等…

CORS 跨域问题解决预检(OPTIONS)请求解释

场景&#xff1a; 业务要求从把系统B嵌入到系统A中&#xff0c;系统A和系统B是完成不同的两个域名&#xff0c;前端同事完成系统嵌入后&#xff0c;从A系统内部调用B系统的接口时候发现跨域错误&#xff08;CORS error&#xff09;&#xff0c;如下&#xff1a; 什么是跨域&…

vscode开发 vue3+ts 的 uni-app 微信小程序项目

创建uni-app项目&#xff1a; # 创建用ts开发的uni-app npx degit dcloudio/uni-preset-vue#vite-ts 项目名称 # 创建用js开发的uni-app npx degit dcloudio/uni-preset-vue#vite 项目名称VS Code 配置 为什么选择 VS Code &#xff1f; HbuilderX 对 TS 类型支持暂不完善VS…

(学习日记)2024.04.16:UCOSIII第四十四节:内存管理

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

手机数据恢复工具

下载地址&#xff1a;手机数据恢复工具.zip Android/HarmonyOS 文件误删是日常使用电子设备时经常遇到的问题&#xff0c;也许一不小心就就可能会误删。 俗话说&#xff1a;数据无价&#xff0c;一但想要找回一些被删除的文件&#xff0c;就需要耗费大量的精力和财力来恢复文…

【宝德PI300T G2智能小站开发教程(三)】centos配置系统开发源

目录 一.前言 二.配置dns服务器 三.测试连通性 四.设置更新源文件 一.前言 openeular系统的宝德板子没有更新的国内源,要如何配置? 二.配置dns服务器 vi /etc/resolv.conf 添加如下内容: nameserver 8.8.8.8 nameserver 114.114.114.114 三.测试连通性 ping www.ba…

vscode远程连接centos

文章目录 vacode连接linux1. 安装插件2. 查看配置3. 打开ssh4. 远程连接 vacode连接linux 1. 安装插件 在扩展栏搜索remote &#xff0c;找到Remote Development插件&#xff0c;进行安装&#xff1a; 2. 查看配置 打开自己的linux终端&#xff0c;输入ifconfig&#xff0c;…

Laravel/Lumen 中使用 Echo + Socket.IO-Client 实现网页即时通讯广播

此处以 Lumen 9 框架为例说明如何调试通过 Echo 服务端以及客户端 如果你是 Laravel/Lumen 10.47 用户&#xff0c;可以先了解官方的 Laravel Reverb。注意 Laravel Reverb 仅支持 Laravel/Lumen 10.47 以及 PHP 8.2Laravel Reverb 参考官网&#xff1a;https://laravel.com/d…

CSS导读 (复合选择器 下)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 2.5 伪类选择器 2.6 链接伪类选择器 2.6.1 链接伪类注意事项 2.6.2 链接伪类选择器实际开发中的写法 2.7 …