
几乎所有开发者都遇到过这样的场景:需求仅做微小调整,却要修改整个模块的代码;新增一个简单功能,牵一发而动全身;线上出现bug,翻遍十几层抽象才定位到问题根源。这些问题的本质,并非代码写得不够多,而是从设计之初就违背了最基础的架构设计原则。
SOLID原则由面向对象设计领域的权威专家Robert C. Martin在《敏捷软件开发:原则、模式与实践》中系统提出,包含五个相互关联、协同生效的子原则,是面向对象设计的核心规范。

一个类或者模块,应该只有一个引起它变化的原因。
SRP的核心是高内聚,将变化原因相同的逻辑聚合在一起,将变化原因不同的逻辑拆分开来。软件的变更永远存在,一个类承担的职责越多,耦合的变化原因就越多,后续修改的风险就越高,维护成本也会指数级上升。
很多开发者将SRP误解为“一个类只能做一件事”,甚至极端到“一个类只能有一个方法”。这是对原则的片面解读:SRP的核心是“一个变化原因”,而非“一个动作”。比如用户数据的增删改查,都属于“用户数据持久化”这同一个职责,对应同一个变化原因,完全可以放在同一个Repository类中,并不违背SRP。
public class UserService {
public void saveUser(User user) {
String sql = "INSERT INTO users (id, username, email) VALUES (?, ?, ?)";
}
public boolean hasAdminPermission(Long userId) {
return false;
}
public void sendWelcomeEmail(User user) {
}
}
这个类存在三个完全独立的变化原因:数据存储规则变更、权限校验规则变更、邮件发送逻辑变更,任何一个规则的调整都需要修改这个类,极易引发关联bug。

