面向对象设计的五大原则(SOLID 原则)
面向对象设计的五大原则(SOLID 原则)是指导我们设计可维护、灵活且易扩展的面向对象系统的核心准则。这些原则帮助开发者避免常见的设计陷阱,使代码更具可读性和可维护性。
0.设计原则和设计模式的关系
设计原则(Design Principles)指的是抽象性比较高、编程都应该遵守的原则,对应的设计模式(Design Pattens)是解决具体场景下特定问题的套路,这里要对两个概念进行区分。换句话说,设计模式要遵循设计原则。
1. 单一职责原则(SRP - Single Responsibility Principle)
定义:一个类应该只有一个引起它变化的原因,即一个类只负责一个功能或职责。
示例:
假设我们有一个类负责处理用户信息,同时负责生成用户报告。这样当用户信息或报告格式发生变化时,都会影响到同一个类,违背了 SRP。
class User {String name;String email;public void saveUser() {// 保存用户信息到数据库}public void generateUserReport() {// 生成用户报告}
}
改进:将用户管理和报告生成拆分为两个类。
class User {String name;String email;public void saveUser() {// 保存用户信息到数据库}
}class UserReportGenerator {public void generateUserReport(User user) {// 生成用户报告}
}
这样,如果用户管理和报告生成的逻辑变更,它们只会影响各自相关的类。
2. 开放封闭原则(OCP - Open/Closed Principle)
定义:软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。即当系统需求变化时,应该通过扩展类的行为,而不是修改已有的类来实现。
示例:
假设我们有一个用于处理图形的类,包含绘制不同图形的逻辑。
class GraphicEditor {public void drawShape(Shape s) {if (s instanceof Circle) {drawCircle((Circle) s);} else if (s instanceof Square) {drawSquare((Square) s);}}private void drawCircle(Circle c) {// 绘制圆形}private void drawSquare(Square s) {// 绘制方形}
}
如果需要支持绘制新形状,例如三角形,就需要修改 drawShape 方法,违反了 OCP。
改进:通过抽象类或接口实现扩展性。
abstract class Shape {public abstract void draw();
}class Circle extends Shape {@Overridepublic void draw() {// 绘制圆形}
}class Square extends Shape {@Overridepublic void draw() {// 绘制方形}
}class GraphicEditor {public void drawShape(Shape s) {s.draw();}
}
现在如果要支持新形状,只需要添加新类,而不需要修改已有代码。
3. 里氏替换原则(LSP - Liskov Substitution Principle)
定义:子类对象必须能够替换其父类对象,程序的行为应该保持不变。即子类应当完整实现父类的行为,而不应违背父类的契约。
示例:
假设我们有一个 Rectangle(矩形)类,后来引入了 Square(正方形)作为它的子类。
class Rectangle {protected int width;protected int height;public void setWidth(int width) {this.width = width;}public void setHeight(int height) {this.height = height;}
}class Square extends Rectangle {@Overridepublic void setWidth(int width) {this.width = width;this.height = width; // 正方形的宽和高必须相等}@Overridepublic void setHeight(int height) {this.height = height;this.width = height; // 正方形的宽和高必须相等}
}
虽然 Square 是 Rectangle 的子类,但由于 Square 违反了矩形的宽高独立性,它无法正确替换 Rectangle。
改进:避免继承错误的层次关系。
class Rectangle {protected int width;protected int height;public void setWidth(int width) {this.width = width;}public void setHeight(int height) {this.height = height;}
}class Square {private int sideLength;public void setSideLength(int sideLength) {this.sideLength = sideLength;}
}
正方形和矩形应该是独立的类,不应通过继承关系来实现。
4. 接口隔离原则(ISP - Interface Segregation Principle)
定义:一个类不应该依赖于它不需要的接口。即接口应该尽量小而精简,不要强迫实现类去实现无关的方法。
示例:
假设有一个大型接口 Worker,它定义了很多不同类型工人的职责。
interface Worker {void work();void eat();
}class Developer implements Worker {@Overridepublic void work() {// 开发代码}@Overridepublic void eat() {// 吃午餐}
}class Robot implements Worker {@Overridepublic void work() {// 执行任务}@Overridepublic void eat() {// 机器人不需要吃饭}
}
Robot 需要实现 eat 方法,但实际上并不需要这个功能,违反了 ISP。
改进:将接口分割成多个小接口。
interface Workable {void work();
}interface Eatable {void eat();
}class Developer implements Workable, Eatable {@Overridepublic void work() {// 开发代码}@Overridepublic void eat() {// 吃午餐}
}class Robot implements Workable {@Overridepublic void work() {// 执行任务}
}
现在 Robot 只需实现 Workable 接口,不再被迫实现与自己无关的功能。
5. 依赖倒置原则(DIP - Dependency Inversion Principle)
定义:高层模块不应该依赖于低层模块,二者都应该依赖于抽象。换句话说,依赖于抽象,而不是具体实现。
示例:
假设我们有一个 Developer 类,它依赖于具体的 BackendDeveloper 和 FrontendDeveloper 类。
class BackendDeveloper {public void writeJava() {// 编写 Java 代码}
}class FrontendDeveloper {public void writeJavaScript() {// 编写 JavaScript 代码}
}class Project {private BackendDeveloper backendDeveloper = new BackendDeveloper();private FrontendDeveloper frontendDeveloper = new FrontendDeveloper();public void implement() {backendDeveloper.writeJava();frontendDeveloper.writeJavaScript();}
}
Project 类直接依赖于具体的开发者实现类,违反了 DIP。
改进:通过抽象接口进行依赖倒置。
interface Developer {void writeCode();
}class BackendDeveloper implements Developer {@Overridepublic void writeCode() {// 编写 Java 代码}
}class FrontendDeveloper implements Developer {@Overridepublic void writeCode() {// 编写 JavaScript 代码}
}class Project {private List<Developer> developers;public Project(List<Developer> developers) {this.developers = developers;}public void implement() {for (Developer developer : developers) {developer.writeCode();}}
}
现在 Project 类依赖于 Developer 接口,而不是具体的开发者实现类,符合 DIP 原则。
总结:
- SRP:一个类只负责一件事。
- OCP:类应该通过扩展而非修改来应对需求变化。
- LSP:子类可以替代父类,不改变程序行为。
- ISP:接口应该小而精,避免无关功能。
- DIP:高层模块依赖于抽象而非具体实现。
相关文章:
面向对象设计的五大原则(SOLID 原则)
面向对象设计的五大原则(SOLID 原则)是指导我们设计可维护、灵活且易扩展的面向对象系统的核心准则。这些原则帮助开发者避免常见的设计陷阱,使代码更具可读性和可维护性。 0.设计原则和设计模式的关系 设计原则(Design Princip…...
Python和MATLAB及C++信噪比导图(算法模型)
🎯要点 视频图像修复模数转换中混合信号链噪音测量频谱计算和量化周期性视觉刺激脑电图高斯噪声的矩形脉冲 总谐波失真 周期图功率谱密度各种心率失常检测算法胶体悬浮液跟踪检测计算交通监控摄像头图像噪音计算 Python信噪比 信噪比是科学和工程中使用的一种测…...
App及web反编译方案
APP反编译代码的工具下载: 下载地址:APK逆向三件套apktool-2.9.3.jar,dex2jar-2.0.zip,jd-gui-windows-1.6.6资源-CSDN文库 》dex2jar: 把dex文件转成jar文件 》 jd-gui: 这个工具用于将jar文件转换成java代码 》APKTool: 首先把…...
学成在线练习(HTML+CSS)
准备工作 项目目录 内部包含当前网站的所有素材,包含 HTML、CSS、图片、JavaScript等等 1.由于元素具有一些默认样式,可能是我们写网页过程中根本不需要的,所有我们可以在写代码之前就将其清除 base.css /* 基础公共样式:清除…...
istio中使用serviceentry结合egressgateway实现多版本路由
假设有一个外部服务,外部服务ip为:10.10.102.90,其中32033为v1版本,32034为v2版本。 现在需要把这个服务引入到istio中,使用egressgateway转发访问该服务的流量,并且需要实现多版本路由,使得he…...
Java项目——苍穹外卖(二)
Redis 简介 Redis是一个基于内存的key-value结构数据库 基于内存存储,读写性能高适合存储热点数据(热点商品、资讯、新闻)企业应用广泛 基础操作 启动 在redis安装目录中打开cmd,输入如上图指令即可启动,按下crtl…...
【Python日志功能】三.日志记录方法与多模块日志
文章目录 相关链接第三篇:日志记录方法与多模块日志1 基本日志记录方法2 在多个模块中使用日志3 文章总结 相关链接 【Python日志功能】一.日志基础与基本配置【Python日志功能】二.高级配置与日志处理器【Python日志功能】三.日志记录方法与多模块日志官方文档&am…...
在pycharm终端中运行pip命令安装模块时,出现了“你要如何打开这个文件”弹出窗口,是什么状况?
这种情况发生在Windows系统上,当在PyCharm终端中运行pip命令安装模块时,如果系统无法确定要使用哪个程序打开该文件,就会出现“你要如何打开这个文件”弹出窗口。 解决方法是: 选择“查找一个应用于此文件”的选项。在弹出的窗口…...
Axure多人协调的方式
当系统有多个模块,又由不同的产品经理负责设计,如何进行协调? 尝试过的方法 1)搭建Axure私服,用Axure的私服进行一个RP文件多人协同编辑; 2)用SVN管理RP文件,每次都要合并。 以上…...
【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程
【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转on…...
React学习笔记(1.0)
在使用vite创建react时,有一个语言选项,就是typescript-SWC,这里介绍一下SWC。 SWC:可扩展的Rust的平台,用于下一代快速开发工具,SWC比Babel快20倍。 简单来说,就是用于格式转换的,…...
Axure RP实战:打造高效图形旋转验证码
Axure RP实战:打造高效图形旋转验证码 在数字产品设计的海洋中,验证码环节往往是用户交互体验的细微之处,却承载着验证用户身份的重要任务。 传统的文本验证码虽然简单直接,但随着用户需求的提高和设计趋势的发展,它…...
101012分页属性
4k页面 P(有效位):1有效,0无效 R/W(读写位):1可读可写,0可读 U/S(权限位):1(User),0(System) A(物理页访问位ÿ…...
从0-1 用AI做一个赚钱的小红书账号(不是广告不是广告)
大家好,我是胡广!是不是被标题吸引过来的呢?是不是觉得自己天赋异禀,肯定是那万中无一的赚钱天才。哈哈哈,我告诉你,你我皆是牛马,不要老想着突然就成功了,一夜暴富了,瞬…...
【Kubernetes】常见面试题汇总(十七)
目录 51.简述 Kubernetes 网络策略? 52.简述 Kubernetes 网络策略原理? 53.简述 Kubernetes 中 flannel 的作用? 54.简述 Kubernetes Calico 网络组件实现原理? 51.简述 Kubernetes 网络策略? - 为实现细粒度的容器…...
Vue 3 中动态赋值 ref 的应用
引言 Vue 3 引入了许多新特性,其中之一便是 Composition API。Composition API 提供了一种新的编程范式,使开发者能够更灵活地组织和复用逻辑。其中 ref 是一个核心概念,它允许我们在组件内部声明响应式的状态。本文将探讨如何在 Vue 3 中使…...
Spring Boot-应用启动问题
在使用 Spring Boot 进行开发时,应用启动问题是开发人员经常遇到的挑战之一。通过有效排查和解决这些问题,可以提高应用的稳定性和可靠性。 1. Spring Boot 启动问题的常见表现 Spring Boot 应用启动失败通常表现为以下几种情况: 应用启动…...
深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)
在 Linux 系统中,网络命名空间(Network Namespaces)是一种强大的功能,它允许系统管理员和开发者隔离网络资源,使得每个命名空间都拥有独立的网络协议栈。这种隔离机制不仅用于容器技术如 Docker,也是网络安…...
C++ 科目二 [const_cast]
基础数据类型 const_cast 仅仅是深层拷贝改变,而不是改动之前的值 如果需要使用改动后的值,需要通过指针或者引用来间接使用 const int n 5; const string s "MyString";// cosnt_cast 针对指针,引用,this指针 // co…...
【电脑组装】✈️从配置拼装到安装系统组装自己的台式电脑
目录 🍸前言 🍻一、台式电脑基本组成 🍺二、组装 🍹三、安装系统 👋四、系统设置 👀五、章末 🍸前言 小伙伴们大家好,上篇文章分享了在平时开发的时候遇到的一种项目整合情况&…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...
2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
