设计模式 7 大设计原则(SOLID + 迪米特 + 合成复用)
一句话总纲:目的是降低耦合、提高复用、易于扩展、方便维护
1. 单一职责原则 SRP(Single Responsibility Principle)
定义:一个类只负责一件事,只有一个引起它变化的原因。
类的职责尽量单一,不要写万能类。
❌ 反例:User类,同时包含用户信息保存、密码加密、发送邮件、生成报表。用户改字段、邮件模板变都会改这个类。 ✅ 正例:
User:仅用户实体属性UserRepository:数据库保存EmailService:发邮件
2. 开闭原则 OCP(Open-Closed Principle)
定义:对扩展开放,对修改关闭。新增功能尽量加代码,不要修改原有成熟代码。
❌ 反例:图形计算面积,if else判断圆形、矩形,新增三角形要修改原有getArea方法。 ✅ 正例:抽象Shape接口,有getArea();圆形、矩形、三角形各自实现接口。新增图形不用改旧代码,新增一个类即可。
3. 里氏替换原则 LSP(Liskov Substitution Principle)
定义:子类可以完全替换父类,程序行为不会出错。子类不能破坏父类原有逻辑。
❌ 反例:父类Rectangle有宽高set;子类Square正方形继承矩形,setWidth同时修改height。把正方形当矩形传入计算函数会出错。 ✅ 正例:正方形和矩形不继承,共同实现一个图形接口。
核心:继承不能随便用,子类不能重写后改变父类原有行为。
4. 接口隔离原则 ISP(Interface Segregation Principle)
定义:不要大而臃肿的接口,拆分细接口;类不需要的方法,就不要强迫它实现。
❌ 反例:一个大接口Worker包含work()、eat()、sleep();机器人实现这个接口,被迫空实现eat、sleep。 ✅ 正例:拆成Workable、Eatable、Sleepable多个小接口。机器人只实现Workable。
5. 依赖倒置原则 DIP(Dependency Inversion Principle)
定义:高层模块不要依赖低层模块,二者都依赖抽象;抽象不依赖细节,细节依赖抽象。
❌ 反例:业务Service直接new MySQLDao,Service强依赖MySQL实现,换成Oracle就要改Service代码。 ✅ 正例:定义UserDao接口;MySQLUserDao、OracleUserDao实现接口。Service依赖UserDao接口,运行时注入具体实现。(Spring依赖注入就是典型)
6. 迪米特法则 LoD(最少知道原则)
定义:一个对象尽量少了解其他对象内部细节,只和直接朋友通信,不要访问陌生对象。
不要链式调用
a.getB().getC().doSomething(),这就是典型违反迪米特。
❌ 反例:老板直接找员工的下属安排任务,老板知道太多内部层级。 ✅ 正例:老板只找主管,主管再安排下属;老板不接触底层员工。
符合迪米特的写法:
// B类新增方法,把C的动作包起来 class B{ private C c; public void doCWork(){ c.doSomething(); // B自己和C交互,A看不到C } } // A只和B打交道,完全不知道C的存在 B b = a.getB(); b.doCWork();7. 合成复用原则 CRP(组合优于继承)
定义:优先使用组合/聚合来复用代码,少用继承。
继承是强耦合;组合是弱耦合,更灵活。
❌ 反例:Car继承Engine来复用引擎代码,引擎是车的部件,不是“车是引擎”。继承是is-a关系。 ✅ 正例:Car里面持有Engine对象(组合),class Car { private Engine engine; },可以动态更换不同引擎。
快速记忆口诀
单、开、里、接、依,迪米特,合成优先SOLID 对应前5条:SRP OCP LSP ISP DIP
面试精简版
- 单一职责:一个类只做一件事
- 开闭原则:扩展开放,修改关闭
- 里氏替换:子类可替换父类,不破坏逻辑
- 接口隔离:细分接口,不强迫实现不需要方法
- 依赖倒置:依赖抽象,不依赖具体实现
- 迪米特法则:最少知道,减少对象间耦合
- 合成复用:优先组合,少用继承