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

手把手吃透设计模式内功:七大原则代码解析与重构指南

事实上,许多学习者将重点放在了记忆“招式”(即具体模式)上,导致即便背熟了众多模式,在实际开发中仍鲜有应用。设计模式真正的核心在于其背后的设计原则。所有经典的设计模式,无一不是在这些基本原则的指导思想下衍生出来的。因此,欲成高手,必先修炼好“内功”。

下面,我将系统阐述构成设计模式基石的七大设计原则。

一、单一职责原则

定义:一个类应该只有一个引起它变化的原因。

解读:通俗来讲,如果你能找到两种及以上不同类型的理由去修改某个类,那么这个类就违反了单一职责原则。

示例剖析:

```java

class UserService {

public void register(String username, String password) {

// 1. 校验用户名和密码

if (username == null || password.length() < 6) {

throw new IllegalArgumentException("参数错误");

}

// 2. 保存用户到数据库

System.out.println("save user to mysql");

// 3. 发送欢迎短信

System.out.println("send sms to user");

// 4. 打印日志

System.out.println("log register success");

}

}

```

上述 `UserService` 类至少面临四种独立的修改原因:

1. 产品需求变化:密码规则调整为数字+字母组合。

2. 运维需求变化:日志需同时输出至本地与远程日志系统。

3. 技术架构变化:数据库从 MySQL 迁移至 Oracle。

4. 运营需求变化:欢迎通知从短信改为邮件。

这四种毫无关联的变化都可能导致修改同一个类,若系统中有多个 `Service`,修改将变得异常繁琐且风险极高。设计模式的目标之一,正是将变化的影响局部化,最大限度减少修改范围。

重构方案:将不同变化的职责封装到独立的类中。

```java

class UserService {

private UserValidator validator = new UserValidator();

private UserRepository repository = new UserRepository();

private Notifier notifier = new Notifier();

private Logger logger = new Logger();

public void register(String username, String password) {

validator.validate(username, password); // 封装规则变化

repository.save(username, password); // 封装存储变化

notifier.notifyUser(username); // 封装通知变化

logger.log("register success"); // 封装日志变化

}

}

```

策略模式即是单一职责原则的一个典型应用。

二、开闭原则

定义:对扩展开放,对修改关闭。

解读:系统设计应允许在不修改现有稳定代码的前提下,通过扩展来增加新功能。

此原则与单一职责原则相辅相成:后者负责封装变化,前者则倡导通过扩展而非修改来应对变化。

反面示例:

```java

class PayService {

public void pay(String type, int amount) {

if ("alipay".equals(type)) {

System.out.println("use alipay");

} else if ("wechat".equals(type)) {

System.out.println("use wechat");

}

// 每新增一种支付方式,都需要修改此方法,添加一个else if

}

}

```

此种设计会导致类不断膨胀,难以维护。

遵循开闭原则的重构:

```java

// 定义抽象策略

interface PayStrategy {

void pay(int amount);

}

// 具体策略实现

class AliPay implements PayStrategy {

public void pay(int amount) {

System.out.println("alipay " + amount);

}

}

class WeChatPay implements PayStrategy {

public void pay(int amount) {

System.out.println("wechat " + amount);

}

}

// 上下文,依赖抽象

class PayService {

private PayStrategy strategy;

public PayService(PayStrategy strategy) {

this.strategy = strategy;

}

public void pay(int amount) {

strategy.pay(amount);

}

}

```

此后新增支付方式(如 `BankPay`),只需实现 `PayStrategy` 接口即可,`PayService` 核心逻辑无需改动。

策略模式、工厂模式、装饰器模式、观察者模式等都是开闭原则的典型体现。

三、里氏替换原则

定义:子类对象必须能够替换其父类对象,且替换后程序的正确性不被破坏。

解读:任何使用父类类型的地方,都应能透明地替换为子类对象。

违反LSP的示例:

```java

class Employee { // 全职员工

public double calculateSalary(double baseSalary) {

return baseSalary; // 全职工资 = 基本工资

}

}

class PartTimeEmployee extends Employee { // 兼职员工

@Override

public double calculateSalary(double baseSalary) {

return baseSalary 0.5; // 兼职按半薪算

}

}

// 使用方法

public void printAnnualSalary(Employee employee) {

// 期望年薪 = 月薪 12

System.out.println(employee.calculateSalary(1000) 12);

}

```

当 `PartTimeEmployee` 实例传入 `printAnnualSalary` 方法时,计算出的“年薪”是基于错误假设的(兼职可能干不满12个月),程序行为出现错误。这说明 `PartTimeEmployee` 并不是 `Employee` 合适的子类。

