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

面向对象设计的五大原则(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; // 正方形的宽和高必须相等}
}

虽然 SquareRectangle 的子类,但由于 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 类,它依赖于具体的 BackendDeveloperFrontendDeveloper 类。

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 原则)

面向对象设计的五大原则&#xff08;SOLID 原则&#xff09;是指导我们设计可维护、灵活且易扩展的面向对象系统的核心准则。这些原则帮助开发者避免常见的设计陷阱&#xff0c;使代码更具可读性和可维护性。 0.设计原则和设计模式的关系 设计原则&#xff08;Design Princip…...

Python和MATLAB及C++信噪比导图(算法模型)

&#x1f3af;要点 视频图像修复模数转换中混合信号链噪音测量频谱计算和量化周期性视觉刺激脑电图高斯噪声的矩形脉冲 总谐波失真 周期图功率谱密度各种心率失常检测算法胶体悬浮液跟踪检测计算交通监控摄像头图像噪音计算 Python信噪比 信噪比是科学和工程中使用的一种测…...

App及web反编译方案

APP反编译代码的工具下载&#xff1a; 下载地址&#xff1a;APK逆向三件套apktool-2.9.3.jar&#xff0c;dex2jar-2.0.zip&#xff0c;jd-gui-windows-1.6.6资源-CSDN文库 》dex2jar: 把dex文件转成jar文件 》 jd-gui: 这个工具用于将jar文件转换成java代码 》APKTool: 首先把…...

学成在线练习(HTML+CSS)

准备工作 项目目录 内部包含当前网站的所有素材&#xff0c;包含 HTML、CSS、图片、JavaScript等等 1.由于元素具有一些默认样式&#xff0c;可能是我们写网页过程中根本不需要的&#xff0c;所有我们可以在写代码之前就将其清除 base.css /* 基础公共样式&#xff1a;清除…...

istio中使用serviceentry结合egressgateway实现多版本路由

假设有一个外部服务&#xff0c;外部服务ip为&#xff1a;10.10.102.90&#xff0c;其中32033为v1版本&#xff0c;32034为v2版本。 现在需要把这个服务引入到istio中&#xff0c;使用egressgateway转发访问该服务的流量&#xff0c;并且需要实现多版本路由&#xff0c;使得he…...

Java项目——苍穹外卖(二)

Redis 简介 Redis是一个基于内存的key-value结构数据库 基于内存存储&#xff0c;读写性能高适合存储热点数据&#xff08;热点商品、资讯、新闻&#xff09;企业应用广泛 基础操作 启动 在redis安装目录中打开cmd&#xff0c;输入如上图指令即可启动&#xff0c;按下crtl…...

【Python日志功能】三.日志记录方法与多模块日志

文章目录 相关链接第三篇&#xff1a;日志记录方法与多模块日志1 基本日志记录方法2 在多个模块中使用日志3 文章总结 相关链接 【Python日志功能】一.日志基础与基本配置【Python日志功能】二.高级配置与日志处理器【Python日志功能】三.日志记录方法与多模块日志官方文档&am…...

在pycharm终端中运行pip命令安装模块时,出现了“你要如何打开这个文件”弹出窗口,是什么状况?

这种情况发生在Windows系统上&#xff0c;当在PyCharm终端中运行pip命令安装模块时&#xff0c;如果系统无法确定要使用哪个程序打开该文件&#xff0c;就会出现“你要如何打开这个文件”弹出窗口。 解决方法是&#xff1a; 选择“查找一个应用于此文件”的选项。在弹出的窗口…...

Axure多人协调的方式

当系统有多个模块&#xff0c;又由不同的产品经理负责设计&#xff0c;如何进行协调&#xff1f; 尝试过的方法 1&#xff09;搭建Axure私服&#xff0c;用Axure的私服进行一个RP文件多人协同编辑&#xff1b; 2&#xff09;用SVN管理RP文件&#xff0c;每次都要合并。 以上…...

【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程

【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转on…...

React学习笔记(1.0)

