ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

软件设计核心:SOLID原则与重构实战指南

软件设计核心:SOLID原则与重构实战指南 1. 我们真的忘记如何设计了吗最近在评审代码时我越来越频繁地看到一种现象业务功能能跑通测试也能过但一旦需求变化改动一个功能要牵动十几个文件新同学接手时完全不敢动代码。这让我忍不住思考一个问题——在追求“快速交付”的今天我们是否真的忘记了如何设计软件设计并不是指画一张漂亮的架构图也不是要求每个项目都套用一套重量级的微服务架构。软件设计的本质是让代码在面对需求变化时仍然保持可读、可扩展、可测试和可维护。说得直白一点设计决定了你三个月后改这段代码时是轻松十行还是头疼三天。很多开发者在学习阶段更多关注语法、框架、中间件却很少系统训练设计能力。结果就是用 Spring Boot 写接口很熟练但一个 Service 类越来越大一个 if / else 分支越堆越长复制粘贴的代码到处都是每个方法都藏着隐藏副作用测试越写越痛苦因为根本没法构造对象。这篇文章会从“设计为什么重要”开始梳理软件设计的基本层次结合 SOLID 原则和设计模式用一个订单折扣系统的完整案例演示如何从坏味道代码逐步重构为可扩展的代码。同时也会分析架构层面常见的设计问题以及 AI 生成代码时代我们为什么更需要具备设计能力。无论你是刚接触系统性设计的新手还是已经写了几年业务代码、想系统整理设计知识的开发者这篇文章都希望给你一套可以照着做的思路而不是空谈“要有设计感”。2. 设计到底是什么从代码到架构的四个层次2.1 设计不只是架构师的事很多人觉得“设计”是架构师的工作普通开发只需要按照原型写接口。这是一个很危险的误解。实际上每一个方法签名、每一个类划分、每一次选择“传参数还是建对象”都在做设计。设计不是某一阶段的活动而是编码过程中持续发生的决策。架构师决定的是系统级别的边界而普通开发者每天都在决定一个类内部的高内聚低耦合程度、一个方法是否承担了过多的职责。如果说写代码是把方案翻译成机器能理解的指令那么设计就是决定用什么结构去组织这些指令。同一个功能用硬编码和 if / else 实现和用策略接口加实现类实现功能结果完全相同但维护成本完全不同。2.2 设计的四个层次为了方便理解我们可以把设计拆成四个递进的层次层次关注点常见问题命名与表达式变量、方法、类是否表意清晰命名随意、魔法数字遍地方法设计方法职责单一、参数合理、副作用少方法过长、参数过多、副作用隐藏类与对象设计类的职责边界、依赖方向、扩展方式上帝类、过度耦合、贫血模型模块与架构设计模块依赖、边界划分、技术选型循环依赖、模块耦合、没有边界大多数项目的设计退化并不是突然发生的而是从第一层开始松懈一路蔓延到第四层。一个命名模糊的方法会诱导后来者往里面继续堆逻辑一个什么都干的类会让整个模块变成一团乱麻。2.3 好设计的客观特征设计的好坏并非完全主观下面这些特征可以帮你快速做一个判断修改一个需求时需要改动的文件数量是否可控新增一种业务类型时能否在不改动已有代码的前提下扩展核心业务逻辑是否可以不依赖具体框架直接进行单元测试代码阅读者能不能通过类名和方法名快速理解业务规则是否存在大量相互耦合、无法独立复用的小模块。如果一个项目出现了“改一行测试挂一片”“新增类型要复制粘贴一段 if / else”“Service 里有几千行代码”的情况说明设计已经被透支了。3. 核心设计原则SOLID 不是面试题而是排错标准3.1 单一职责原则一个类只负责一件事单一职责原则Single Responsibility Principle, SRP是最容易理解、也最容易被违反的原则之一。它指的是一个类或方法应当只有一个引起它变化的原因。看一个反例// 文件路径src/main/java/com/example/design/bad/UserService.java public class UserService { public void register(String username, String email) { // 1. 校验用户名 if (username null || username.length() 3) { throw new IllegalArgumentException(用户名长度不能小于3); } // 2. 校验邮箱 if (email null || !email.contains()) { throw new IllegalArgumentException(邮箱格式不正确); } // 3. 保存用户 // userDao.save(...); // 4. 发送欢迎邮件 // emailClient.send(email, 欢迎注册); // 5. 记录日志 // logger.info(user registered: {}, username); } }这个UserService看似只是一个注册方法实际上承担了校验、持久化、通知、日志四件事。明天如果需要调整“发送欢迎邮件”的模板或者改成短信通知就得修改这个类后天如果校验规则变了还是修改这个类。一个类被多个需求变更点拉扯很快就会失控。按职责拆开以后每个类都有自己的边界// 文件路径src/main/java/com/example/design/refactor/UserValidator.java public class UserValidator { public void validate(String username, String email) { if (username null || username.length() 3) { throw new IllegalArgumentException(用户名长度不能小于3); } if (email null || !email.contains()) { throw new IllegalArgumentException(邮箱格式不正确); } } }// 文件路径src/main/java/com/example/design/refactor/UserRegistrationService.java public class UserRegistrationService { private final UserValidator userValidator; private final UserRepository userRepository; private final UserNotifier userNotifier; public UserRegistrationService(UserValidator userValidator, UserRepository userRepository, UserNotifier userNotifier) { this.userValidator userValidator; this.userRepository userRepository; this.userNotifier userNotifier; } public void register(String username, String email) { userValidator.validate(username, email); User user userRepository.save(new User(username, email)); userNotifier.sendWelcomeMessage(user); } }注意这里并不是要求每个类只有一个方法而是要求每个类只有一个“变化原因”。当需求变更时我们希望影响面集中在某一个类内部而不是蔓延到整条链路。3.2 开闭原则对扩展开放对修改封闭开闭原则Open-Closed Principle, OCP是设计模式中价值最高的原则之一。它的核心含义是当系统需要增加新功能时尽量通过新增代码来扩展而不是不断修改已有的核心逻辑。下面是一个典型的需要用开闭原则整治的代码// 文件路径src/main/java/com/example/design/bad/OrderDiscountService.java public class OrderDiscountService { public double calculateDiscount(String userLevel, double amount) { double discount 0; if (NORMAL.equals(userLevel)) { discount 0; } else if (SILVER.equals(userLevel)) { discount amount * 0.05; } else if (GOLD.equals(userLevel)) { discount amount * 0.10; } else if (PLATINUM.equals(userLevel)) { discount amount * 0.15; } return discount; } }这段代码的坏味道非常典型每增加一个会员等级都要新增一个else if分支如果某个等级的规则从“固定折扣”变成“满减”又要重写整个方法。这种以条件判断为核心的结构在业务规则增多后会变成无法维护的分支丛林。重构方式请看第 4 节的完整案例。3.3 依赖倒置原则依赖抽象不依赖具体实现依赖倒置原则Dependency Inversion Principle, DIP告诉我们要面向接口编程上层模块不依赖下层模块的实现细节。一个简单的判断方法你的 Service 代码里如果到处都是new XxxClient()那么这个 Service 很难测试也很难替换实现。解决思路有两个使用构造注入把依赖通过构造函数传入让调用方依赖接口而不是具体的实现类。用 Spring 开发时这一点通常由容器帮助完成但很多项目仍然会写出在方法内部直接new依赖对象的代码。这种代码在单测时往往只能通过 Mockito 强行 mock一旦依赖链条变长测试成本会直线上升。3.4 组合优于继承继承是代码复用的利器但也是耦合的源头。父类一旦修改行为所有子类都会受影响多层继承之后子类到底继承了多少隐式行为几乎无法判断。更推荐的方式是组合把可变化的行为抽取为接口通过持有接口引用的方式复用能力。比如动物类不再通过Bird extends Animal来获得飞行能力而是通过FlyBehavior接口来组合。这样企鹅不会“继承”到飞行能力代码表达也更贴近现实。这条原则在业务系统里体现为优先使用策略模式、装饰器模式等组合式方案而不是建立深层的继承树。3.5 DRY 与 YAGNI 的平衡DRYDont Repeat Yourself不要重复自己重复代码意味着修改时需要同时改多处漏改会产生隐蔽 bug。YAGNIYou Arent Gonna Need It不要过度设计不要为想象中永远不会来的需求提前抽象。这两条原则单独看都对放在一起却存在张力。过度追求 DRY 会导致提前抽象引入不必要的间接层过度追求 YAGNI 会导致代码堆叠后续重构成本更高。我的建议是在同一个模块内出现三处以上完全相同的逻辑时再抽取判断未来需求是否确定再决定是否引入抽象层。抽象的价值不仅在于复用更在于为“变化”预留接缝但没有变化的抽象就是浪费。4. 完整实战案例订单折扣系统重构这一节我们通过一个实际可运行的 Java 示例完整演示从坏味道代码到良好设计的全过程。示例采用纯 Java 实现不依赖 Spring方便直接运行验证。4.1 需求描述电商订单中会员折扣规则如下普通会员无折扣白银会员95 折优惠 5%黄金会员9 折优惠 10%铂金会员85 折优惠 15%节日活动在此基础上全场满 500 减 50。注意这是一个动态变化的规则集合。今天可能是“会员折扣”明天可能加入“新人立减 100”“优惠券抵扣”“秒杀活动打折”。不同的促销类型还可能叠加。4.2 第一版面向过程的 if / else 实现// 文件路径src/main/java/com/example/design/bad/OrderService.java public class OrderService { /** * 计算订单实付金额 * * param memberLevel 会员等级 * param originalAmount 原价 * param isFestival 是否节日活动 */ public double payAmount(String memberLevel, double originalAmount, boolean isFestival) { double discountRate 1.0; if (NORMAL.equals(memberLevel)) { discountRate 1.0; } else if (SILVER.equals(memberLevel)) { discountRate 0.95; } else if (GOLD.equals(memberLevel)) { discountRate 0.90; } else if (PLATINUM.equals(memberLevel)) { discountRate 0.85; } double afterMemberDiscount originalAmount * discountRate; if (isFestival originalAmount 500) { afterMemberDiscount afterMemberDiscount - 50; } return afterMemberDiscount; } }这个实现的问题非常明显新增会员等级要改payAmount方法新增促销类型还要继续加参数、加分支方法参数列表越来越长payAmount的签名会变成灾难业务规则无法独立测试因为所有逻辑耦合在一起。4.3 重构思路策略模式 组合模式我们做如下设计定义DiscountStrategy接口统一表达一种折扣规则每种规则是一个独立策略实现类订单的价格计算由一组策略按照顺序叠加完成策略列表由外部传入核心计算逻辑不感知具体策略。首先定义策略接口// 文件路径src/main/java/com/example/design/refactor/DiscountStrategy.java public interface DiscountStrategy { /** * 返回规则名称便于日志和排查 */ String name(); /** * 对订单金额执行折扣计算 */ double apply(Order order); }// 文件路径src/main/java/com/example/design/refactor/Order.java public class Order { private final String memberLevel; private final double originalAmount; public Order(String memberLevel, double originalAmount) { this.memberLevel memberLevel; this.originalAmount originalAmount; } public String getMemberLevel() { return memberLevel; } public double getOriginalAmount() { return originalAmount; } }然后实现会员等级策略// 文件路径src/main/java/com/example/design/refactor/MemberLevelDiscount.java public class MemberLevelDiscount implements DiscountStrategy { private final double rate; public MemberLevelDiscount(double rate) { this.rate rate; } Override public String name() { return 会员等级折扣( rate ); } Override public double apply(Order order) { return order.getOriginalAmount() * (1 - rate); } }实现节日满减策略// 文件路径src/main/java/com/example/design/refactor/FestivalFullReduction.java public class FestivalFullReduction implements DiscountStrategy { private final double threshold; private final double reduction; public FestivalFullReduction(double threshold, double reduction) { this.threshold threshold; this.reduction reduction; } Override public String name() { return 节日满 threshold 减 reduction; } Override public double apply(Order order) { if (order.getOriginalAmount() threshold) { return reduction; } return 0; } }再实现一个策略参与计算的组合入口。这里我们用一种简单的叠加方式折扣金额累加然后从原价中扣除// 文件路径src/main/java/com/example/design/refactor/OrderPricingService.java import java.util.List; public class OrderPricingService { private final ListDiscountStrategy strategies; public OrderPricingService(ListDiscountStrategy strategies) { this.strategies strategies; } public double calculate(Order order) { double totalDiscount 0; for (DiscountStrategy strategy : strategies) { double discount strategy.apply(order); System.out.println(应用规则: strategy.name() , 减免: discount); totalDiscount discount; } double finalAmount order.getOriginalAmount() - totalDiscount; return Math.max(finalAmount, 0); } }为了接入新的会员等级我们建立一个策略工厂// 文件路径src/main/java/com/example/design/refactor/DiscountStrategyFactory.java import java.util.ArrayList; import java.util.List; public class DiscountStrategyFactory { public static ListDiscountStrategy createDefaultStrategies(String memberLevel, boolean festival) { ListDiscountStrategy strategies new ArrayList(); switch (memberLevel) { case SILVER: strategies.add(new MemberLevelDiscount(0.95)); break; case GOLD: strategies.add(new MemberLevelDiscount(0.90)); break; case PLATINUM: strategies.add(new MemberLevelDiscount(0.85)); break; default: break; } if (festival) { strategies.add(new FestivalFullReduction(500, 50)); } return strategies; } }有人可能会问工厂里还是有 switch 分支这不算坏味道吗这里需要区分一个概念switch 本身不是问题问题在于“规则计算逻辑”是否还埋在业务方法里。将“如何根据参数创建策略列表”收敛到 factory 之后核心计算逻辑已经与具体规则解耦。新增策略时只需要修改 factory不需要再碰OrderPricingService。4.4 运行与验证写一个简单的 main 方法验证结果// 文件路径src/main/java/com/example/design/refactor/Main.java import java.util.List; public class Main { public static void main(String[] args) { // 黄金会员原价 1000节日活动 Order order new Order(GOLD, 1000); ListDiscountStrategy strategies DiscountStrategyFactory.createDefaultStrategies(order.getMemberLevel(), true); OrderPricingService pricingService new OrderPricingService(strategies); double finalAmount pricingService.calculate(order); System.out.println(原价: order.getOriginalAmount()); System.out.println(实付: finalAmount); } }运行结果预期如下应用规则: 会员等级折扣(0.9), 减免: 100.0 应用规则: 节日满500.0减50.0, 减免: 50.0 原价: 1000.0 实付: 850.0这里我们故意让策略计算折扣金额而不是直接计算折扣后的价格是为了让多个策略可以按照“减免金额”进行叠加逻辑更清晰。4.5 重构后的扩展性验证现在假设运营要求新增“新用户首单立减 30”。我们只需要新增一个策略类然后在工厂中追加一行// 文件路径src/main/java/com/example/design/refactor/NewUserDiscount.java public class NewUserDiscount implements DiscountStrategy { private final boolean isNewUser; public NewUserDiscount(boolean isNewUser) { this.isNewUser isNewUser; } Override public String name() { return 新用户首单立减30; } Override public double apply(Order order) { return isNewUser ? 30 : 0; } }在DiscountStrategyFactory中// 新增代码片段 if (isNewUser) { strategies.add(new NewUserDiscount(true)); }整个过程不需要修改OrderPricingService不需要修改已有策略完美契合开闭原则。这就是设计带来的直接收益新需求到来时改动范围被限制在最容易理解的位置。5. 从代码设计走向架构设计5.1 分层架构依赖方向必须单向分层架构是最常见也最实用的架构设计方式。典型的三层结构是 Controller、Service、Repository每一层只依赖它的下一层。但很多项目写着写着就变形了Controller 直接操作数据库Service 之间相互调用Repository 返回 Map 而不是领域对象最后层与层之间没有边界任何改动都需要全局排查。检查分层是否健康可以看两个信号上层能否只依赖接口定义而不感知下层的实现细节修改某一层时是否牵动了消费者接口的改动。如果这两个信号都不满足说明边界已经失效。5.2 模块化设计包的边界就是未来维护的边界Java 项目中包结构本身就是设计表达。一个容易维护的包结构应当做到“按业务能力分包”而不是“按技术类型分包”。一个常见的反模式是com.example.order ├── controller │ └── OrderController.java ├── service │ ├── OrderService.java │ └── UserService.java ├── dao │ ├── OrderDao.java │ └── UserDao.java └── entity ├── OrderEntity.java └── UserEntity.java这种结构在项目初期非常流行但问题在于order模块里出现了user的服务和实体说明订单和用户已经混在一起。更好的做法是按聚合或者业务域组织com.example.order ├── OrderController.java ├── OrderService.java ├── OrderRepository.java └── Order.java com.example.user ├── UserController.java ├── UserService.java ├── UserRepository.java └── User.java把相关行为放到一起外部依赖通过接口暴露模块之间的关系更加清晰。5.3 防腐层隔离外部系统的变化项目与外部服务第三方 API、旧系统、数据库表结构交互时最好加一个防腐层Anti-Corruption Layer。防腐层的职责是把外部模型翻译成内部领域模型避免外部系统的概念扰动核心业务。举例来说外部订单接口可能返回state1/2/3这样的状态码而内部业务使用的是“待支付、已支付、已关闭”。如果核心代码直接消费外部状态码那么外部系统一改整个业务逻辑都要跟着改。正确做法是在防腐层统一转换为内部状态枚举核心业务只感知内部枚举。防腐层虽然多了一层代码但在系统集成场景下这一层是成本最低的保险。5.4 领域驱动设计什么时候需要上 DDD很多人一提到领域驱动设计DDD就想到一堆晦涩的概念聚合根、领域事件、值对象、限界上下文。对于小型 CRUD 系统DDD 确实可能带来过度设计。但当你面对一个复杂的业务系统规则密集、状态流转多、多个团队同时维护时DDD 提供的“业务边界”思维就非常有价值。即使不全面上 DDD也建议借鉴它的两个基本思想在模型中用业务语言命名而不是用数据库表名和字段名明确核心域与支撑域资源向核心域倾斜。复杂系统的维护难点从来不是写不出代码而是找不到业务规则藏在哪里。DDD 的价值就是让业务规则以显式的方式落在代码里。6. 常见设计问题与排查思路下面这些问题是评审代码时最常见的“设计坏味道”遇到时可以对照排查。问题现象常见原因解决思路Service 类超过 1000 行多个业务用例堆积在一个类按用例拆分抽取独立服务新增条件就加 else if没有抽象变化点使用策略模式或状态模式构造方法参数超过 6 个对象职责不清晰拆分类或将参数聚合为对象单元测试困难硬编码依赖、大量 new构造注入 面向接口改一个需求改多个模块模块边界不清晰重新划分包结构与依赖方向一个类大量 getter/setter贫血模型业务逻辑外移将行为放入领域对象循环依赖模块依赖方向失控抽取公共接口或中间层复制粘贴代码抽象层次缺失抽方法、抽公共组件6.1 上帝类什么都要管的类上帝类God Class是指一个类承担了过多职责方法很多、状态很多几乎成了“工具类收纳箱”。排查方法很简单如果一个类的方法与另一个完全不相关的业务领域耦合或者类中有一半方法依赖这个类自己维护的全局状态就需要拆分了。拆分不是把方法随机分散到其他类而是要重新梳理“变化原因”。每个类只保留一个变化原因其他职责迁移到对应的新类中。6.2 过度设计为抽象而抽象过度设计的典型症状是一个简单功能引入了接口、抽象类、工厂、模板方法调用链路要跳五层才能看懂业务逻辑。设计模式本身没有错错的是在不合适的场景强行使用。判断某个抽象是否必要一个简单有效的方法是问自己未来六个月内这类规则是否真的可能新增如果答案是否定的就不需要提前抽象。你随时可以在出现第二次重复时再进行重构这也是“事不过三”原则的实践方式。6.3 循环依赖模块无法独立演进Spring 项目中A 依赖 B、B 又依赖 A 的循环依赖经常发生。这种设计不仅会导致启动警告甚至失败更重要的是说明两个模块之间没有清晰的层析关系。修复循环依赖通常有三个思路将公共的部分抽到另一个模块引入事件机制把强依赖变成异步解耦重新审视依赖方向让其中一个模块只依赖接口而不是具体实现。解决循环依赖的关键是让模块之间的依赖关系变成单向的、自上而下的结构。6.4 贫血模型业务逻辑流失贫血模型指领域对象只有数据、没有行为所有的业务逻辑都写在 Service 层。短期的结果是 Service 层越来越膨胀长远的后果是业务规则无处安放。贫血模型不是绝对的错误简单的 CRUD 场景完全可以接受。但当业务逻辑复杂时让领域对象自己“持有行为”是更好的选择。例如Order对象不仅仅有getStatus()还可以有confirm()、cancel()这些能改变自身状态的方法而不是让OrderService直接修改状态字段。7. AI 时代设计能力反而更重要了7.1 AI 生成代码的隐患现在开发者越来越多地使用 AI 工具生成代码。AI 能快速产出可运行的代码但它理解的是“当前这个 prompt 所描述的需求”不具备对项目长期演进的设计视角。它不会主动考虑开闭原则不会自动评估这个功能在未来三个月内会不会变化更不会为了整个模块的可测试性去调整结构。因此AI 生成代码的体验往往是“第一版跑得很快第二次需求变更时发现根本改不动。”原因很简单——AI 的生成结果更倾向于直接、具体、站在单点需求上而不是模块化的、面向变化的。7.2 用设计思维重构 AI 输出把 AI 当成一个非常熟练、但缺乏设计判断力的“初级开发者”是更合理的使用方式。拿到 AI 生成的代码后建议按以下流程过一遍它是否直接修改了核心方法有没有新增策略、新增状态、新增分支如果这个规则变了需要改动的范围是多大有没有办法把这些逻辑收敛到一个独立类中一句话总结AI 负责生成人负责设计。设计能力越强使用 AI 的效率越高——因为你能更快判断哪些代码可复用哪些代码只是“能跑”。7.3 设计评审与设计文档在团队协作中设计评审是防止设计退化最重要的手段。不必写厚重的设计文档只要在需求排期时花 15 分钟回答下面几个问题这次需求会影响哪些模块变更点是在已有抽象内部还是需要新增抽象核心业务逻辑是否容易单元测试是否引入了不必要的新依赖把这些问题作为每一次技术评审的固定 check-list比偶尔做一次“架构整改”更有效。8. 设计与工程实践建议结合多年业务项目经验下面是我认为最值得落地到日常开发的工程建议。8.1 命名即设计好的命名是成本最低的设计。方法名要表达“做什么”而不是“怎么做”变量名要表达业务含义而不是类型含义。getData()就比getUserListByCondition()差得多createOrder()就比handle()清晰得多。8.2 面向测试设计如果一个类很难写单元测试设计通常也有问题。写测试不是额外工作而是对设计的检验。推荐在写业务逻辑前先思考这个逻辑能否在没有任何框架的情况下通过构造函数注入依赖并完成测试如果做不到就先调整结构。8.3 变更成本意识每次修改代码时在心里算一笔账这个改动影响了多少文件如果超过 5 个就要停下来问自己是不是设计上存在问题这个“影响文件数量”就是设计的量化指标。8.4 保持小步重构重构不是一次大爆炸而是持续进行的小步优化。每次开发新需求时顺手把经过的坏味道代码清理一点把一个过长的 if / else 换成分支映射表把一个只在一个地方使用的工具类方法内联回去。小步重构风险低、反馈快能持续保持代码健康度。8.5 安全意识优先设计面向扩展的同时也要关注安全边界。比如导入的第三方库要确认版本与许可证暴露的接口要做权限校验处理用户输入时要防范注入。一个好的设计应该让安全校验成为流程的一部分而不是事后补丁。9. 总结与学习路线回到文章开头的那个问题我们是否忘记了如何设计答案因人而异。但至少可以确定的是在当前大量使用框架和 AI 辅助编码的环境下设计已经不只是一个加分项而是保证代码可持续演进的必需品。不会设计的代码在业务快速变化时会以成倍的姿态阻碍研发效率。本文回顾了设计的基本层次分析了 SOLID 原则在真实代码中的价值并用订单折扣系统演示了从 if / else 到策略模式的重构过程。同时也讨论了分层架构、模块化、防腐层以及 AI 时代设计能力的重要性。如果你希望进一步系统学习设计可以参考以下路线先掌握设计原则SOLID、DRY、YAGNI、组合优于继承再熟悉高频设计模式策略、工厂、模板方法、观察者、装饰器接着练习重构手法IDEA 中抽取方法、提取接口、移动类等快捷键然后阅读架构入门书籍理解分层与模块边界最后参与真实项目的设计评审从批评和被批评中提升判断力。设计能力的提升没有捷径只能通过“看代码—写代码—重构代码—评审代码”的循环反复打磨。下一次当你面对一个看起来“能跑但改不动”的模块时不妨停下来想一想如果是现在重新设计我会怎么划分边界如果你也遇到过因为“没有设计”而导致的维护难题或者有更好的重构经验欢迎在评论区分享。觉得这篇文章有帮助的话可以收藏备用后续找时间对照这个思路检查一下自己的项目代码。
返回列表