1. 单一职责原则让每个类专注做好一件事我第一次接手一个电商项目时发现有个3000多行的Order类既处理订单创建逻辑又负责支付流程还管着物流跟踪。当需要修改支付接口时整个类有20多个地方需要测试这就是典型的违反单一职责原则SRP。核心定义一个类应该只有一个引起它变化的原因。换句话说每个类只负责一项具体功能。实际案例我们重构后的设计// 订单基本信息管理 class Order { private String orderId; private Date createTime; // 其他基础字段... } // 支付处理专用类 class PaymentProcessor { public boolean processPayment(Order order, Payment payment) { // 支付逻辑 } } // 物流跟踪专用类 class ShippingTracker { public void updateShippingStatus(Order order) { // 物流状态更新 } }常见误区把相关性不强的功能塞进同一个类在方法级别违反SRP一个方法做多件事过度拆分导致类爆炸实践建议当发现类名中出现And、Or时就要警惕方法超过50行就该考虑拆分修改代码时如果影响不相关功能说明违反了SRP我在实际项目中总结出一个判断标准如果一个功能的修改会迫使你修改完全不相关的测试用例这个类就肯定违反了SRP。2. 里氏替换原则继承关系的安全使用指南去年团队里有个新手程序员写了个这样的代码class Rectangle { protected int width, height; void setWidth(int w) { width w; } void setHeight(int h) { height h; } } class Square extends Rectangle { Override void setWidth(int w) { super.setWidth(w); super.setHeight(w); // 正方形宽高同步修改 } Override void setHeight(int h) { super.setHeight(h); super.setWidth(h); // 正方形宽高同步修改 } }看起来没问题但看这个测试用例void testArea(Rectangle r) { r.setWidth(5); r.setHeight(4); assert r.getArea() 20; // 对Square会失败 }这就是典型的违反里氏替换原则LSP。核心思想子类必须能够完全替换父类且不影响程序正确性。关键守则子类不要重写父类非抽象方法子类方法前置条件参数要求要比父类宽松子类方法后置条件返回结果要比父类严格解决方案abstract class Shape { abstract int getArea(); } class Rectangle extends Shape { // 实现略 } class Square extends Shape { // 实现略 }实际项目中我常用这个检查方法把项目中所有父类引用替换为子类引用单元测试应该全部通过。3. 依赖倒置原则面向接口编程的威力记得2016年做支付系统时最初是这样写的class PaymentService { private AlipayProcessor alipay; // 直接依赖具体实现 void processPayment() { alipay.execute(); } }当需要增加微信支付时问题来了——要修改PaymentService。这违反了依赖倒置原则DIP。正确做法interface PaymentProcessor { void execute(); } class AlipayProcessor implements PaymentProcessor { Override void execute() { /* 支付宝实现 */ } } class WechatProcessor implements PaymentProcessor { Override void execute() { /* 微信实现 */ } } class PaymentService { private PaymentProcessor processor; // 依赖抽象 PaymentService(PaymentProcessor processor) { this.processor processor; } }三层定义高层模块不依赖低层模块都该依赖抽象抽象不依赖细节细节依赖抽象实战技巧使用Spring等框架的依赖注入工厂模式创建具体实例配置化决定具体实现在微服务架构中这个原则尤为重要。我们团队现在所有服务间的调用都通过接口定义具体实现可以随时替换。4. 接口隔离原则避免胖接口的陷阱最近review代码时看到一个这样的接口interface UserService { void login(); void register(); void resetPassword(); void updateProfile(); void deleteAccount(); void sendEmail(); void generateReport(); }这个接口的问题在于客户端被迫实现不需要的方法修改一个方法会影响所有实现类违反单一职责原则解决方案interface AuthService { void login(); void register(); void resetPassword(); } interface ProfileService { void updateProfile(); void deleteAccount(); } interface NotificationService { void sendEmail(); } interface ReportService { void generateReport(); }设计要点接口方法数控制在3-5个按客户端需求拆分接口使用组合而非继承我在实际项目中会这样验证如果实现类中有空方法或抛出UnsupportedOperationException就说明接口需要拆分。5. 迪米特法则降低耦合的黄金准则来看一个常见的问题代码class OrderService { public void process(Order order) { Customer customer order.getCustomer(); Address address customer.getAddress(); String city address.getCity(); // 使用city... } }这违反了迪米特法则LoD因为OrderService知道了太多它不需要知道的细节。改进方案class OrderService { public void process(Order order) { String city order.getCustomerCity(); // 使用city... } } class Order { public String getCustomerCity() { return customer.getAddress().getCity(); } }核心思想一个对象应该对其他对象保持最少的了解只与直接朋友通信。朋友包括当前对象本身成员变量对象方法参数对象方法返回值对象典型场景避免链式调用a.getB().getC()使用门面模式封装子系统减少方法参数中的陌生类在分布式系统中这个原则尤为重要。我们要求服务间API返回完整数据避免客户端多次调用。6. 开闭原则软件设计的终极目标开闭原则OCP是最重要的原则它要求对扩展开放对修改关闭。听起来很抽象看这个例子原始代码class Shape { enum Type { CIRCLE, SQUARE } Type type; // 其他字段... } class AreaCalculator { public double calculate(Shape shape) { switch (shape.type) { case CIRCLE: return calculateCircleArea(shape); case SQUARE: return calculateSquareArea(shape); default: throw new IllegalArgumentException(); } } }每新增一个形状类型就要修改AreaCalculator这违反了OCP。符合OCP的方案interface Shape { double calculateArea(); } class Circle implements Shape { Override double calculateArea() { /* 实现 */ } } class Square implements Shape { Override double calculateArea() { /* 实现 */ } }实现OCP的关键技术策略模式模板方法模式观察者模式装饰器模式在框架设计中我们常用插件架构来实现OCP。比如我们的规则引擎系统核心代码多年未变但通过新增规则实现类支持了数百种业务场景。7. 六大原则的综合应用实战现在来看一个综合案例设计一个跨平台文件处理系统。初始设计class FileProcessor { public void process(String filePath) { String ext getFileExtension(filePath); if (ext.equals(txt)) { // 处理文本文件 } else if (ext.equals(csv)) { // 处理CSV } // 更多判断... } }问题分析违反OCP新增格式需修改类违反SRP处理逻辑集中违反DIP依赖具体实现重构后设计interface FileHandler { boolean canHandle(String ext); void process(File file); } class TxtHandler implements FileHandler { // 实现略 } class CsvHandler implements FileHandler { // 实现略 } class FileProcessor { private ListFileHandler handlers; public void process(String filePath) { String ext getFileExtension(filePath); for (FileHandler handler : handlers) { if (handler.canHandle(ext)) { handler.process(new File(filePath)); return; } } throw new UnsupportedFormatException(ext); } }优化效果符合OCP新增格式只需添加Handler符合SRP每个Handler只处理一种格式符合DIP依赖抽象接口符合LSP所有Handler可互换符合ISPFileHandler接口精简符合LoDProcessor不关心处理细节在实际项目中这种设计使我们的文件处理系统支持格式从5种扩展到30多种而核心代码几乎没变。新同事只需要实现FileHandler接口就能添加新格式支持不需要理解整个系统。记住这些原则不是教条而是指导方针。我见过过度设计导致系统复杂化的案例也见过合理权衡带来的简洁高效。关键是要理解原则背后的思想创建更灵活、更易维护的软件。当原则之间出现冲突时要根据具体场景做出权衡这才是高级工程师的智慧。