七大设计原则

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

为了实现这些目标,程序员在开发程序时应该遵循一系列的设计原则。其中,有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.hqwc.cn/news/619618.html

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

相关文章

【数字IC/FPGA】什么是无符号数?什么是有符号数?

进制 虽然在日常生活中&#xff0c;我们已经习惯了使用10进制数字&#xff0c;但在由数字电路构成的数字世界中&#xff0c;2进制才是效率更高的选择。 10进制与2进制 10进制&#xff08;decimal&#xff09;计数法&#xff08;一般也叫阿拉伯计数法&#xff09;是在日常生活…

SpringBoot集成Kafka之SASL_SSL

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Kafka 是一个由 Li…

软件无线电安全之HackRF One初探

HackRF介绍 HackRF是一款开源软件无线电&#xff08;SDR&#xff09;平台&#xff0c;由Great Scott Gadgets公司推出。它具有广泛的频率覆盖范围&#xff0c;从1 MHz到6 GHz&#xff0c;支持大部分常见的无线通信频段。采用软件定义无线电技术&#xff0c;HackRF提供了自定义…

最优算法100例之43-包含min函数的栈

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数,在该栈中,调用min,push及pop的时间复杂…

DC-2靶机知识点

知识点总结 1.IP访问与域名访问 2.端口扫描 3.目录扫描 4.cewl密码生成器 5.指纹探测 6.爆破ssh 7.msf的使用 8.rbash逃逸 9.git提权 靶机&#xff0c;攻击机就不多说了&#xff0c;给个靶机地址 https://download.vulnhub.com/dc/DC-2.zip 环境配置 因为访问该靶机…

移动应用安全合规动态:网信办、金管局发文强调数据安全;3月个人信息违规抽查结果出炉!(第五期)

一、监管部门动向&#xff1a;国家互联网信息办公室公布《促进和规范数据跨境流动规定》; 工信部发布《关于网络安全保险典型服务方案目录的公示》 二、安全新闻&#xff1a;恶意软件警报&#xff01;黑客利用软件即服务攻击印度安卓用户&#xff1b;Cerberus银行恶意软件的虚…

杭州掀起快递物流创新浪潮,2024长三角快递物流展7月共绘智慧物流新蓝图

杭州&#xff0c;作为中国的电商之都&#xff0c;近年来在快递物流行业背景与应用方面取得了显著的发展。随着电子商务的迅猛增长&#xff0c;杭州的快递物流行业迅速崛起&#xff0c;成为支撑电商产业发展的重要力量。 2024长三角&#xff08;杭州&#xff09;快递物流供应链…

vue3.4 新特性 defineModel() 宏

v-model 简介 官网是这样解释 v-model 的 v-model 的功能是&#xff0c;实现数据的双向绑定【本质上是 :value 和 input 语法糖】 如果是表单元素&#xff0c;下面两种写法是一样&#xff0c;这时v-model就是语法糖&#xff0c;帮你简化了操作 <input v-model"messag…

浅尝一下ECS(Entity Component System)(学习笔记)

参考文章&#xff1a;浅谈Unity ECS&#xff08;一&#xff09;Uniy ECS基础概念介绍&#xff1a;面向未来的ECS - 知乎 (zhihu.com) 视频链接&#xff1a;【青幻译制】GDC讲座系列之三 守望先锋的游戏架构和网络代码_哔哩哔哩_bilibili 云风的 BLOG: 浅谈《守望先锋》中的 E…

机器学习—特征预处理和降维(四)

什么是特征预处理&#xff1f; 通过一些转换函数将特征数据转换成更加适合算法模型的特征数据过程 1包含内容 数值型数据的无量纲化&#xff1a; 归一化标准化 2特征预处理API sklearn. preprocessing为什么要进行归一化 or 标准化&#xff1f; 特征的单位或者大小相差较大…

neo4j使用详解(十八、java driver使用及性能优化<高级用法>——最全参考)

Neo4j系列导航&#xff1a; neo4j安装及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 neo4j索引及调优 neo4j java Driver等更多 1.依赖引入 <dependency><groupId>org.neo4j.driver</groupId><…

Springboot+Vue项目-基于Java+MySQL的校园周边美食探索及分享平台系统(附源码+演示视频+LW)

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