在使用vite创建react时&#xff0c;有一个语言选项&#xff0c;就是typescript-SWC&#xff0c;这里介绍一下SWC。 SWC&#xff1a;可扩展的Rust的平台&#xff0c;用于下一代快速开发工具&#xff0c;SWC比Babel快20倍。 简单来说&#xff0c;就是用于格式转换的&#xff0c…...

Axure RP实战:打造高效图形旋转验证码

Axure RP实战&#xff1a;打造高效图形旋转验证码 在数字产品设计的海洋中&#xff0c;验证码环节往往是用户交互体验的细微之处&#xff0c;却承载着验证用户身份的重要任务。 传统的文本验证码虽然简单直接&#xff0c;但随着用户需求的提高和设计趋势的发展&#xff0c;它…...

101012分页属性

4k页面 P&#xff08;有效位&#xff09;&#xff1a;1有效&#xff0c;0无效 R/W&#xff08;读写位&#xff09;&#xff1a;1可读可写&#xff0c;0可读 U/S&#xff08;权限位&#xff09;&#xff1a;1(User)&#xff0c;0(System) A&#xff08;物理页访问位&#xff…...

从0-1 用AI做一个赚钱的小红书账号(不是广告不是广告)

大家好&#xff0c;我是胡广&#xff01;是不是被标题吸引过来的呢&#xff1f;是不是觉得自己天赋异禀&#xff0c;肯定是那万中无一的赚钱天才。哈哈哈&#xff0c;我告诉你&#xff0c;你我皆是牛马&#xff0c;不要老想着突然就成功了&#xff0c;一夜暴富了&#xff0c;瞬…...

【Kubernetes】常见面试题汇总(十七)

目录 51.简述 Kubernetes 网络策略&#xff1f; 52.简述 Kubernetes 网络策略原理&#xff1f; 53.简述 Kubernetes 中 flannel 的作用&#xff1f; 54.简述 Kubernetes Calico 网络组件实现原理&#xff1f; 51.简述 Kubernetes 网络策略&#xff1f; - 为实现细粒度的容器…...

Vue 3 中动态赋值 ref 的应用

引言 Vue 3 引入了许多新特性&#xff0c;其中之一便是 Composition API。Composition API 提供了一种新的编程范式&#xff0c;使开发者能够更灵活地组织和复用逻辑。其中 ref 是一个核心概念&#xff0c;它允许我们在组件内部声明响应式的状态。本文将探讨如何在 Vue 3 中使…...

Spring Boot-应用启动问题

在使用 Spring Boot 进行开发时&#xff0c;应用启动问题是开发人员经常遇到的挑战之一。通过有效排查和解决这些问题&#xff0c;可以提高应用的稳定性和可靠性。 1. Spring Boot 启动问题的常见表现 Spring Boot 应用启动失败通常表现为以下几种情况&#xff1a; 应用启动…...

深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)

在 Linux 系统中&#xff0c;网络命名空间&#xff08;Network Namespaces&#xff09;是一种强大的功能&#xff0c;它允许系统管理员和开发者隔离网络资源&#xff0c;使得每个命名空间都拥有独立的网络协议栈。这种隔离机制不仅用于容器技术如 Docker&#xff0c;也是网络安…...

C++ 科目二 [const_cast]

基础数据类型 const_cast 仅仅是深层拷贝改变&#xff0c;而不是改动之前的值 如果需要使用改动后的值&#xff0c;需要通过指针或者引用来间接使用 const int n 5; const string s "MyString";// cosnt_cast 针对指针&#xff0c;引用&#xff0c;this指针 // co…...

【电脑组装】✈️从配置拼装到安装系统组装自己的台式电脑

目录 &#x1f378;前言 &#x1f37b;一、台式电脑基本组成 &#x1f37a;二、组装 &#x1f379;三、安装系统 &#x1f44b;四、系统设置 &#x1f440;五、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;上篇文章分享了在平时开发的时候遇到的一种项目整合情况&…...

别再乱用npm install了!手把手教你用npx only-allow为项目指定包管理器(支持pnpm/yarn/npm)

