六大原则
通用规则:高内聚、低耦合
01 单一职责
- 定义:一个类应该只有一个发生变化的原因
- 示例场景:视频网站模拟
- 自变量:分辨率、广告时长
- 因变量:用户等级
- 理解:
- 视频分辨率、广告时长随且仅随用户等级的变化而变化,因此可以将视频分辨率和广告时长凝聚为一个用户服务类;
- 自变量(用户等级)的唯一性体现了单一,用户等级的职责是控制用户服务类;
- 因变量之间相互独立,他们能凝聚为一个类的原因是它们都与自变量有关系,且仅受自变量的唯一控制。
- 延伸思考
- 什么时候会打破单一职责?
- 自变量多于一个时,打破单一性。比如广告时长不仅受用户等级控制,还受金主钞能力控制(?不确定
- 自变量的改变对因变量无影响,即自变量对因变量的控制失效了。比如无论用户等级多少,都只提供1080P的视频分辨率。
- 职责如何细化?
比如A控制B,B控制(CDE),应该找到直接控制CDE的B让它对CDE负责,而不是找B的上司A去间接控制CDE
- 什么时候会打破单一职责?
02 开闭原则
- 定义:扩展开放,修改封闭
- 总结:高级架构师建立了基础模块,其他团队只能继承基础模块,在自己的地盘堆屎山
- 示例场景:圆面积计算
- 场景S:标准模块里的
π
定义为3.14,但是不同团队对精度有着不同的需求 - 任务T:在满足自己精度需求的同时,不影响其他团队对标准模块的使用
- 行动A:通过继承方式,写一个高精度的
π
去重写标准模块原来的π
- 结果R:(无?)
- 场景S:标准模块里的
- 如何选择扩展还是修改?
对于公共需求,可以收集升级基础模块;对于非共性需求,自己继承折腾去吧
03 里式替换
- 定义:继承必须确保超类所拥有的性质在子类中依然成立
- 里式替换强调了对功能的增强(不破坏父类),开闭原则强调了不同需求下对原有功能的修改/替换
- 示例场景:卡的设计
- 场景S:已有的基类—卡
- 任务T:通过继承基类,扩展设计储蓄卡类和信用卡类
- 行动A:通过继承的方式,在新的类里补充功能
- 结果R:(无?)
04 迪米特法则
- 定义:最少知道,减少依赖
- 总结:上级放权,让手下的人干活,自己只需要知道结果
- 示例场景:学生成绩&排名
- 场景S:学校由三种成员组成:学生、老师、校长
- 任务T:校长想要获得每个班级的信息(老师、总分、平均分、人数)
- 行动A:
- Before:校长需要自己动手查询班级信息
- After:校长放权给老师,让老师查询自己班的信息,再向上汇报
校长不需要知道怎么信息是如何来的,只需要知道最后的结果,即最少知道
- 结果R:(无?)
05 接口隔离
- 定义:更小的接口、更具体的接口
- 总结:当接口里定义了很多方法,但应用时只需要实现其中的一部分,因此要求程序员尽量将臃肿庞大的接口拆分为更小更具体的接口
- 示例场景:游戏技能设计
- 场景S:给游戏里的英雄设计技能,所有技能包括射箭、隐藏、沉默、眩晕四种
- 任务T:实现后裔(射箭、隐藏、沉默)和廉颇(隐藏、沉默、眩晕)
- 行动A:拆解英雄技能池这个大接口,使得每个技能对应唯一接口,英雄即可通过实现多接口的方式完成设计
- 结果R:(无?)
06 依赖倒置
- 定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 上级定义标准,下级设计细节
- 示例场景:抽奖策略设计
- Before:用哪种策略就要掉哪种方法,写死的
DrawControl
依赖于抽奖策略的具体实现(低层模块),当需要添加新的策略时,DrawControl
也需要修改 - After:使用了统一的接口,通过传参调整具体使用的抽奖策略
DrawControl
依赖于抽奖策略的接口Idraw
(抽象)
- Before:用哪种策略就要掉哪种方法,写死的