public interface UserRepository {
void save(User user);
User findById(Long userId);
}
publicinterface UserPermissionService {
boolean hasAdminPermission(Long userId);
}
publicinterface UserNotificationService {
void sendWelcomeEmail(User user);
}
publicclass UserService {
privatefinal UserRepository userRepository;
privatefinal UserPermissionService permissionService;
privatefinal UserNotificationService notificationService;
public UserService(UserRepository userRepository,
UserPermissionService permissionService,
UserNotificationService notificationService) {
this.userRepository = userRepository;
this.permissionService = permissionService;
this.notificationService = notificationService;
}
public void registerUser(User user) {
userRepository.save(user);
notificationService.sendWelcomeEmail(user);
}
public boolean checkUserAdminPermission(Long userId) {
return permissionService.hasAdminPermission(userId);
}
}
拆分后的每个类都只有一个变化原因,职责边界清晰,修改其中任何一个类都不会影响其他类的逻辑,大幅降低了维护成本。
软件实体(类、模块、函数)应该对扩展开放,对修改关闭。
OCP的核心目标是降低代码变更带来的风险。已有经过测试的稳定代码,修改时极易引入新的bug,还需要重新进行全量回归测试;而通过扩展新增代码,不会影响原有逻辑的稳定性,也无需对原有功能进行回归测试。
OCP的落地核心是面向抽象编程:用抽象定义稳定的顶层规范,用实现实现可变的业务细节。当需求变更时,仅需新增实现类扩展功能,无需修改原有抽象和已有的稳定实现。
public class PaymentService {
public void processPayment(String paymentType, double amount) {
if ("WECHAT_PAY".equals(paymentType)) {
System.out.println("处理微信支付,金额:" + amount);
} else if ("ALIPAY".equals(paymentType)) {
System.out.println("处理支付宝支付,金额:" + amount);
}
}
}
这段代码中,新增任何一种支付方式,都需要修改processPayment方法的原有代码,新增else if分支,违背了“对修改关闭”的原则,极易在修改时影响已有支付方式的逻辑。
public interface PaymentStrategy {
void processPayment(double amount);
}
publicclass WechatPayStrategy implements PaymentStrategy {
@Override
public void processPayment(double amount) {
System.out.println("处理微信支付,金额:" + amount);
}
}
publicclass AlipayStrategy implements PaymentStrategy {
@Override
public void processPayment(double amount) {
System.out.println("处理支付宝支付,金额:" + amount);
}
}
publicclass UnionPayStrategy implements PaymentStrategy {
@Override
public void processPayment(double amount) {
System.out.println("处理银联支付,金额:" + amount);
}
}
publicclass PaymentService {
privatefinal PaymentStrategy paymentStrategy;
public PaymentService(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(double amount) {
paymentStrategy.processPayment(amount);
}
}
重构后的代码,新增支付方式仅需新增PaymentStrategy的实现类,无需修改PaymentService和已有的支付实现类,完全符合“对扩展开放,对修改关闭”的原则。
所有引用基类的地方,必须能透明地使用其子类的对象,而不会产生任何逻辑错误或行为异常。该原则由计算机科学家Barbara Liskov在1987年的面向对象编程大会上首次提出,是面向对象继承的核心设计规范。
LSP的核心是规范继承的使用边界,要求子类必须完全遵守父类的行为约定,不能改变父类方法的前置条件、后置条件和不变量。很多开发者乱用继承,通过重写父类方法改变了原有行为,导致父类能正常运行的逻辑,子类运行时出现异常,这就是典型的违背LSP的场景。
很多开发者会将LSP和Java的多态特性混为一谈,这里需要明确区分:
public class Rectangle {
protecteddouble width;
protecteddouble height;
public void setWidth(double width) {
this.width = width;
}
public void setHeight(double height) {
this.height = height;
}
public double getArea() {
return width * height;
}
}
publicclass Square extends Rectangle {
@Override
public void setWidth(double width) {
this.width = width;
this.height = width;
}
@Override
public void setHeight(double height) {
this.height = height;
this.width = height;
}
}
publicclass LspTest {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
testRectangleArea(rectangle);
Rectangle square = new Square();
testRectangleArea(square);
}
public static void testRectangleArea(Rectangle rectangle) {
rectangle.setWidth(3);
rectangle.setHeight(4);
double expectedArea = 12.0;
double actualArea = rectangle.getArea();
System.out.printf("预期面积%.1f,实际面积%.1f%n", expectedArea, actualArea);
}
}
这段代码中,Square类继承了Rectangle类,重写了setWidth和setHeight方法,改变了父类的行为约定:父类中setWidth仅会修改宽度,不会影响高度,而子类中setWidth会同时修改宽高,导致父类能正常运行的testRectangleArea方法,传入子类对象时出现逻辑错误,完全违背了LSP。
public interface Shape {
double getArea();
}
public record Rectangle(double width, double height) implements Shape {
@Override
public double getArea() {
return width * height;
}
}
public record Square(double side) implements Shape {
@Override
public double getArea() {
return side * side;
}
}
publicclass LspCorrectTest {
public static void main(String[] args) {
Shape rectangle = new Rectangle(3, 4);
System.out.println("长方形面积:" + rectangle.getArea());
Shape square = new Square(4);
System.out.println("正方形面积:" + square.getArea());
}
}
重构后的代码,通过不可变的record类实现Shape接口,Rectangle和Square完全独立,各自遵守Shape接口的行为约定,所有引用Shape接口的地方,都能透明地使用两个实现类,不会出现逻辑异常,完全符合LSP规范。
客户端不应该依赖它不需要的接口。
ISP的核心是最小依赖,要求将臃肿的大接口拆分为细粒度的小接口,每个接口仅定义单一维度的行为规范,让客户端仅依赖它实际需要的接口,避免强制客户端实现不需要的方法,减少系统的耦合度。
ISP和SRP经常被开发者混淆,这里明确二者的核心区别:
public interface Animal {
void fly();
void swim();
void run();
}
publicclass Dog implements Animal {
@Override
public void fly() {
thrownew UnsupportedOperationException("狗不会飞");
}
@Override
public void swim() {
System.out.println("狗在游泳");
}
@Override
public void run() {
System.out.println("狗在奔跑");
}
}
publicclass Fish implements Animal {
@Override
public void fly() {
thrownew UnsupportedOperationException("鱼不会飞");
}
@Override
public void swim() {
System.out.println("鱼在游泳");
}
@Override
public void run() {
thrownew UnsupportedOperationException("鱼不会奔跑");
}
}
这段代码中,Animal接口包含了三个不同维度的行为,导致实现类必须强制实现不需要的方法,只能抛出异常或空实现,不仅增加了无用代码,还提高了系统的耦合度:当Animal接口的fly方法发生变更时,所有实现类都需要修改,哪怕这个方法对它们完全无用。
public interface Flyable {
void fly();
}
publicinterface Swimmable {
void swim();
}
publicinterface Runnable {
void run();
}
publicclass Dog implements Swimmable, Runnable {
@Override
public void swim() {
System.out.println("狗在游泳");
}
@Override
public void run() {
System.out.println("狗在奔跑");
}
}
publicclass Fish implements Swimmable {
@Override
public void swim() {
System.out.println("鱼在游泳");
}
}
publicclass Bird implements Flyable, Runnable {
@Override
public void fly() {
System.out.println("鸟在飞翔");
}
@Override
public void run() {
System.out.println("鸟在地面奔跑");
}
}
重构后的代码,将臃肿的大接口拆分为三个细粒度的行为接口,每个实现类仅需要实现自己实际需要的接口,完全不会依赖不需要的方法。当某个行为接口发生变更时,仅会影响实际实现该接口的类,大幅降低了系统的耦合度,完全符合ISP规范。
高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
DIP的核心是解耦高层业务逻辑与低层实现细节。传统的分层设计中,高层的业务模块直接依赖低层的工具模块,导致低层实现发生变更时,高层业务模块必须跟着修改,系统耦合度极高。DIP通过引入抽象层,让高层和低层都依赖抽象,抽象定义稳定的规范,细节实现可变的逻辑,实现了高层和低层的解耦。
DIP的落地核心是面向接口编程,而非面向实现编程。在实际开发中,就是通过接口或抽象类定义顶层规范,高层模块仅调用接口的方法,不直接依赖具体的实现类;低层模块实现接口,通过依赖注入的方式注入到高层模块中。
public class MySQLOrderRepository {
public void saveOrder(Order order) {
}
}
public class OrderService {
private final MySQLOrderRepository orderRepository = new MySQLOrderRepository();
public void createOrder(Order order) {
orderRepository.saveOrder(order);
}
}
这段代码中,高层的OrderService直接依赖低层的MySQLOrderRepository实现类,当需要将数据库从MySQL切换为PostgreSQL时,必须修改OrderService的代码,完全违背了DIP,系统耦合度极高,难以扩展和维护。
public interface OrderRepository {
void saveOrder(Order order);
}
publicclass MySQLOrderRepository implements OrderRepository {
@Override
public void saveOrder(Order order) {
}
}
publicclass PostgreSQLOrderRepository implements OrderRepository {
@Override
public void saveOrder(Order order) {
}
}
publicclass OrderService {
privatefinal OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void createOrder(Order order) {
orderRepository.saveOrder(order);
}
}
重构后的代码,引入了OrderRepository抽象接口,高层的OrderService仅依赖这个接口,不关心具体的实现类;低层的数据库实现类都实现了这个接口,切换数据库仅需替换实现类,无需修改OrderService的代码,完全符合DIP规范。同时,这种设计也天然符合OCP,新增存储方式仅需新增实现类,无需修改原有代码。
SOLID的五个原则不是孤立存在的,而是相互协同、相互支撑的。下面通过一个完整的订单折扣计算场景,展示五个原则如何协同落地。
// 符合ISP:细粒度的折扣策略接口,仅定义单一行为
publicinterface DiscountStrategy {
double calculate(double originalPrice, Order order);
}
// 符合SRP:仅处理普通会员折扣逻辑,唯一变化原因是会员折扣规则变更
publicclass MemberDiscountStrategy implements DiscountStrategy {
@Override
public double calculate(double originalPrice, Order order) {
returnswitch (order.getMemberLevel()) {
case1 -> originalPrice * 0.9;
case2 -> originalPrice * 0.8;
case3 -> originalPrice * 0.7;
default -> originalPrice;
};
}
}
// 符合SRP:仅处理节日促销折扣逻辑,唯一变化原因是促销规则变更
publicclass FestivalDiscountStrategy implements DiscountStrategy {
@Override
public double calculate(double originalPrice, Order order) {
if (order.isFestivalPromotion()) {
return originalPrice * 0.85;
}
return originalPrice;
}
}
// 符合LSP:完全遵守DiscountStrategy的行为约定,不改变原有逻辑预期
publicclass BulkDiscountStrategy implements DiscountStrategy {
@Override
public double calculate(double originalPrice, Order order) {
if (order.getQuantity() >= 10) {
return originalPrice * 0.8;
}
return originalPrice;
}
}
// 符合DIP:仅依赖DiscountStrategy抽象,不依赖具体实现类
// 符合OCP:新增折扣策略仅需新增实现类,无需修改原有代码
// 符合SRP:仅负责折扣计算的流程编排,唯一变化原因是折扣计算流程变更
publicclass DiscountService {
privatefinal List<DiscountStrategy> discountStrategies;
public DiscountService(List<DiscountStrategy> discountStrategies) {
this.discountStrategies = discountStrategies;
}
public double calculateFinalPrice(double originalPrice, Order order) {
double finalPrice = originalPrice;
for (DiscountStrategy strategy : discountStrategies) {
finalPrice = strategy.calculate(finalPrice, order);
}
return finalPrice;
}
}
这段代码完整落地了SOLID的五个原则,职责边界清晰,耦合度极低,扩展能力极强,后续新增任何折扣规则,都仅需新增DiscountStrategy的实现类,无需修改原有代码,完全不会影响已有的稳定逻辑。
DRY原则全称Don't Repeat Yourself,由Andrew Hunt和David Thomas在《程序员修炼之道》中首次提出,是软件工程中最基础、最常用的设计原则之一。
系统中的每一处知识和业务逻辑,都必须有一个单一、明确、权威的表述。
DRY的核心是消除知识的重复,而非简单的消除代码行的重复。软件系统的维护成本,很大程度上来自于重复的逻辑:当同一个业务规则在多个地方重复实现时,后续规则发生变更,必须修改所有重复的地方,一旦遗漏就会出现数据不一致的bug,维护成本会随着重复次数指数级上升。
很多开发者将DRY误解为“不能有任何重复的代码行”,甚至将两段业务逻辑完全不同、仅代码结构相似的代码硬抽成一个通用方法,这是对DRY的严重误解。这里明确区分两个核心概念:
DRY的违背场景不仅限于代码重复,还包括系统中所有知识的重复,常见的场景有以下几类:
对于重复的业务逻辑,核心是抽成单一的权威实现,所有场景都复用这个实现,示例如下:
public class UserOrderService {
public double calculateOrderPrice(double originalPrice, int memberLevel) {
double discount = switch (memberLevel) {
case1 -> 0.9;
case2 -> 0.8;
case3 -> 0.7;
default -> 1.0;
};
return originalPrice * discount;
}
}
publicclass PromotionService {
public double calculatePromotionPrice(double originalPrice, int memberLevel) {
double discount = switch (memberLevel) {
case1 -> 0.9;
case2 -> 0.8;
case3 -> 0.7;
default -> 1.0;
};
return originalPrice * discount * 0.95;
}
}
public class DiscountCalculator {
public static double calculateMemberDiscount(double originalPrice, int memberLevel) {
double discount = switch (memberLevel) {
case1 -> 0.9;
case2 -> 0.8;
case3 -> 0.7;
default -> 1.0;
};
return originalPrice * discount;
}
}
publicclass UserOrderService {
public double calculateOrderPrice(double originalPrice, int memberLevel) {
return DiscountCalculator.calculateMemberDiscount(originalPrice, memberLevel);
}
}
publicclass PromotionService {
public double calculatePromotionPrice(double originalPrice, int memberLevel) {
double memberDiscountedPrice = DiscountCalculator.calculateMemberDiscount(originalPrice, memberLevel);
return memberDiscountedPrice * 0.95;
}
}
重构后的代码,会员折扣逻辑只有DiscountCalculator这一个权威来源,后续折扣规则变更,仅需修改这一处代码,所有复用的场景都会自动生效,完全符合DRY原则。
DRY的最大坑点是过度抽象,很多开发者为了消除代码重复,将两个业务逻辑完全不同、仅代码结构相似的方法硬抽成一个通用方法,为了兼容不同的业务场景,在方法中加入大量的if-else分支和入参标志位,最终导致这个通用方法变得极其臃肿,后续任何一个场景的逻辑变更,都需要修改这个通用方法,反而提高了耦合度和维护成本。
这里给出DRY落地的两个核心判断标准:
KISS原则全称Keep It Simple, Stupid,最早由美国海军在1960年代的工程设计中提出,核心思想是“大多数系统,保持简单比复杂更有效”,后来被广泛应用于软件工程领域,成为核心的设计原则之一。
软件设计的核心目标是简洁,要避免任何不必要的复杂度,能用简单方案解决的问题,绝对不要用复杂方案。
KISS的核心是控制复杂度。软件系统的bug率、维护成本,和系统的复杂度正相关:代码越复杂,越难理解,越难排查问题,越容易出现bug。很多开发者为了炫技,或者为了提前应对未来可能出现的需求,在设计时加入大量不必要的抽象、设计模式和扩展点,把简单的问题搞得极其复杂,最终导致系统难以维护,这就是典型的违背KISS原则的场景。
很多开发者将KISS误解为“不用设计模式、不用做架构设计,怎么简单怎么写”,甚至写出大量面条式代码,这是对KISS的严重误解。KISS不是拒绝设计,而是拒绝不必要的复杂度:合理的架构设计和设计模式,能让系统的逻辑更清晰、更易维护,本质上是降低了系统的复杂度,完全符合KISS原则;而为了设计而设计,为了炫技而加入不必要的抽象,才是违背KISS的核心。
代码层面的KISS核心是可读性优先,示例如下:
public class OrderService {
public double calculateFinalPrice(Order order) {
return order.getItems().stream()
.mapToDouble(i -> i.getPrice() * i.getQuantity())
.sum() * (order.getMemberLevel() > 0 ? 1 - 0.1 * order.getMemberLevel() : 1)
* (order.getCreateTime().getMonthValue() == 12 ? 0.85 : 1)
* (order.getItems().size() > 10 ? 0.8 : 1);
}
}
这段代码把所有逻辑都写在一行里,虽然代码行数少,但是可读性极差,后续修改任何一个折扣规则,都需要读懂整行代码,极易出现bug,违背KISS原则。
public class OrderService {
public double calculateFinalPrice(Order order) {
double totalAmount = calculateTotalAmount(order);
double memberDiscountedAmount = applyMemberDiscount(totalAmount, order.getMemberLevel());
double festivalDiscountedAmount = applyFestivalDiscount(memberDiscountedAmount, order.getCreateTime());
double bulkDiscountedAmount = applyBulkDiscount(festivalDiscountedAmount, order.getItems().size());
return bulkDiscountedAmount;
}
private double calculateTotalAmount(Order order) {
return order.getItems().stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}
private double applyMemberDiscount(double amount, int memberLevel) {
returnswitch (memberLevel) {
case1 -> amount * 0.9;
case2 -> amount * 0.8;
case3 -> amount * 0.7;
default -> amount;
};
}
private double applyFestivalDiscount(double amount, LocalDateTime createTime) {
if (createTime.getMonthValue() == 12) {
return amount * 0.85;
}
return amount;
}
private double applyBulkDiscount(double amount, int itemCount) {
if (itemCount >= 10) {
return amount * 0.8;
}
return amount;
}
}
重构后的代码,将每个逻辑拆分为独立的小方法,每个方法只做一件事,逻辑清晰,可读性极强,后续修改任何一个折扣规则,都只需要修改对应的方法,不会影响其他逻辑,完全符合KISS原则。
架构层面的KISS核心是最小够用原则:
很多开发者会觉得KISS和SOLID、DRY是对立的,比如SOLID要求拆分职责,会增加类的数量,是不是违背KISS?其实不然,三大原则的最终目标是完全一致的,都是为了构建出易维护、易扩展、低复杂度的系统:
下面通过一个完整的电商订单创建场景,展示三大原则如何协同落地,构建出简洁、易维护、易扩展的代码体系。
// 符合ISP、SRP:细粒度的接口,单一职责
publicinterface OrderRepository {
void save(Order order);
}
publicinterface StockService {
void deductStock(Long productId, int quantity);
}
publicinterface PaymentService {
PaymentResult processPayment(Order order);
}
publicinterface OrderNotificationService {
void sendOrderSuccessNotification(Order order);
}
// 符合DRY:统一的参数校验逻辑,单一权威来源
publicclass OrderValidator {
public static void validateOrder(Order order) {
if (order.getUserId() == null) {
thrownew IllegalArgumentException("用户ID不能为空");
}
if (order.getItems() == null || order.getItems().isEmpty()) {
thrownew IllegalArgumentException("订单商品不能为空");
}
order.getItems().forEach(item -> {
if (item.getProductId() == null) {
thrownew IllegalArgumentException("商品ID不能为空");
}
if (item.getQuantity() <= 0) {
thrownew IllegalArgumentException("商品数量必须大于0");
}
});
}
}
// 符合DIP:仅依赖抽象接口,不依赖具体实现
// 符合SRP:仅负责订单创建的流程编排,唯一变化原因是订单创建流程变更
// 符合OCP:新增流程节点仅需扩展,无需修改核心流程
// 符合KISS:逻辑清晰,每一步都简洁明确,没有不必要的复杂度
// 符合DRY:所有子逻辑都复用统一的实现,没有重复代码
publicclass OrderCreateService {
privatefinal OrderRepository orderRepository;
privatefinal StockService stockService;
privatefinal PaymentService paymentService;
privatefinal OrderNotificationService notificationService;
public OrderCreateService(OrderRepository orderRepository,
StockService stockService,
PaymentService paymentService,
OrderNotificationService notificationService) {
this.orderRepository = orderRepository;
this.stockService = stockService;
this.paymentService = paymentService;
this.notificationService = notificationService;
}
public Order createOrder(Order order) {
// 1. 参数校验
OrderValidator.validateOrder(order);
// 2. 扣减库存
order.getItems().forEach(item ->
stockService.deductStock(item.getProductId(), item.getQuantity())
);
// 3. 保存订单
orderRepository.save(order);
// 4. 处理支付
PaymentResult paymentResult = paymentService.processPayment(order);
if (!paymentResult.isSuccess()) {
thrownew RuntimeException("支付失败:" + paymentResult.getErrorMessage());
}
// 5. 发送通知
notificationService.sendOrderSuccessNotification(order);
// 6. 返回结果
return order;
}
}
这段代码完整协同落地了三大原则:
所有的设计原则,最终目标都是为了让代码更易维护、更易扩展、更少出bug。原则是指导工具,不是教条。很多开发者死记硬背原则的定义,为了符合原则而强行拆分、强行抽象,反而把代码搞得更复杂,违背了原则的初衷。
避坑指南:每次做设计决策时,先问自己一个问题:这个设计,是否真的能让代码更易维护、更易扩展?如果答案是否定的,哪怕完全符合原则的定义,也不要这么做。
很多开发者在设计时,总想着“未来可能会有这个需求,提前做好扩展”,于是加入大量的扩展点、抽象层和兼容逻辑,把简单的业务场景搞得极其复杂。而实际上,大部分提前设计的扩展点,永远都不会被用到,反而增加了当前系统的维护成本。
避坑指南:遵循“YAGNI”原则(You Aren't Gonna Need It),只设计和实现当前业务真正需要的功能,不要为了未来可能的需求提前做过度设计。当需求真正到来时,再基于当时的场景做重构和扩展,成本会低得多。
为了消除代码重复,把两个业务逻辑完全不同、仅代码结构相似的方法硬抽成一个通用方法,为了兼容不同场景加入大量的if-else分支,最终导致通用方法变得极其臃肿,维护成本远超重复的代码。
避坑指南:DRY的核心是“知识重复”,不是“代码行重复”。只有当两段代码对应同一个业务规则、同一个变化原因时,才需要做抽象合并。否则,哪怕代码完全一样,也不要强行抽象。
很多开发者对原则的理解过于片面,走向极端:
避坑指南:全面理解原则的核心本质,而非表面定义。所有原则都有适用场景,需要根据实际业务场景灵活运用,平衡好设计和复杂度,不要走向极端。
SOLID、DRY、KISS三大架构设计原则,不是高深的理论,而是软件工程领域数十年沉淀下来的、经过无数项目验证的最佳实践。它们的核心目标完全一致:帮助开发者构建出高内聚、低耦合、易维护、易扩展的代码体系,从根源上避免屎山代码的产生。