用only-allow统一团队包管理器&#xff1a;从配置到CI的全流程指南 你是否曾经在拉取一个新项目后&#xff0c;面对npm install、yarn还是pnpm i的抉择感到困惑&#xff1f;或者更糟的是&#xff0c;团队成员混用不同包管理器导致node_modules结构不一致&#xff0c;引发各种诡…...

用数字逻辑门复刻柏林钟:从二进制编码到硬件实现

1. 项目概述&#xff1a;用数字电路复刻“柏林钟”作为一个在柏林长大的孩子&#xff0c;我从小就对库达姆大街上的那座“柏林钟”着迷。它不像传统时钟那样用指针或数字告诉你时间&#xff0c;而是通过几排不同颜色的发光方块&#xff0c;以一种近乎艺术的方式呈现时间。这种独…...

如何在macOS上免费解锁QQ音乐加密文件:完整指南

如何在macOS上免费解锁QQ音乐加密文件&#xff1a;完整指南 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac&#xff0c;qmc0,qmc3转mp3, mflac,mflac0等转flac)&#xff0c;仅支持macOS&#xff0c;可自动识别到QQ音乐下载目录&#xff0c;默认转换结果…...

别再用SonarQube凑数了!DeepSeek原生圈复杂度引擎的6大颠覆性能力(含GitHub私有部署密钥)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek圈复杂度分析的底层原理与范式革命 DeepSeek圈复杂度分析并非传统McCabe度量的简单复刻&#xff0c;而是基于控制流图&#xff08;CFG&#xff09;动态重构与语义感知路径裁剪的双重机制构建的新范式。…...

文件-语言-系统:基础IO-2.0——IO重定向接口,语言层缓冲区,系统级缓冲区。内核级分析!

bit::Shadow✧(≖ ◡ ≖✿ 目录 重定向接口dup2() ">" ">>" "<" 函数原型 输出重定向1和2的使用 文件描述符表 ./a.out运行&#xff1a; "./a.out >"默认重定向是fd 1 合并标准输入输出 缓冲区 什么是缓冲…...

保姆级教程:Windows系统下Arcgis 10.2从下载、安装到汉化一次搞定(附常见License启动失败解决方案)

Windows系统下Arcgis 10.2完整安装与汉化实战指南第一次接触Arcgis的新手往往会被复杂的安装流程和神秘的License Manager搞得晕头转向。作为一款功能强大的地理信息系统软件&#xff0c;Arcgis在科研、城市规划、环境监测等领域有着广泛应用&#xff0c;但它的安装过程确实会让…...

什么情况下会核销贷款

贷款核销的核心前提是&#xff1a;贷款被认定为 “损失类” 且经 “穷尽追偿” 仍无法收回&#xff0c;银行按监管与会计规则从账面冲销&#xff0c;但债权不消灭、仍可追偿。一、核心认定条件&#xff08;满足其一即可&#xff09;破产 / 注销 / 吊销&#xff1a;借款人和担保…...

氘可来昔替尼常见副作用为鼻咽炎头痛及腹泻,如何应对

任何口服药物的临床价值&#xff0c;都必须在疗效与安全性的天平上找到精准的平衡点。氘可来昔替尼以PASI 75应答率的全面胜出证明了自己在银屑病治疗中的卓越地位&#xff0c;而其不良反应谱同样经过了严苛的临床验证。鼻咽炎、头痛和腹泻构成了这款药物最需关注的三大安全信号…...

在Node.js服务中集成Taotoken实现稳定的大模型能力调用

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在Node.js服务中集成Taotoken实现稳定的大模型能力调用 对于需要在后端服务中集成AI功能的Node.js开发者而言&#xff0c;直接对接…...

基于PGA2311的树莓派Hi-Fi模拟音量控制器设计与实现

1. 项目概述&#xff1a;为树莓派DAC打造的高品质模拟音量控制器玩过树莓派音频播放器的朋友都知道&#xff0c;用上像PCM1794A这类高性能DAC芯片后&#xff0c;音质确实能上一个台阶&#xff0c;但有个不大不小的麻烦&#xff1a;这类芯片本身不带音量控制。软件调音量&#x…...