遵循LSP的设计:

应基于共同行为抽象接口,而非不恰当的继承。

```java

interface SalaryCalculator {

double calculateSalary(double baseSalary);

}

class FullTimeSalaryCalculator implements SalaryCalculator {

public double calculateSalary(double baseSalary) { return baseSalary; }

}

class PartTimeSalaryCalculator implements SalaryCalculator {

public double calculateSalary(double baseSalary) { return baseSalary 0.5; }

}

```

策略模式、模板方法模式、装饰器模式等都体现了LSP思想,确保子类或具体实现能安全替换父类或接口。

四、依赖倒置原则

定义:

1. 高层模块不应依赖低层模块,二者都应依赖抽象。

2. 抽象不应依赖细节,细节应依赖抽象。

解读:高层业务逻辑应依赖于接口(抽象),而非具体的实现类。

反面示例:

```java

class OrderService {

private PayService payService = new PayService();

public void checkout(double amount, String type) {

if (type.equals("alipay")) {

payService.payByAliPay(amount);

} else if (type.equals("wechat")) {

payService.payByWeChat(amount);

}

}

}

```

`OrderService`(高层)直接依赖了具体的 `PayService` 及其方法。新增支付方式需要同时修改高层和低层代码。

遵循DIP的重构:

```java

// 抽象

interface PayStrategy {

void pay(double amount);

}

// 具体实现

class AliPay implements PayStrategy { / ... / }

class WeChatPay implements PayStrategy { / ... / }

// 高层模块依赖抽象

class OrderService {

private PayStrategy payStrategy;

public OrderService(PayStrategy payStrategy) { // 依赖注入

this.payStrategy = payStrategy;

}

public void checkout(double amount) {

payStrategy.pay(amount); // 只调用抽象方法

}

}

// 使用

OrderService order1 = new OrderService(new AliPay());

order1.checkout(100);

```

此原则保证了当底层实现变化时,高层业务逻辑保持稳定,系统可通过扩展灵活适应变化。

依赖注入、策略模式、工厂模式是此原则的主要应用。

五、接口隔离原则

定义:客户端不应被迫依赖它不使用的接口方法。

解读:接口应当职责单一、粒度细小,只暴露客户端真正需要的方法,避免“胖接口”。

反面示例:

```java

public interface UserService{

void addUser(User user);

void log(String tag, String message); // 与用户核心业务无关

void uploadFile(File file); // 与用户核心业务无关

}

```

`UserService` 接口混杂了日志、文件上传等不相关的职责,导致实现类被迫实现所有方法,降低了内聚性。

遵循ISP的重构:

```java

public interface UserService{

void addUser(User user);

void updateUser(User user);

User findById(Integer id);

void delete(Integer id);

}

public interface LogService{

void log(String tag, String message);

}

public interface UploadService{

void uploadFile(File file);

}

```

将不同业务的接口分离,使得系统更清晰,也提高了接口的复用性。

策略模式、装饰器模式、适配器模式都体现了接口隔离的思想。

六、迪米特法则(最少知识原则)

定义:一个对象应尽可能少地了解其他对象,只与“直接的朋友”通信。

解读:降低类之间的耦合度,避免过深的链式调用。

违反示例:

```java

class User {

private Address address;

public Address getAddress() { return address; }

}

class Address {

private String city;

public String getCity() { return city; }

}

class OrderService {

public void printUserCity(User user) {

// 深层调用:user > address > city

System.out.println(user.getAddress().getCity());

}

}

```

`OrderService` 不仅依赖 `User`,还间接依赖了 `Address` 的内部细节。一旦 `Address` 结构变化,`OrderService` 也可能需要修改。

遵循迪米特法则的重构:

```java

class User {

private Address address;

public String getCity() { // 封装对Address的访问

return address.getCity();

}

}

class OrderService {

public void printUserCity(User user) {

// 只与User交互,不感知Address细节

System.out.println(user.getCity());

}

}

```

此法有效防止了 `a.getB().getC().doSomething()` 这类深层链式调用,降低了模块间的耦合,使系统更易维护。

外观模式、代理模式是该法则的典型应用。

七、合成复用原则

定义:优先使用对象组合,而非类继承,来达到复用的目的。

解读:组合比继承具有更大的灵活性,能降低类之间的耦合度。

反面示例(过度使用继承):

