架构设计基础系列:面向对象设计的原则

引言
面向对象设计(Object-Oriented Design,OOD)是软件开发中的重要概念,其核心在于通过对象、类、继承、封装和多态等机制,实现对现实世界问题的抽象和建模。OOD不仅有助于提高代码的可重用性、可维护性和可扩展性,还能有效管理复杂系统中的依赖关系。本文将深入探讨面向对象设计的原则,并通过丰富的Java代码案例进行说明。

1.单一职责原则(Single Responsibility Principle, SRP)
定义
一个类应该只有一个引起它变化的理由,或者说,一个类应该只有一个职责。
代码案例
// 违反单一职责原则
public class UserManager {public void createUser(String name, String email) {// 创建用户逻辑}public void sendEmail(String email, String message) {// 发送邮件逻辑}
}// 遵循单一职责原则
public class UserService {public void createUser(String name, String email) {// 创建用户逻辑}
}public class EmailService {public void sendEmail(String email, String message) {// 发送邮件逻辑}
}

2.开放封闭原则(Open Closed Principle,OCP)
定义
软件实体(类、模块、函数等)应该可以扩展,但是不可修改。
是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而_开放封闭原则_正是对这一目标的最直接体现
代码案例
// 违反开放封闭原则
public class Shape {public void draw() {System.out.println("Drawing Shape");}
}// 需要新增一种形状时,需要修改Shape类或其子类// 遵循开放封闭原则
public abstract class Shape {public abstract void draw();
}public class Circle extends Shape {@Overridepublic void draw() {System.out.println("Drawing Circle");}
}// 可以通过添加新的子类来扩展功能,无需修改现有代码
public class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("Drawing Rectangle");}
}
3.里氏替换原则(Liskov Substitution Principle,LSP)
定义
子类必须能够替换它们的基类而不影响程序的正确性。
其核心思想是:如果S是T的子类型,那么在所有使用T类型的地方,都可以替换成S类型而不会影响程序的正确性。这意味着子类应该能够完全替代其父类,并且在使用时不会出现任何错误或异常。
代码案例
// 违反里氏替换原则
public class Bird {public void fly() {System.out.println("Flying");}
}public class Ostrich extends Bird {@Overridepublic void fly() {throw new UnsupportedOperationException("Ostriches can't fly");}
}// 遵循里氏替换原则
public abstract class FlyingBird {public abstract void fly();
}public class Sparrow extends FlyingBird {@Overridepublic void fly() {System.out.println("Sparrow is flying");}
}// Ostrich类不继承FlyingBird,因为它不能飞

4.接口隔离原则(Interface Segregation Principle, ISP)
定义
客户端不应该被迫依赖于它们不使用的方法;接口应该小而专一,仅包含客户端需要的方法。这样可以减少不必要的依赖,提高系统的灵活性和可维护性。
其核心思想是客户端不应该依赖它不需要的接口,即类之间的依赖关系应建立在最小的接口上,避免设计臃肿的接口。
代码案例
// 违反接口隔离原则
public interface Animal {void eat();void sleep();void breathe(); // 并非所有动物都通过肺呼吸
}// 遵循接口隔离原则
public interface Eater {void eat();
}public interface Sleeper {void sleep();
}public class Dog implements Eater, Sleeper {@Overridepublic void eat() {System.out.println("Dog is eating");}@Overridepublic void sleep() {System.out.println("Dog is sleeping");}
}
5.依赖倒置原则(Dependence Inversion Principle,DIP)
定义
高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
代码案例
// 违反依赖倒置原则
public class HighLevelModule {private LowLevelModule lowLevelModule = new LowLevelModule();public void doSomething() {lowLevelModule.performTask();}
}// 遵循依赖倒置原则
public interface TaskPerformer {void performTask();
}public class LowLevelModule implements TaskPerformer {@Overridepublic void performTask() {System.out.println("Performing low level task");}
}public class HighLevelModule {private TaskPerformer taskPerformer;public HighLevelModule(TaskPerformer taskPerformer) {this.taskPerformer = taskPerformer;}public void doSomething() {taskPerformer.performTask();}
}

6. 组合复用原则(Composite Reuse Principle, CRP)
定义
优先使用对象组合(Composition)而非继承(Inheritance)来实现代码复用,避免因继承层次过深导致的代码僵化。
代码案例
// 使用继承导致脆弱的设计
public class Engine { /* 基础引擎实现 */ }public class Car extends Engine { public void start() { System.out.println("Car starts with engine"); }
} // 新增电动引擎时,继承体系难以扩展
public class ElectricEngine extends Engine { /* 电动引擎实现 */ } // 使用组合替代继承
public interface Engine { void ignite();
} public class GasolineEngine implements Engine { @Override public void ignite() { System.out.println("Igniting gasoline engine"); }
} public class ElectricMotor implements Engine { @Override public void ignite() { System.out.println("Activating electric motor"); }
} public class Car { private final Engine engine; public Car(Engine engine) { this.engine = engine; } public void start() { engine.ignite(); System.out.println("Car started"); }
}

7. 迪米特法则(Law of Demeter, LoD)
定义
一个对象应仅与其直接朋友(即方法参数、成员变量、自身创建的对象)交互,避免“远距离耦合”。
代码案例
// 违反迪米特法则:直接访问深层对象
public class Customer { private Wallet wallet; public Wallet getWallet() { return wallet; }
} public class Wallet { private double balance; public double getBalance() { return balance; }
} // 在外部类中直接调用链式方法
public class PaymentService { public void processPayment(Customer customer) { double balance = customer.getWallet().getBalance(); // 直接依赖Wallet的实现细节 }
} // 遵循迪米特法则:封装间接访问
public class Customer { private Wallet wallet; public double getWalletBalance() { return wallet.getBalance(); }
} public class PaymentService { public void processPayment(Customer customer) { double balance = customer.getWalletBalance(); // 仅依赖Customer的接口 }
}
8. 稳定抽象原则(Stable Abstractions Principle, SAP)
定义
模块的抽象程度应与其稳定性成正比,稳定模块应通过抽象接口提供扩展点。
代码案例
// 稳定的抽象接口
public interface DataRepository { void save(String data);
} // 不稳定的具体实现可独立变化
public class DatabaseRepository implements DataRepository { @Override public void save(String data) { System.out.println("Saving to database: " + data); }
} public class CloudStorageRepository implements DataRepository { @Override public void save(String data) { System.out.println("Uploading to cloud: " + data); }
} // 高层模块依赖抽象,不受底层实现变化影响
public class ReportGenerator { private final DataRepository repository; public ReportGenerator(DataRepository repository) { this.repository = repository; } public void generateReport() { String reportData = "Report content"; repository.save(reportData); }
}

原则的综合应用:设计模式中的体现
面向对象设计原则常通过经典设计模式落地。例如:
策略模式(Strategy Pattern)
- 依赖抽象(DIP),通过接口隔离不同算法(ISP)。
public interface SortingStrategy { void sort(int[] array);
} public class QuickSort implements SortingStrategy { /* 具体实现 */ }
public class MergeSort implements SortingStrategy { /* 具体实现 */ }
装饰器模式(Decorator Pattern)
- 开放封闭原则(OCP)的典型实践,通过组合动态扩展功能。
public interface Coffee { double getCost(); }
public class SimpleCoffee implements Coffee { /* 基础实现 */ } public abstract class CoffeeDecorator implements Coffee { protected Coffee decoratedCoffee; public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; }
} public class MilkDecorator extends CoffeeDecorator { @Override public double getCost() { return decoratedCoffee.getCost() + 0.5; }
}

总结
面向对象设计原则并非孤立的教条,而是一个具有正交性的有机体系。其协同作用体现在三个关键维度:
-
模块化治理
通过单一职责原则与接口隔离原则的正交组合,以声明式设计(Declarative Design)界定模块边界,消除职责扩散(Responsibility Diffusion)风险。 -
架构演化控制
基于开放封闭原则的扩展性承诺与依赖倒置原则的抽象约束,形成面向变更的免疫系统(Change-Resistant Architecture),有效控制系统熵增。 -
耦合解耦工程
通过组合复用原则的对象委托机制和迪米特法则的局部性原理(Locality Principle),实现耦合度的亚线性增长(Sublinear Growth)。
开发者应在技术债务控制与架构演进速度间建立动态平衡:
- 在敏捷开发的原型验证阶段,可对里氏替换原则实施有限豁免(如通过@Deprecated注解标记风险边界)
- 在核心业务域模块中,则需通过稳定抽象原则构建防腐层(Anti-Corruption Layer),保障领域模型纯度
架构启示:优秀设计不是原则的简单叠加,而是基于系统上下文(System Context)的拓扑重构(Topological Refactoring)。
相关文章:
架构设计基础系列:面向对象设计的原则
引言 面向对象设计(Object-Oriented Design,OOD)是软件开发中的重要概念,其核心在于通过对象、类、继承、封装和多态等机制,实现对现实世界问题的抽象和建模。OOD不仅有助于提高代码的可重用性、可维护性和可扩展性&a…...
UE5学习笔记 FPS游戏制作35 使用.csv配置文件
文章目录 导入.csv要求首先创建一个结构体导入配置文件读取配置 导入 .csv要求 第一行必须包含标题 第一列的内容必须不能重复,因为第一列会被当成行的名字,在数据处理中发挥类似于字典的key的作用 当前的配置文件内容如下 首先创建一个结构体 结构…...
嵌入式单片机ADC数模转换的基本方法
第一:模数转换的概述 1:模数转换的概念 一般在电路中,信号分为两种,一种是模拟信号,一种是数字信号,绝大多数传感器采集的都是模拟信号,如温度、湿度、烟雾浓度、亮度.......,但是对于计算机需要处理的数字信号,那就需要利用电路把模拟信号转换为数字信号,这个转换的…...
Web数据挖掘及其在电子商务中的研究与应用
标题:Web数据挖掘及其在电子商务中的研究与应用 内容:1.摘要 随着互联网的飞速发展,Web数据呈现出爆炸式增长,电子商务领域更是积累了海量数据。在此背景下,对Web数据进行有效挖掘并应用于电子商务具有重要意义。本研究旨在探索Web数据挖掘技…...
01-Docker 安装
1、安装环境介绍 安装环境:Linux CentOS 7 本安装教程参考Docker官方文档,地址如下:https://docs.docker.com/engine/install/centos/ 2、卸载旧版docker 首先如果系统中已经存在旧的Docker,则先卸载: yum remove do…...
Redis 的缓存雪崩、击穿、穿透及其解决办法
文章目录 Redis 的缓存雪崩、击穿、穿透及其解决办法缓存雪崩解决办法 缓存击穿解决方案 缓存穿透解决方案 Redis 的缓存雪崩、击穿、穿透及其解决办法 本篇文章回顾 Redis 当中缓存崩溃、击穿、穿透现象以及相应的解决办法,主要的参考资料是:https://w…...
使用 Selenium 构建简单高效的网页爬虫
在当今数据驱动的世界中,网络爬虫已成为获取网络信息的重要工具。本文将介绍如何使用 Python 和 Selenium 构建一个简单而高效的网页爬虫,该爬虫能够处理现代网站的动态内容,支持代理设置和用户配置文件。 为什么选择 Selenium? …...
性能比拼: Pingora vs Nginx (My NEW Favorite Proxy)
本内容是对知名性能评测博主 Anton Putra Pingora vs Nginx Performance Benchmark: My NEW Favorite Proxy! 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 介绍 在本视频中,我们将对比 Nginx 和 Pingora(一个用于构建网络服务的 Rust 框架…...
Ranger一分钟
简介 Ranger Admin:Web UIPolicy Admin Tool:定义和管理策略的模块Ranger Plugins:HDFS、Hive、HBase、Kafka、Storm、YARNRanger UserSync: LDAP、Active DirectoryRanger KMS:管理和保护数据加密的密钥 加密密钥管理…...
STM32单片机入门学习——第5节: [3-1]GPIO输出
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.01 STM32开发板学习——第5节: [3-1]GPIO输出 前言开发板说明引用解答和…...
Open GL ES ->模型矩阵、视图矩阵、投影矩阵等变换矩阵数学推导以及方法接口说明
Open GL ES 变换矩阵详解 一、坐标空间变换流程 局部空间 ->Model Matrix(模型矩阵)-> 世界空间 世界空间->View Matrix(视图矩阵)->观察空间 观察空间 ->Projection Matrix(投影矩阵)->裁剪空间 裁剪空间 ->ViewPort Transform(视口变换)>屏幕空间 …...
AI提示词:自然景区智能客服
提示描述 专为自然景区游客设计的智能客服系统,旨在通过人工智能技术提供实时、准确的景区信息、游览建议、安全提示和服务支持,提升游客的体验质量和满意度。 提示词 # Role: 自然景区智能客服## Profile: - Author: xxx - Version: 1.0 - Language: …...
c#的反射和特性
在 C# 中,反射(Reflection)和特性(Attributes)是两个强大的功能,它们在运行时提供元编程能力,广泛用于框架开发、对象映射和动态行为扩展。以下是对它们的详细介绍,包括定义、用法、…...
智能体项目实现AI对话流式返回效果
1、智能体项目里与AI大模型对话的时候,需要从后端的流式接口里取数据并实现打字机渲染效果。这里涉及到 Markdown 格式的渲染,所以需要配合 marked.js 实现,安装 marked.js : npm install marked 引用: import { ma…...
定时任务(python)
介绍 🧩 什么是“定时任务”? 定时任务,就是按照设定的时间间隔或时间点自动执行某些操作。比如: • 每天早上8点发通知 • 每隔10秒采集一次数据 • 每小时清理一次缓存相关使用 ✅ 最简单的方式:while True tim…...
Python学习第二十七天
yield关键字 yield关键字扮演着核心角色,主要用于处理异步数据流和请求调度。 主要作用 生成器函数:将方法转换为生成器,可以逐步产生结果而不需要一次性返回所有数据 异步处理:支持Scrapy的异步架构,提高爬取效率 …...
Docker Compose 启动jar包项目
参考文章安装Docker和Docker Compose 点击跳转 配置 创建一个文件夹存放项目例如mydata mkdir /mydata上传jar包 假设我的jar包名称为goudan.jar 编写dockerfile文件 vim app-dockerfile按键盘上的i进行编辑 # 使用jdk8 FROM openjdk:8-jre# 设置时区 上海 ENV TZAsia/Sh…...
pytorch中dataloader自定义数据集
前言 在深度学习中我们需要使用自己的数据集做训练,因此需要将自定义的数据和标签加载到pytorch里面的dataloader里,也就是自实现一个dataloader。 数据集处理 以花卉识别项目为例,我们分别做出图片的训练集和测试集,训练集的标…...
SQL Server:触发器
在 SQL Server Management Studio (SSMS) 中查看数据库触发器的方法如下: 方法一:通过对象资源管理器 连接到 SQL Server 打开 SSMS,连接到目标数据库所在的服务器。 定位到数据库 在左侧的 对象资源管理器 中,展开目标数据库&a…...
标题:利用 Rork 打造定制旅游计划应用程序:一步到位的指南
引言: 在数字化时代,旅游计划应用程序已经成为旅行者不可或缺的工具。但开发一个定制的旅游应用可能需要耗费大量时间与精力。好消息是,Rork 提供了一种快捷且智能的解决方案,让你能轻松实现创意。以下是使用 Rork 创建一个定制旅…...
WebSocket原理详解(二)
WebSocket原理详解(一)-CSDN博客 目录 1.WebSocket协议的帧数据详解 1.1.帧结构 1.2.生成数据帧 2.WebSocket协议控制帧结构详解 2.1.关闭帧 2.2.ping帧 2.3.pong帧 3.WebSocket心跳机制 1.WebSocket协议的帧数据详解 1.1.帧结构 WebSocket客户端与服务器通信的最小单…...
计算声音信号波形的谐波
计算声音信号波形的谐波 1、效果 2、定义 在振动分析中,谐波通常指的是信号中频率是基频整数倍的成分。基频是振动的主要频率,而谐波可能由机械系统中的非线性因素引起。 3、流程 1. 信号生成:生成或加载振动信号数据(模拟或实际数据)。 2. 预处理:预处理数据,如去噪…...
RepoReporter 仿照`TortoiseSVN`项目监视器,能够同时支持SVN和Git仓库
RepoReporter 项目地址 RepoReporter 一个仓库监视器,仿照TortoiseSVN项目监视器,能够同时支持SVN和Git仓库。 工作和学习会用到很多的仓库,每天都要花费大量的时间在频繁切换文件夹来查看日志上。 Git 的 GUI 工具琳琅满目,Git…...
C++多线程的性能优化
高效线程池设计与工作窃取算法实现解析 1. 引言 现代多核处理器环境下,线程池技术是提高程序并发性能的重要手段。本文解析一个采用工作窃取(Work Stealing)算法的高效线程池实现,通过详细代码分析和性能测试展示其优势。 2. 线程池核心设计 2.1 类结…...
【TS学习】(19)TS中的类
在 TypeScript 中,类(Class) 是面向对象编程的核心结构,用于封装数据和行为。TypeScript 的类继承了 JavaScript 的类特性,并增加了类型系统和高级功能的支持(如访问修饰符、存取器和装饰器)。 …...
UI设计系统:如何构建一套高效的设计规范?
UI设计系统:如何构建一套高效的设计规范? 1. 色彩系统的建立与应用 色彩系统是设计系统的基础之一,它不仅影响界面的整体美感,还对用户体验有着深远的影响。首先,设计师需要定义主色调、辅助色和强调色,并…...
深度学习--softmax回归
回归可以用于预测多少的问题,预测房屋出售价格,棒球队可能获胜的的常数或者患者住院的天数。 事实上,我们也对分类问题感兴趣,不是问 多少,而是问哪一个 1 某个电子邮件是否属于垃圾邮件 2 某个用户可能注册还是不注册…...
【计算机网络】记录一次校园网无法上网的解决方法
问题现象 环境:实训室教室内时间:近期突然出现 (推测是学校在施工,部分设备可能出现问题)症状: 连接校园网 SWXY-WIFI 后: 连接速度极慢偶发无 IP 分配(DHCP 失败)即使分…...
Java关于抽象类和抽象方法
引入抽象: 在之前把不同类中的共有成员变量和成员方法提取到父类中叫做继承。然后对于成员方法在不同子类中有不同的内容,对这些方法重新书写叫做重写;不过如果有的子类没有用继承的方法,用别的名字对这个方法命名的话࿰…...
第二十一章:Python-Plotly库实现数据动态可视化
Plotly是一个强大的Python可视化库,支持创建高质量的静态、动态和交互式图表。它特别擅长于绘制三维图形,能够直观地展示复杂的数据关系。本文将介绍如何使用Plotly库实现函数的二维和三维可视化,并提供一些优美的三维函数示例。资源绑定附上…...
