恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
20260426_6种设计原则
首页
资讯中心
/
20260426_6种设计原则
20260426_6种设计原则
发布时间:2026/8/7 12:53:31
一、单一职责原则(Single Responsibility Principle, SRP)一个类只负责一个功能领域中的相应职责。所谓的职责就是指类变化的原因也就是业务需求。如果一个类有多于一个的原因被改变那么这个类就有超过两个及以上的职责。而单一职责约定一个类应该有且仅有一个改变类的原因。单一职责优点1降低了类的复杂度。一个类只负责一项职责比负责多项职责要简单得多。2提高了代码的可读性。一个类简单了可读性自然就提高了。3提高了系统的可维护性。代码的可读性高了并且修改一项职责对其他职责影响降低了可维护性自然就提高了。4变更引起的风险变低了。单一职责最大的优点就是修改一个功能对其他功能的影响显著降低。设计难点1职责划分职责没有量化标准该怎么细化细化后是否都要有一个接口或类这些都需要从实际的项目去考虑。2类的冗余适度原则本来一个类可以搞定的实现如果非得修改用户名一个类修改密码一个类来实现单一原则这样也会让你的类变得非常多反而不容易维护。实际项目设计中如果代码可以复用的情况下能不能用单一职责原则来划分类。方法上一定要保持单一职责原则便于代码调试让其他人员能更快更好的读懂代码、理解这个类或者方法的功能。模拟场景视频网站用户分类// 违背原则方案 public class VideoUserService { public void serveGrade(String userType) { if (VIP会员.equals(userType)) { System.out.println(VIP会员视频1080P蓝光); } else if (普通会员.equals(userType)) { System.out.println(普通会员视频720P超清); } else if (访客用户.equals(userType)) { System.out.println(访客用户视频480P高清); } } } // 单一职责原则改善代码 // 1.定义接口 public interface IVideoUserService { // 视频清晰级别1080P、720P、480P void definition(); // 广告播放方式有广告、无广告 void advertisement(); } // 2.实现类 // 访客用户 public class GuestVideoUserService implements IVideoUserService { public void definition() { System.out.println(访客用户视频480P高清); } public void advertisement() { System.out.println(访客用户视频有广告); } } // 普通会员 public class OrdinaryVideoUserService implements IVideoUserService { public void definition() { System.out.println(普通会员视频720P超清); } public void advertisement() { System.out.println(普通会员视频有广告); } } // VIP会员 public class VipVideoUserService implements IVideoUserService { public void definition() { System.out.println(VIP会员视频1080P蓝光); } public void advertisement() { System.out.println(VIP会员视频无广告); } }二、开闭原则(Open-Closed Principle, OCP)1. 对扩展开放当需要新增业务行为、新增功能你可以通过继承子类、实现接口、组合新类去拓展能力 允许新增成员变量、新增方法、覆写抽象方法来完成新需求。注意可以覆写抽象方法尽量不要随便覆写已经实现完毕的普通方法违背里氏替换原则风险。扩展优先新增方法而不是篡改旧方法行为。业务场景手机可以拨打电话现在需要新增扩展功能拍照、蓝牙传输分别演示三种不改动原有父类代码、只新增代码进行扩展的方式继承扩展新方法、接口实现、组合模式。// 原始稳定类之后绝不修改该类源码遵守对‑修改关闭 // 基础手机已经上线对外功能打电话后续需求增加时不会改动本类任何代码 public class Phone { public void call() { System.out.println(手机进行打电话); } } // 方式一创建子类继承父类添加全新方法继承扩展 // 只新增方法不去重写 call ()原有行为不受任何影响 // 智能手机继承Phone扩展拍照功能原有代码完全不动 public class SmartPhone extends Phone { // 新增扩展方法没有修改父类任何源码 public void takePhoto(){ System.out.println(智能手机拍照); } } // 优点简单快捷 // 缺点继承是强耦合一旦后续需要多组扩展容易造成子类爆炸禁止覆写父类已有方法否则容易违反里氏替换原则 // 实现接口编写新的实现类最常用开闭 依赖倒置 // 这种方案就是面向抽象扩展高层依赖接口符合依赖倒置原则。 // 抽取扩展能力的抽象接口新增实现类旧代码不动。 // 扩展能力抽象接口 public interface BluetoothAble { void bluetoothTransfer(); } // 新建实现类扩展蓝牙功能不去修改Phone public class BluetoothPhone implements BluetoothAble { // 可以持有原有手机复用原有打电话功能 private Phone phone new Phone(); public void call(){ phone.call(); } Override public void bluetoothTransfer() { System.out.println(开启蓝牙传输文件); } } // 方式三使用组合模式包装原有类优先推荐优于继承 // 组合在新类里面持有原始类成员变量委派调用原有功能新增功能。 // 原则多用组合少用继承 // CameraPhone 包装原有Phone扩展拍照原Phone源码零改动 public class CameraPhone { // 组合持有原始对象 private Phone phone; public CameraPhone(Phone phone) { this.phone phone; } // 委托调用原有功能 public void call(){ phone.call(); } // 新增扩展功能 public void takePhoto(){ System.out.println(外接摄像头组件进行拍照); } }开发优先级组合 接口实现 继承。进阶例子装饰器模式组合模式的经典变种开闭典范// 装饰器就是不停包装旧对象叠加功能原类永远不动 // 装饰器依靠组合实现无限扩展完全遵守开闭原则。 public abstract class PhoneDecorator extends Phone{ protected Phone phone; public PhoneDecorator(Phone phone){ this.phone phone; } Override public void call(){ phone.call(); } } // 拍照装饰器 class PhotoDecorator extends PhoneDecorator{ public PhotoDecorator(Phone phone) { super(phone); } public void takePhoto(){ System.out.println(装饰‑拍照); } } //蓝牙装饰器 class BlueDecorator extends PhoneDecorator{ public BlueDecorator(Phone phone) { super(phone); } public void blueTrans(){ System.out.println(装饰‑蓝牙); } }2.对修改闭合一个对外已经稳定投入使用的类不要改动它已定义好的对外公开接口、原有代码逻辑。 对外契约一旦定型源码不再修改需求变更靠扩展而不是改动旧类。即用抽象定义结构用具体实现扩展细节。模拟场景计算器现在只支持 加法、减法// 违背原则方案 public class Calculator { // 运算类型: add subtract public double calculate(double a, double b, String opType) { if (add.equals(opType)) { return a b; } else if (subtract.equals(opType)) { return a - b; } // 后面新增乘法、除法必须在这里新增if‑else // 修改原有稳定代码 → 违反开闭原则 else if (multiply.equals(opType)) { return a * b; } throw new RuntimeException(不支持该运算); } } // 开闭原则改善代码 // 1.定义接口运算规范对扩展预留入口 public interface Operation { double compute(double a, double b); } // 2.每种算法新建独立实现类新增功能只新建类 // 加法 public class AddOperation implements Operation { Override public double compute(double a, double b) { return a b; } } // 减法 public class SubtractOperation implements Operation { Override public double compute(double a, double b) { return a - b; } }三、里氏替换原则(Liskov Substitution Principle, LSP)定义任何父类出现的地方子类一定可以替换父类程序行为不会出错。1. 子类方法的参数类型必须与其超类的参数类型相匹配或更加抽象。即子类参数范围 父类参数同等或者更加抽象。2. 子类方法的返回值类型必须与超类方法的返回值类型或是其子类别相匹配。即子类返回值范围 ≤ 父类返回类型等于父类或者它的子类。3. 子类不能抛出父类没有的异常、不能弱化父类前置条件、不能加强后置条件。4. 超类的不变量必须保留。不变量 (invariant)超类对象自始至终需要维持、永远成立的状态约束、业务规则。只要父类实例执行完公开方法之后该条件成立任何子类执行完该方法之后也必须维持这条约束不可以将它破坏。简单举例常见不变量人的年龄 ≥ 0长方形 setWidth () 只修改宽度、不会改动高度集合 size ≥ 0圆半径必须 0本质子类重写父类 public 方法之后不能够改变对象固有的状态约定。 一旦不变量被摧毁父类引用接收子类对象时业务代码就会出错违背里氏替换原则。违背 LSP 的常见坏习惯1. 子类重写父类普通业务方法修改原有逻辑2. 父类方法不会抛异常子类重写后抛出异常3. 父类参数允许 0~100子类限制成 10~50加强前置条件4. 父类返回非空子类有可能返回 null。合格继承遵守规则父类能用的地方子类就能上子类不能篡改父类原有行为1. 父类所有 public 普通方法子类不要覆写2. 子类只新增自己独有的方法3. 如果需要改写行为 → 使用接口、组合代替继承。模拟场景长方形 Rectangle正方形 Square 继承长方形// 违背原则方案 // 父类长方形 // Rectangle 的不变量setWidth() 仅修改 widthsetHeight() 仅修改 height宽高互相独立。 public class Rectangle { protected int width; protected int height; public void setWidth(int width) { this.width width; } public void setHeight(int height) { this.height height; } public int getWidth() { return width; } public int getHeight() { return height; } } // 子类覆写方法打破不变量 /** * 子类正方形继承长方形 —— 违反里氏替换原则 * 正方形宽高必须相等重写set方法强制两边赋值 */ class Square extends Rectangle { Override public void setWidth(int width) { super.setWidth(width); super.setHeight(width); } Override public void setHeight(int height) { super.setHeight(height); super.setWidth(height); } } // 改进方案取消不合理继承抽取顶层抽象接口 // 顶层抽象接口图形 public interface Shape { int getWidth(); int getHeight(); } // 长方形独立实现接口 class Rectangle implements Shape { private int width; private int height; public void setWidth(int width) { this.width width; } public void setHeight(int height) { this.height height; } Override public int getWidth() { return width; } Override public int getHeight() { return height; } } // 正方形独立实现接口不再继承长方形 class Square implements Shape { private int side; public void setSide(int side) { this.side side; } Override public int getWidth() { return side; } Override public int getHeight() { return side; } }// 违背原则方案 // 父类长方形 public class Rectangle { protected int width; protected int height; public void setWidth(int width) { this.width width; } public void setHeight(int height) { this.height height; } public int getWidth() { return width; } public int getHeight() { return height; } } /** * 子类正方形继承长方形 —— 违反里氏替换原则 * 正方形宽高必须相等重写set方法强制两边赋值 */ class Square extends Rectangle { Override public void setWidth(int width) { super.setWidth(width); super.setHeight(width); } Override public void setHeight(int height) { super.setHeight(height); super.setWidth(height); } } // 改进方案取消不合理继承抽取顶层抽象接口 // 顶层抽象接口图形 public interface Shape { int getWidth(); int getHeight(); } // 长方形独立实现接口 class Rectangle implements Shape { private int width; private int height; public void setWidth(int width) { this.width width; } public void setHeight(int height) { this.height height; } Override public int getWidth() { return width; } Override public int getHeight() { return height; } } // 正方形独立实现接口不再继承长方形 class Square implements Shape { private int side; public void setSide(int side) { this.side side; } Override public int getWidth() { return side; } Override public int getHeight() { return side; } }客户端依靠父类不变量执行业务public class Client { //业务逻辑前提setWidth 不会修改 height public static void expand(Rectangle rect){ int oldHeight rect.getHeight(); rect.setWidth(50); if(rect.getHeight() ! oldHeight){ throw new RuntimeException(不变量遭到破坏); } } public static void main(String[] args) { Rectangle normalRect new Rectangle(); normalRect.setHeight(10); expand(normalRect); //正常执行 Rectangle square new Square(); square.setHeight(10); expand(square); //抛出异常 } }四、接口隔离原则(Interface Segregation Principle, ISP)客户端不应被迫依赖于其不使用的方法。尽量缩小接口的范围使得客户端的类不必实现其不需要的行为。当一个接口太大时我们需要将它分割成一些更细小的接口使用该接口的客户端仅需知道与之相关的方法即可。臃肿接口带来的缺点接口改动任意一个方法所有实现类都要跟着改动实现类被迫编写大量空的无用方法代码耦合严重、粒度太粗可读性差。模拟场景工作机器接口包含所有工种功能/** * 臃肿的巨型接口所有工作方法全部塞在一起 * 违背ISP设备只用部分功能却被迫实现全部抽象方法 */ public interface Worker { // 程序员 void code(); // 清洁工 void clean(); // 厨师 void cookFood(); } // 程序员只会写代码但是被逼实现剩下两个无关方法 public class Programmer implements Worker { Override public void code() { System.out.println(程序员正在敲代码); } Override public void clean() { // 无用空实现被迫实现不需要的接口方法 } Override public void cookFood() { // 无用空实现 } } // 清洁工只需要打扫卫生 public class Cleaner implements Worker{ Override public void code() {} Override public void clean() { System.out.println(清洁工打扫卫生); } Override public void cookFood() {} } // 优化改造遵守接口隔离原则拆分细粒度接口 // 把大接口拆分成多个职责单一、细小的功能接口 // 接口1会编码 public interface CodeAble { void code(); } // 接口2能够打扫 public interface CleanAble { void clean(); } // 接口3可以做饭 public interface CookAble { void cookFood(); } // 现在实现类只去实现自己需要的接口无关接口完全不用搭理 // 程序员只需要编码接口 public class Programmer implements CodeAble { Override public void code() { System.out.println(程序员正在敲代码); } } // 清洁工只实现打扫接口 public class Cleaner implements CleanAble{ Override public void clean() { System.out.println(清洁工打扫卫生); } } // 全能后勤人员可以同时实现多个细小接口 public class AllRoundWorker implements CodeAble,CleanAble,CookAble{ Override public void code() {} Override public void clean() {} Override public void cookFood() {} }五、依赖倒置原则(Dependency Inversion Principle, DIP)核心本质高层模块不应该依赖低层模块两者都应该依赖于抽象。抽象不应该依赖于细节细节应该依赖于抽象。依赖的三种方法// 准备好被依赖的服务类 // 被依赖对象消息发送服务 public class MessageService { public void sendMsg(String content){ System.out.println(发送消息 content); } }1构造函数传递依赖对象构造器注入通过构造方法参数传入依赖对象一旦创建依赖就不可更改。public class UserController { private final MessageService messageService; // 构造方法注入依赖 public UserController(MessageService messageService) { this.messageService messageService; } public void login(){ messageService.sendMsg(用户登录成功); } }优点依赖强制不为null 属性可以用final对象创建完毕依赖已经就绪不会出现空指针适合必须拥有、不可更换的依赖。缺点如果依赖很多构造函数参数列表很长运行期间不方便更换依赖对象。2Setter方法传递依赖对象setter 注入提供 setXxx () 方法实例创建之后再注入依赖支持动态更换。public class UserController { private MessageService messageService; // setter注入 public void setMessageService(MessageService messageService) { this.messageService messageService; } public void login(){ messageService.sendMsg(用户登录成功); } }优点适合可选依赖程序运行阶段动态切换实现类多个依赖时构造器不会臃肿。缺点创建完对象之后依赖才赋值有可能忘记 set出现空指针不能使用final修饰成员变量。Spring 当中必填依赖推荐构造注入可选依赖推荐 Setter 注入3接口声明依赖接口注入定义注入专用接口容器调用接口方法注入依赖。通俗规定一个注入方法所有需要该依赖的类实现这个接口。// 注入接口用来注入MessageService public interface MessageAware { void setMessageService(MessageService messageService); } public class UserController implements MessageAware { private MessageService messageService; Override public void setMessageService(MessageService messageService) { this.messageService messageService; } public void login(){ messageService.sendMsg(用户登录成功); } }优点容器统一识别、批量注入Spring‑Aware 系列BeanNameAware、ApplicationContextAware就是这种方式容器只需要判断instanceof Aware接口即可批量完成依赖注入缺点侵入性最强业务代码必须实现专属注入接口耦合容器 API每一种依赖都需要新增一个接口容易接口泛滥。开发建议普通业务 Bean 优先构造注入 Setter 注入只有需要拿到 Spring 容器本身对象的时候才使用 ApplicationContextAware 这类接口注入。依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立不互相影响实现模块间的松耦合、扩展方便、改动一处不会牵连其它业务。我们在项目中使用这个原则要遵循下面的规则1每个类尽量都有接口或者抽象类或者抽象类和接口两都具备// 错误写法高层直接依赖具体实现 // 低层具体实现没有抽取抽象 public class MysqlDao { public void save(){ System.out.println(MySQL 保存数据); } } // 高层业务类直接依赖具体类 MysqlDao public class BusinessService{ private MysqlDao dao new MysqlDao(); public void doWork(){ dao.save(); } } // 弊端以后要切换 Oracle就要修改 BusinessService 源码违反开闭原则。 // 遵守规则抽出抽象接口 // 抽象层 public interface BaseDao{ void save(); } // 具体实现依赖抽象 public class MysqlDao implements BaseDao{ Override public void save() { System.out.println(MySQL 保存数据); } } public class OracleDao implements BaseDao{ Override public void save() { System.out.println(Oracle 保存数据); } } // 所有可扩展的业务实现面向抽象高层接下来只对接接口。2变量的表面类型尽量是接口或者抽象类引用类型声明为抽象而不是具体实现类实际对象可以随时更换子类。// 反面 // 表面类型是具体类 private MysqlDao dao; // 正确写法 public class BusinessService{ // 引用类型为抽象接口 private BaseDao dao; // 构造器注入抽象依赖 public BusinessService(BaseDao dao){ this.dao dao; } public void doWork(){ dao.save(); } } // 客户端切换底层数据库不需要修改 BusinessService public class Client { public static void main(String[] args) { // 使用MySQL BusinessService service1 new BusinessService(new MysqlDao()); service1.doWork(); // 直接更换实现高层代码丝毫不动 BusinessService service2 new BusinessService(new OracleDao()); service2.doWork(); } }3任何类都不应该从具体类派生不要继承一个已经具备完整业务的具体类应当继承抽象类、实现接口。// 反面示例继承具体类紧耦合细节 // 具体实现类 public class MysqlDao{ public void save(){ System.out.println(mysql保存); } } // 从具体类派生严重耦合 public class NewMysqlDao extends MysqlDao{ Override public void save() { System.out.println(新版mysql保存); } } // 缺点父类是细节实现父类一旦改动字段、私有逻辑子类全部受牵连。 // 正确继承抽象 public abstract class AbstractDao{ public abstract void save(); } public class MysqlDao extends AbstractDao{ Override public void save() {} } // 抽象层稳定很少改动具体实现可以随便新增、修改不会影响上层。4尽量不要覆写基类的方法如果抽象类已经提供了实现子类尽量不要重写。类间依赖的是抽象覆写了抽象方法对依赖的稳定性会有一定的影响。依赖倒置依靠抽象维持稳定一旦子类大规模覆写父类方法抽象约定就失效上层依赖抽象却得到不一样的行为。// 反面案例覆写父类已实现方法破坏抽象约定 abstract class AbstractDao{ // 抽象类已经实现公共日志逻辑 public void save(){ log(); } public void log(){ System.out.println(执行数据库操作日志); } } public class MysqlDao extends AbstractDao{ // 覆写父类已经实现好的log改变约定行为 Override public void log() { System.out.println(诡异的自定义日志); } } // 上层模块依赖 AbstractDao默认相信 log () 的固定行为子类覆写之后行为不可控。 // 正确做法 // 模板方法需要扩展新增钩子抽象方法而不是覆写已有实现 // 固定通用逻辑放在父类变化点定义成抽象方法。 abstract class AbstractDao{ public void save(){ log(); doSave(); } public void log(){ System.out.println(执行数据库操作日志); } // 变化点定义抽象子类只实现这个抽象 protected abstract void doSave(); } class MysqlDao extends AbstractDao{ Override protected void doSave() { System.out.println(mysql保存); } }5结合里氏替换原则使用二者搭档关系依赖倒置原则高层面向抽象隔绝细节里氏替换原则所有子类遵守父类约定可以安全替换抽象。原理依赖倒置让高层依赖抽象里氏替换保证所有抽象的子类可以安全替换父类 / 接口不会改变预期行为、不会破坏超类不变量。// 如果违背里氏替换子类重写方法篡改行为 public class BadDao implements BaseDao{ Override public void save() { // 本该持久化现在空实现 } } public class Client { public static void main(String[] args) { // 高层依赖 BaseDao 抽象但是子类行为异常业务直接出错。 BusinessService service new BusinessService(new BadDao()); service.doWork(); } }六、迪米特法则Law of Demeter, LoD一个对象应该对其他对象有最少的了解。迪米特原则的核心观念就是类间解耦弱耦合只有弱耦合后类的复用率才可以提高。模拟场景模拟学生、老师、校长之间的关系老师需要负责具体某一个学生的学习情况而校长关心老师所在班级的总体成绩不会过问具体一个学生的学习情况。// 违背原则方案 // 定义学生类 Data AllArgsConstructor public class Student { private String name; // 学生姓名 private int rank; // 考试排名总排名 private double grade; // 总分 } // 定义老师类 public class Teacher { private String name; // 老师姓名 private String clazz; // 班级 private static ListStudent studentList; public Teacher(String name, String clazz) { this.name name; this.clazz clazz; } static { studentList new ArrayListStudent(); studentList.add(new Student(小明, 10, 567)); studentList.add(new Student(小花, 54, 356)); studentList.add(new Student(小丁, 23, 439)); studentList.add(new Student(小丽, 2, 665)); studentList.add(new Student(小红, 19, 502)); } public String getName() { return name; } public String getClazz() { return clazz; } public ListStudent getStudentList() { return studentList; } } // 校长类获取学生人数、总分、平均分 public class Principal { private Teacher teacher new Teacher(张老师, 3年级1班); // 查询班级信息学生人数、总分、平均分 public MapString, Object queryClazzInfo() { int stuCount clazzStudentCount(); double totalScore clazzTotalScore(); double averageScore clazzAverageScore(); MapString, Object mapObj new HashMap(); mapObj.put(班级, teacher.getClazz()); mapObj.put(老师姓名, teacher.getName()); mapObj.put(学生人数, stuCount); mapObj.put(总分, totalScore); mapObj.put(平均分, averageScore); return mapObj; } private double clazzAverageScore() { double totalScore 0; for (Student stu : teacher.getStudentList()) { totalScore stu.getGrade(); } return totalScore / teacher.getStudentList().size(); } private int clazzStudentCount() { return teacher.getStudentList().size(); } private double clazzTotalScore() { double totalScore 0; for (Student stu : teacher.getStudentList()) { totalScore stu.getGrade(); } return totalScore; } } // 改善代码不该让校长直接管理学生校长应该管理老师。由老师提供相应的学生信息查询服务 // 定义老师类 public class Teacher { private String name; // 老师姓名 private String clazz; // 班级 private static ListStudent studentList; public Teacher(String name, String clazz) { this.name name; this.clazz clazz; } static { studentList new ArrayListStudent(); studentList.add(new Student(小明, 10, 567)); studentList.add(new Student(小花, 54, 356)); studentList.add(new Student(小丁, 23, 439)); studentList.add(new Student(小丽, 2, 665)); studentList.add(new Student(小红, 19, 502)); } // 查询平均分 public double clazzAverageScore() { double totalScore 0; for (Student stu : studentList) { totalScore stu.getGrade(); } return totalScore / studentList.size(); } // 查询班学生人数 public int clazzStudentCount() { return studentList.size(); } // 查询总分 public double clazzTotalScore() { double totalScore 0; for (Student stu : studentList) { totalScore stu.getGrade(); } return totalScore; } public String getName() { return name; } public String getClazz() { return clazz; } } // 校长类获取学生人数、总分、平均分 public class Principal { private Teacher teacher new Teacher(张老师, 3年级1班); // 查询班级信息学生人数、总分、平均分 public MapString, Object queryClazzInfo() { MapString, Object mapObj new HashMap(); mapObj.put(班级, teacher.getClazz()); mapObj.put(老师姓名, teacher.getName()); mapObj.put(学生人数, teacher.clazzStudentCount()); mapObj.put(总分, teacher.clazzTotalScore()); mapObj.put(平均分, teacher.clazzAverageScore()); return mapObj; } }七、总结1、单一职责原则 SRP保证类职责干净后续扩展的时候不容易牵连多处代码核心高内聚职责拆分降低修改带来的风险。口诀一类一事一事一类。2、开闭原则 OCP最终目标依靠抽象实现扩展核心面向抽象新增代替改动规避回归 Bug。口诀加功能加新类不改老代码。3、里氏替换原则 LSP用来保证子类扩展之后可以安全替换父类扩展不会引发异常前置‑入参子类可接收参数范围 ≥ 父类不能收紧入参条件Java 重写参数类型必须一致后置‑返回值返回类型等于父类或是父类的子类协变返回返回更加具体对象不变量执行公开方法之后父类所有对象状态约束必须保留、不可破坏异常约束子类抛出受检异常只能是父类异常的子类禁止抛出陌生受检异常。核心继承是契约子类不能篡改父类行为。口诀子类可替换父类不破坏原有约定。4、接口隔离原则 ISP保证接口职责干净对外接口粒度细小对外契约稳定核心接口粒度细小按需依赖拒绝多余方法。口诀胖接口拆瘦接口用什么接口就实现什么。5、依赖倒置原则 DIP面向抽象搭建稳定框架高层依赖抽象接口新增具体实现即可扩展核心面向抽象编程解除高层和底层实现的耦合。口诀高层依赖抽象不依赖具体。6、迪米特法则 LoD约束对象之间通信控制耦合程度核心降低类之间耦合减少类之间的关联修改扩展的时候牵连范围很小。口诀只和熟人说话不和陌生人打交道通过中间方法封装调用。