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

【架构】面向对象六大设计原则

面向对象六大设计原则

在软件开发中,代码量随着项目规模的增长而膨胀,如果没有良好的架构设计,项目就会变得难以维护。面向对象六大设计原则 是指导我们编写高质量代码的重要法则。它们并不是晦涩的规则,而是帮助我们在实际开发中 降低耦合、增强复用、提高可维护性 的经验总结。

单一职责原则 SRP

一个类只做一件事
如果一个类承担了过多功能,它的变化就会牵一发而动全身。例如一个类既负责用户登录,又负责日志记录,那么登录逻辑变更时,日志模块也可能被意外影响。
我们应尽量把不同的功能拆分成多个独立的类或模块,使得每个类有且只有一个导致它变化的原因。

UserService 既处理业务又负责日志与持久化

public class UserService {public void register(String name) {// 业务逻辑System.out.println("Validate and create user: " + name);// 持久化System.out.println("Saving user to DB: " + name);// 日志System.out.println("Log: user registered " + name);}
}

职责拆分

public class UserService {private final UserRepository userRepository;private final Logger logger;public UserService(UserRepository userRepository, Logger logger) {this.userRepository = userRepository;this.logger = logger;}public void register(String name) {userRepository.save(new User(name));logger.log("User registered: " + name);}
}interface UserRepository {void save(User user);
}interface Logger {void log(String message);
}class User { String name;User(String name) { this.name = name; }
}

开闭原则 OCP

对扩展开放,对修改关闭
我们希望在新增功能时,不要去修改已有的类,而是通过扩展(继承、接口、多态等方式)来实现。这样做的好处是,原有逻辑不被破坏,风险大大降低。
假如有一个图形绘制系统,最开始只有 Circle,后来要增加 Rectangle。如果按照开闭原则,我们不去修改绘制类内部,而是通过新增 Rectangle 类并实现绘制接口即可。

支付方法硬编码到 PaymentProcessor

public class PaymentProcessor {public void pay(String type, double amount) {if ("WeChat".equals(type)) {} else if ("AliPay".equals(type)) {}}
}

对新增支付方式开放实现类

public interface PaymentStrategy {void pay(double amount);
}public class WeChatPay implements PaymentStrategy {public void pay(double amount) { }
}public class AliPay implements PaymentStrategy {public void pay(double amount) { }
}public class PaymentProcessor {public void process(PaymentStrategy strategy, double amount) {strategy.pay(amount);}
}

用策略扩展新支付方式时,不需要改 PaymentProcessor,只需新增实现类。


里氏替换原则 LSP

子类必须能够替换其基类出现的地方,且程序行为不被破坏
也就是说,如果父类能做的事情,子类也必须能做;子类不能违背父类的契约。
如果 Birdfly() 方法,而我们让 Penguin 继承 Bird 却无法飞,这就破坏了里氏替换。正确做法是把 Bird 抽象成 Animal,把会飞的鸟作为 FlyingBird,避免不合理的继承。

方圆问题

class Rectangle {protected int width, height;public void setWidth(int w) { width = w; }public void setHeight(int h) { height = h; }public int area() { return width * height; }
}class Square extends Rectangle {public void setWidth(int w) {super.width = w;super.height = w; // 覆盖行为:修改一个属性影响另一个}public void setHeight(int h) {super.width = h;super.height = h;}
}

若把 Square 当作 Rectangle 使用,会破坏对 setWidth/setHeight 的预期。

更合理的抽象

interface Shape {int area();
}class Rectangle implements Shape {private int width, height;public Rectangle(int w, int h) { this.width = w; this.height = h; }public int area() { return width * height; }
}class Square implements Shape {private int side;public Square(int side) { this.side = side; }public int area() { return side * side; }
}

用更合适的抽象(Shape)来避免不恰当的继承,保证替换安全。


依赖倒置原则 DIP

高层模块不依赖底层模块,二者共同依赖抽象;抽象不依赖细节,细节依赖抽象
简单说,就是我们应该面向接口编程,而不是面向实现编程。
支付系统中不应该直接依赖 WeChatPayAliPay,而是依赖一个通用的 PayInterface。这样在切换或新增支付方式时,只需实现接口,而不用修改业务逻辑。

违反示例(高层依赖具体实现)

public class OrderService {private MySqlOrderRepository repository = new MySqlOrderRepository();// 直接new导致OrderService强耦合于MySQL实现
}

改进示例(依赖抽象并注入)

public interface OrderRepository {void save(Order order);
}public class MySqlOrderRepository implements OrderRepository {public void save(Order order) { }
}public class OrderService {private final OrderRepository repository;public OrderService(OrderRepository repository) {this.repository = repository; // 依赖注入,灵活注入OrderRepository的实现类}public void place(Order order) {repository.save(order);}
}

OrderService 只依赖 OrderRepository 抽象,测试时可注入 mock,切换实现也方便。


接口隔离原则 ISP

一个接口应该只包含客户需要的方法
如果接口过于臃肿,会迫使实现类去实现一些它们根本不需要的方法,造成冗余。
例如,如果我们定义了一个 Animal 接口,里面有 fly()run()swim(),那么企鹅实现它时就会很尴尬。正确的做法是把大接口拆成更小、更专注的接口,比如 FlyableRunnableSwimmable

臃肿接口 Printer

interface Printer {void print(Document d);void scan(Document d);void fax(Document d);
}class SimplePrinter implements Printer {public void print(Document d) {// all right}public void scan(Document d) { throw new UnsupportedOperationException(); }public void fax(Document d) { throw new UnsupportedOperationException(); }
}

SimplePrinter 被迫实现不需要的方法,产生空实现或异常。

接口拆分

interface Printable { void print(Document d); }
interface Scannable { void scan(Document d); }
interface Faxable { void fax(Document d); }class SimplePrinter implements Printable {public void print(Document d) { }
}

客户端只依赖它需要的接口,职责更清晰也更灵活。


迪米特法则 LoD

也叫最少知道原则,强调对象之间应尽量少地相互了解
简单说就是不要和陌生人说话。一个对象只与直接朋友(自己创建的对象、参数、成员变量)通信,而不是跨层级去操作别的对象的内部细节。
例如:A.getB().getC().doSomething() 就违反了迪米特法则,因为 A 不应该知道 C 的存在,最好通过 B 提供一个方法来间接完成操作。

链式调用的违反示例

class Engine {public OilTank getOilTank() { return new OilTank(); }
}
class Car {private Engine engine;public Engine getEngine() { return engine; }
}
Car car = new Car();
car.getEngine().getOilTank().drain(); // 违反LoD:car不该知道oilTank的存在

通过封装提供高层方法以改进

class Engine {private OilTank oilTank;public void drainOil() { oilTank.drain(); }
}
class Car {private Engine engine;public void drainEngineOil() { engine.drainOil(); }
}Car car = new Car();
car.drainEngineOil(); // Car的调用只涉及自己的直接朋友

把对内部组件的操作通过高层方法封装,减少耦合,便于演化与测试。

六大设计原则并不是孤立存在的,而是相互联系、共同作用的。掌握并灵活运用这六大原则,就能在项目中写出更清晰、更健壮、更易扩展的代码。

http://www.dtcms.com/a/393840.html

相关文章:

  • ✅ 基于OpenCV与HyperLPR的车牌识别系统 PyQt5可视化 支持多种输入 深度学习毕业设计
  • 深度学习在计算机视觉中的最新进展:范式转变与前沿探索
  • 本地免费使用网页表格控件websheet
  • Spring Boot集成MQTT与单片机通信
  • 【Axios 】web异步请求
  • FreeRTOS实战指南 — 6 临界段保护
  • 关于CFS队列pick_next_task_fair选取下一个任务的分析
  • 【算法笔记】链表相关的题目
  • Netty从0到1系列之Recycler对象池技术【3】
  • 网页开发入门:CSS与JS基础及BS/CS架构解析
  • css单位换算及适配
  • Java制作双脑同步 Hemi-Sync 音频
  • webrtc弱网-ProbeBitrateEstimator类源码分析与算法原理
  • 在OpenHarmony上适配图形显示【4】——rk3568_4.0r_mesa3d适配
  • 嵌入式(3)——RTC实时时钟
  • 内核模块组成和裁剪参考表
  • 140-understanding_the_armv8.x_and_armv9.x_extensions_guide
  • 【序列晋升】40 Spring Data R2DBC 轻量异步架构下的数据访问最佳实践
  • TGRS | 视觉语言模型 | 语言感知领域泛化实现高光谱跨场景分类, 代码开源!
  • Oracle / MySQL / MariaDB / SQL Server 常用连接与基础查询(Linux操作系统上)
  • 将 Jupyter Notebook 转换为 PDF
  • torchvision 编译安装 nano
  • 华为昇腾 910 到 950 系列 NPU 深度解析
  • 设计模式---门面模式
  • SQL Server从入门到项目实践(超值版)读书笔记 26
  • Datawhale学习笔记——深度语义匹配模型DSSM详解、实战与FAQ
  • 一文了解瑞萨MCU常用的芯片封装类型
  • LeetCode:44.二叉搜索树中第K小的元素
  • 初学者如何系统性地学习Linux?
  • LeetCode:43.验证二叉搜索树