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

深入浅出:面向对象软件设计原则(OOD)

目录

前言
1.单一责任原则(SRP)
2.开发封闭原则(OCP)
3.里氏替换原则(LSP)
4.依赖倒置原则(DIP)
5.接口分离原则(ISP)
6.共同封闭原则(CCP)
7.共同重用原则(CRP)
1.无环依赖原则(ADP)
总结

前言

在当今的软件开发领域,面向对象编程(OOP)已经成为了主流。良好的设计原则对于编写出高质量、易于维护的代码至关重要。在本文中,我们将详细介绍面向对象软件设计的八大原则,并通过实际案例来解释每一个原则。无论您是初学者还是有一定经验的开发者,理解这些原则都将对您的编程技能提升大有裨益。


1.单一责任原则(SRP)

概述:一个类应该只有一个引起变化的原因。例如,一个处理用户账户的类应该只负责用户账户相关的功能,不应该同时处理订单处理或数据库连接等其他功能。

代码案例:

// 不好的例子:UserAccount类同时处理了账户信息和数据库操作
class UserAccount {public void createUser(String username, String password) {// 处理用户账户逻辑}public void saveToDatabase() {// 数据库操作逻辑}
}// 好的例子:将数据库操作分离到另一个类
class UserAccount {public void createUser(String username, String password) {// 只处理用户账户逻辑}
}
class Database {public void saveToDatabase(UserAccount user) {// 数据库操作逻辑}
}

具体的方法和建议:

  • 在设计类时,确保每个类只负责一个功能或职责。
  • 通过类的职责来命名类,使得类的名称能够清晰地表达其功能。
  • 定期回顾代码,检查是否有类承担了过多的责任,如果有,考虑将其拆分为多个类。

2.开发封闭原则(OCP)

概述:软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在不修改原有代码的基础上增加新的功能。

代码案例:

// 不好的例子:每次添加新形状都需要修改ShapeArea类
class ShapeArea {public double calculateArea(Rectangle r) { /* ... */ }public double calculateArea(Circle c) { /* ... */ }// 如果添加新的形状,需要修改这个类
}// 好的例子:通过继承实现扩展
abstract class Shape {public abstract double calculateArea();
}
class Rectangle extends Shape { /* ... */ }
class Circle extends Shape { /* ... */ }
// 添加新形状时,只需扩展Shape类

具体的方法和建议:

  • 使用接口和抽象类来定义公共的行为,使得子类可以扩展这些行为而不是修改。
  • 在编写代码时,考虑未来可能的扩展点,并设计成可以插入新的模块或功能。
  • 避免使用硬编码的值或条件,而是使用配置文件或策略模式来切换行为。

3.里氏替换原则(LSP)

概述:子类必须能够替换它们的基类。这意味着,在任何使用基类的地方,都可以使用子类对象,而不会影响到程序的正确性。

代码案例:

// 不好的例子:Square不是长方形的特殊情况,替换会导致问题
class Rectangle {public void setWidth(int width) { /* ... */ }public void setHeight(int height) { /* ... */ }
}
class Square extends Rectangle {public void setWidth(int width) {super.setWidth(width);super.setHeight(width); // 强制正方形}public void setHeight(int height) {super.setWidth(height);super.setHeight(height); // 强制正方形}
}
// 使用Rectangle的地方替换为Square可能会出现问题// 好的例子:正方形应该有自己的接口
interface Quadrilateral {void setWidth(int width);void setHeight(int height);
}
class Rectangle implements Quadrilateral { /* ... */ }
class Square implements Quadrilateral { /* ... */ }

具体的方法和建议:

  • 确保子类能够替换父类而不影响程序的正确性。
  • 避免在子类中引入新的约束或改变父类的行为。
  • 使用多态来调用子类的方法,这样可以确保子类对象可以被父类引用所使用。

4.依赖倒置原则(DIP)

概述:高层模块不应该依赖低层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

代码案例:

// 不好的例子:高层模块依赖于具体实现
class EmailService {public void sendEmail(String message) { /* ... */ }
}
class Notification {private EmailService emailService = new EmailService();public void sendNotification(String message) {emailService.sendEmail(message);}
}// 好的例子:高层模块依赖于抽象
interface INotificationService {void sendNotification(String message);
}
class EmailService implements INotificationService {public void sendNotification(String message) { /* ... */ }
}
class Notification {private INotificationService notificationService;public Notification(INotificationService service) {notificationService = service;}public void sendNotification(String message) {notificationService.sendNotification(message);}
}

具体的方法和建议:

  • 高层模块(业务逻辑)不应该依赖于低层模块(数据访问层、基础设施),而是依赖于抽象。
  • 使用依赖注入(DI)来提供抽象的依赖项,而不是在类内部创建具体的依赖项。
  • 定义好接口和抽象类,并通过构造函数、方法参数或属性来传递依赖。

5.接口分离原则(ISP)

概述:客户端不应该被迫依赖于他们不用的方法。接口应该最小化,而不是包含所有可能的方法。

代码案例:

// 不好的例子:一个接口包含太多不相关的方法
interface IAnimal {void eat();void fly(); // 并非所有动物都会飞void swim(); // 并非所有动物都会游泳
}
class Bird implements IAnimal { /* ... */ }
class Fish implements IAnimal { /* ... */ }// 好的例子:将接口拆分为多个专门的接口
interface IFlyable {void fly();
}
interface ISwimmable {void swim();
}
interface IAnimal {void eat();
}
class Bird implements IAnimal, IFlyable { /* ... */ }
class Fish implements IAnimal, IS

具体的方法和建议:

  • 避免创建大而全的接口,而是应该根据功能将接口拆分为多个小的、专门的接口。
  • 客户端(使用接口的类)只需要依赖于它们实际使用的方法。
  • 使用接口组合而不是继承来扩展接口的功能。

6.共同封闭原则(CCP)

概述:共同封闭原则指出,一个包中的所有类应该对同一类性质的变更做出反应,即一个变更的原因应该只影响一个包中的所有类,而不是整个系统中的类。

代码案例:

// 不好的案例:一个包包含了多个不相关的功能
package com.example.mixedbag;
class UserAccount {// ...
}
class Order {// ...
}
class Product {// ...
}// 好的案例:按照功能将类分布在不同的包中
package com.example.user;
class UserAccount {// ...
}package com.example.order;
class Order {// ...
}package com.example.product;
class Product {// ...
}

具体的方法和建议:

  • 将相关的类和接口放在同一个模块或包中,这样当业务规则变化时,只需要修改一个模块或包。
  • 设计时考虑类的变化频率和原因,将可能一起变化的类放在一起。

7.共同重用原则(CRP)

概述:一个项目中有一个工具包,包含了各种工具类,比如日期处理、字符串处理和数学计算。如果一个开发者只重用了日期处理类,那么他可能不需要字符串处理和数学计算类。这违反了共同重用原则。

代码案例:

// 不好的案例:工具包中包含了不相关的工具类
package com.example.utils;
class DateUtils {// ...
}
class StringUtils {// ...
}
class MathUtils {// ...
}// 好的案例:按照功能将工具类分布在不同的包中
package com.example.date;
class DateUtils {// ...
}package com.example.string;
class StringUtils {// ...
}package com.example.math;
class MathUtils {// ...
}

具体的方法和建议:

  • 如果一个类被重用,那么与它一起被频繁重用的其他类也应该放在同一个包中。
  • 设计包和模块时,考虑它们的重用性,避免将不相关的类放在一起。

1.无环依赖原则(ADP)

概述:无环依赖原则指出,在包的依赖关系图中不应该存在环,即包之间的依赖关系应该是单向的,避免循环依赖。

代码案例:

// 不好的案例:数据访问包和业务逻辑包之间存在循环依赖
package com.example.data;
import com.example.business.BusinessLogic;class DataAccess {private BusinessLogic businessLogic;// ...
}package com.example.business;
import com.example.data.DataAccess;class BusinessLogic {private DataAccess dataAccess;// ...
}// 好的案例:业务逻辑包依赖于数据访问包,但数据访问包不依赖于业务逻辑包
package com.example.data;
class DataAccess {// ...
}package com.example.business;
import com.example.data.DataAccess;class BusinessLogic {private DataAccess dataAccess;// ...
}

具体的方法和建议:

  • 确保包之间的依赖关系是单向的,并且没有循环依赖。
  • 使用依赖关系图来可视化包之间的依赖,并确保图中没有环。

总结

通过本文的介绍,我们深入理解了面向对象软件设计的八大原则:单一责任原则、开发封闭原则、里氏替换原则、依赖倒置原则、接口分离原则、共同封闭原则、共同重用原则和无环依赖原则。这些原则为我们提供了一种思考问题和解决问题的方法论,帮助我们编写出更加健壮、灵活和可维护的代码。在实际的软件开发过程中,我们应该时刻牢记这些原则,并努力将它们应用到实践中。只有这样,我们才能更好地应对复杂的业务需求,不断提高我们的开发效率和软件质量。

我最近在备考软考中级,但是发现缺少实际经验,很难理解,大家可以分享自己的学习经验吗,在评论区~

相关文章:

深入浅出:面向对象软件设计原则(OOD)

目录 前言 1.单一责任原则(SRP) 2.开发封闭原则(OCP) 3.里氏替换原则(LSP) 4.依赖倒置原则(DIP) 5.接口分离原则(ISP) 6.共同封闭原则(CCP&#xff09…...

缓存与数据一致性问题

1、更新了数据库,再更新缓存 假设数据库更新成功,缓存更新失败,在缓存失效和过期的时候,读取到的都是老数据缓存。 2、更新缓存,更新数据库 缓存更新成功了,数据库更新失败,是不是读取的缓存的都…...

2024年上海高考作文题目(ChatGPT版)

一、2024年6月7日上海高考作文题目 生活中,人们常用认可度判别事物,区分高下。请写一篇文章,谈谈你对“认可度”的认识和思考。 要求:(1)自拟题目;(2)不少于800字。 二、…...

.net 调用海康SDK以及常见的坑解释

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在工控领域,很多时候需要…...

KVM+GFS高可用

资源列表 操作系统 IP 主机名 Centos7 192.168.10.51 gfs1 Centos7 192.168.10.52 gfs2 Centos7 192.168.10.53 kvm 在gfs节点部署GlusterFS #添加hosts解析 cat >> /etc/hosts << EOF 192.168.10.51 gfs1 192.168.10.52 gfs2 192.168.10.53 kvm EOF …...

C++迈向精通:当我尝试修改虚函数表

尝试修改虚函数表 本期纯整活儿好吧&#xff01;&#xff01;&#xff01;&#xff01; 初衷 有一天我突然开始好奇虚函数表是否真的存在&#xff0c;于是我开始想是否能够从C中查看或者调用虚函数表中的内容。&#xff0c;于是有了下面的操作。 操作过程 起初我并没有思路…...

IDEA 高效插件工具

文章目录 LombokMaven Helper 依赖冲突any-rule(正则表达式插件)快速生成javadocGsonFormat (Aits) 将json解析成类Diagrams使用 类图SequenceDiagram时序图GenerateAllSetter&#xff08;AltEnter&#xff09;大小写转写String ManipulationGitToolBox 代码提交人activate-pow…...

SQL入门大全

SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理关系型数据库的标准编程语言。它具有数据操纵和数据定义等多种功能&#xff0c;为数据库管理系统提供了强大的交互性特点&#xff0c;能够极大地提高计算机应用系统的工作质量与…...

【深度优先搜索 广度优先搜索】297. 二叉树的序列化与反序列化

本文涉及知识点 深度优先搜索 广度优先搜索 深度优先搜索汇总 图论知识汇总 LeetCode297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传…...

App UI 风格,引领设计风向

App UI 风格&#xff0c;引领设计风向...

TIM—通用定时器高级定时器

通用/高级定时器的功能 在基本定时器功能的基础上新增功能&#xff1a; 通用定时器有4个独立通道&#xff0c;且每个通道都可以用于下面功能。 &#xff08;1&#xff09;输入捕获&#xff1a;测量输入信号的周期和占空比等。 &#xff08;2&#xff09;输出比较&#xff1a;产…...

【数据结构与算法(C语言)】循环队列图解

目录 1. 前言1.1 普通循环队列假溢出1.1.1 初始化队列1.1.2 插满队列1.1.3 删除元素后&#xff0c;再插入元素 1.2 循环队列1.2.1 插入元素&#xff0c;队列已满1.2.2 将元素J1、J2出列&#xff0c;循环队列又空出两个空间1.2.3 元素J6可以继续入列 2. 存储结构和函数说明2.1 队…...

私域流量转化不济的原因

你是不是也曾感到私域流量的转化一直不如意&#xff1f;让我来告诉你&#xff0c;这六大问题是为什么&#xff0c;以及如何轻松解决它们&#xff0c;提升你的私域流量转化率&#xff01; 1. 问题&#xff1a;目标不明确 你是否常常感到茫然&#xff0c;不知道私域流量应该有何目…...

百万上下文RAG,Agent还能这么玩

❝ 在AI技术飞速发展的今天&#xff0c;我们见证了许多令人惊叹的突破。最近&#xff0c;Qwen2模型的开源引起了广泛的关注&#xff0c;它不仅展示了超越闭源模型的能力&#xff0c;还带来了一个全新的框架——Qwen-Agent。 Qwen-Agent的设计思路虽然与LangChain相似&#xff0…...

【后端开发】服务开发场景之高可用(冗余设计,服务限流,降级熔断,超时重试,性能测试)

【后端开发】服务开发场景之高可用&#xff08;冗余设计&#xff0c;服务限流&#xff0c;降级熔断&#xff0c;超时重试&#xff0c;性能测试&#xff09; 文章目录 序&#xff1a;如何设计一个高可用的系统&#xff1f;可用性的判断指标是什么&#xff1f;哪些情况会导致系统…...

在 Selenium 中更改 User-Agent | 步骤与最佳实践

在 Selenium 中更改 User Agent 是许多网页抓取任务中的关键步骤。它有助于将自动化脚本伪装成常规浏览器&#xff0c;从而避免被网站检测到。本指南将带您了解如何在 Selenium 中更改 Google Chrome 的 User Agent&#xff0c;并提供最佳实践以确保您的网页抓取任务顺利进行。…...

2024酒店IPTV云桌面系统建设方案

Hello大家好&#xff0c;我是点量小芹&#xff0c;这一年多的时间一直在分享实时云渲染像素流相关的内容&#xff0c;今天和大家聊聊酒店IPTV云桌面电视系统解决方案&#xff0c;或者有的朋友也会称之为IPTV服务器。熟悉小芹的朋友知道&#xff0c;IPTV软件系统是我们一直在推的…...

java Thrift TThreadPoolServer 多个processor 的实现

当我们使用Thrift 通信的时候&#xff0c;服务端有时候需要注册多个类&#xff0c;去实现通信&#xff0c;这时候我们就不能再使用单一Processor的方式&#xff0c;就要使用多个Processor&#xff0c;那么如何去实现呢&#xff1f; 多个Process 服务端 public static void m…...

失眠焦虑的解脱之道:找回内心的平静

&#x1f343; 在这个快节奏的时代&#xff0c;失眠与焦虑似乎成了许多人的隐形敌人。每当夜幕降临&#xff0c;它们便悄悄潜入心底&#xff0c;扰乱我们的思绪&#xff0c;让宁静的夜晚变得无比漫长。然而&#xff0c;生活总有办法让我们找回内心的平静&#xff0c;只需稍作调…...

OLED柔性屏的显示效果如何

OLED柔性屏的显示效果非常出色&#xff0c;具有多方面的优势。以下是关于OLED柔性屏显示效果的详细分析&#xff1a; 色彩表现&#xff1a;OLED柔性屏的每个像素都可以独立发光&#xff0c;因此色彩准确性极高。黑色呈现得非常深邃&#xff0c;而亮部则展现出鲜明而生动的细节。…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...