```java

class Report {

public void generatePDF() { System.out.println("生成PDF报表"); }

public void generateExcel() { System.out.println("生成Excel报表"); }

}

class LoggedReport extends Report { // 通过继承添加日志功能

@Override

public void generatePDF() {

System.out.println("记录日志");

super.generatePDF();

}

@Override

public void generateExcel() {

System.out.println("记录日志");

super.generateExcel();

}

}

```

`LoggedReport` 与 `Report` 紧密耦合。若 `Report` 的方法签名或行为改变,`LoggedReport` 必须同步修改,且无法灵活地复用其他类型的“增强”功能(如加密、压缩等)。

遵循合成复用原则的重构:

```java

class Report {

public void generatePDF() { / ... / }

public void generateExcel() { / ... / }

}

class Logger { // 独立的日志功能

public void log(String msg) { System.out.println("记录日志: " + msg); }

}

class ReportService { // 通过组合复用

private Report report;

private Logger logger;

public ReportService(Report report, Logger logger) {

this.report = report;

this.logger = logger;

}

public void generatePDF() {

logger.log("生成PDF");

report.generatePDF();

}

public void generateExcel() {

logger.log("生成Excel");

report.generateExcel();

}

}

```

通过组合,可以更灵活地装配不同功能,且 `Report` 和 `Logger` 可以独立变化和复用。

总结

七大设计原则构成了高质量软件设计的基石。在日常开发中,时刻以这些原则为指导,能够使你的程序结构更清晰、代码更简洁、模块更独立,从而显著降低维护成本,提升系统的可扩展性与健壮性。掌握这些“内功”,远比单纯记忆设计模式“招式”更为重要。

来源:小程序app开发|ui设计|软件外包|IT技术服务公司-木风未来科技-成都木风未来科技有限公司

http://www.gsyq.cn/news/149023.html

相关文章:

  • Elasticsearch倒排索引详解:如何实现高效搜索
  • excel如何筛选重复项?码住这4种方法!
  • Asio12-HandlePacketStickingProblemSimply
  • 第四章 SQL Server备份和还原
  • 【课程设计/毕业设计】基于springboot美发门店管理系统设计与实现基于springboot的美发商城系统【附源码、数据库、万字文档】
  • 计算机Java毕设实战-基于springboot的美发商城系统服务预约技师选择、到店时间预约【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 计算机Java毕设实战-基于SpringBoot的植物知识管理与分享平台的设计与实现家庭园艺种植分享平台设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 番茄小说下载器 2025.12.21 | 现代化、高效的番茄小说下载器,支持批量下载和多种格式导出
  • STM32平衡车工具-匿名助手+虚拟串口如何使用。
  • 计算机Java毕设实战-基于springboot+vue技术的二手车交易管理系统的设计与实现基于SpringBoot+Vue的二手车交易平台设计【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 2-[(2-叠氮乙酰基)氨基]-2-脱氧-D-吡喃甘露糖—糖生物学与代谢标记的关键化学探针 1971934-97-0
  • Java毕设选题推荐:基于springboot+vue技术的二手车交易管理系统的设计与实现汽车管理汽车品牌管理,公告类型管理,论坛管理【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 新品限免|国产大模型工程化实战:GLM-4.7与MiniMax M2.1 免费选型对比
  • Java毕设项目:基于springboot+vue技术的二手车交易管理系统的设计与实现(源码+文档,讲解、调试运行,定制等)
  • Java毕设选题推荐:基于Java的停车场管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • python自定义注解
  • 12/24第五章
  • python基于Vue的客户关系管理系统的设计与实现_3itcvt88
  • vue基于python的民宿房间预订推荐系统的设计与实现_7r8s9b63(pycharm django flask)
  • Pandoc转换Word文档:使用Lua过滤器统一调整Pandoc文档中的图片和表格格式
  • VFF-Net:一种取代反向传播的AI训练新算法
  • Java计算机毕设之基于Java的停车场管理系统(完整前后端代码+说明文档+LW,调试定制等)
  • springboot-vue企业会议交换机设备维修批量运维管理系统 vue没论文py09
  • 数据库介绍
  • 【毕业设计】基于Java的停车场管理系统(源码+文档+远程调试,全bao定制等)
  • Java毕设项目:基于springboot的户外救援系统(源码+文档,讲解、调试运行,定制等)
  • Java毕设项目推荐-基于springBool+Vue美食分享平台的设计与实现基于网络平台的美食分享【附源码+文档,调试定制服务】
  • vue基于python的企业人事绩效考核综合管理系统_z006916a(pycharm django flask)
  • 事后诸葛亮会议报告 - Nyanya-
  • 一文搞懂单点登录系统:架构、原理与实战案例