六大原则

通用规则:高内聚、低耦合

01 单一职责

  • 定义:一个类应该只有一个发生变化的原因
  • 示例场景:视频网站模拟
    image-20220315224207161.png
    • 自变量:分辨率、广告时长
    • 因变量:用户等级
    • 理解:
      1. 视频分辨率、广告时长随且仅随用户等级的变化而变化,因此可以将视频分辨率和广告时长凝聚为一个用户服务类;
      2. 自变量(用户等级)的唯一性体现了单一,用户等级的职责是控制用户服务类;
      3. 因变量之间相互独立,他们能凝聚为一个类的原因是它们都与自变量有关系,且仅受自变量的唯一控制。
  • 延伸思考
    • 什么时候会打破单一职责?
      1. 自变量多于一个时,打破单一性。比如广告时长不仅受用户等级控制,还受金主钞能力控制(?不确定
      2. 自变量的改变对因变量无影响,即自变量对因变量的控制失效了。比如无论用户等级多少,都只提供1080P的视频分辨率。
    • 职责如何细化?
      比如A控制B,B控制(CDE),应该找到直接控制CDE的B让它对CDE负责,而不是找B的上司A去间接控制CDE

02 开闭原则

  • 定义:扩展开放,修改封闭
  • 总结:高级架构师建立了基础模块,其他团队只能继承基础模块,在自己的地盘堆屎山
  • 示例场景:圆面积计算
    • 场景S:标准模块里的π定义为3.14,但是不同团队对精度有着不同的需求
    • 任务T:在满足自己精度需求的同时,不影响其他团队对标准模块的使用
    • 行动A:通过继承方式,写一个高精度的π去重写标准模块原来的π
    • 结果R:(无?)
  • 如何选择扩展还是修改?
    对于公共需求,可以收集升级基础模块;对于非共性需求,自己继承折腾去吧

03 里式替换

  • 定义:继承必须确保超类所拥有的性质在子类中依然成立
  • 里式替换强调了对功能的增强(不破坏父类),开闭原则强调了不同需求下对原有功能的修改/替换
  • 示例场景:卡的设计
    • 场景S:已有的基类—卡
    • 任务T:通过继承基类,扩展设计储蓄卡类和信用卡类
    • 行动A:通过继承的方式,在新的类里补充功能
      image-20220315223651143.png
    • 结果R:(无?)

04 迪米特法则

  • 定义:最少知道,减少依赖
  • 总结:上级放权,让手下的人干活,自己只需要知道结果
  • 示例场景:学生成绩&排名
    • 场景S:学校由三种成员组成:学生、老师、校长
    • 任务T:校长想要获得每个班级的信息(老师、总分、平均分、人数)
    • 行动A:
      • Before:校长需要自己动手查询班级信息
      • After:校长放权给老师,让老师查询自己班的信息,再向上汇报
        校长不需要知道怎么信息是如何来的,只需要知道最后的结果,即最少知道

image-20220317103149817.png

  • 结果R:(无?)

05 接口隔离

  • 定义:更小的接口、更具体的接口
  • 总结:当接口里定义了很多方法,但应用时只需要实现其中的一部分,因此要求程序员尽量将臃肿庞大的接口拆分为更小更具体的接口
  • 示例场景:游戏技能设计
    • 场景S:给游戏里的英雄设计技能,所有技能包括射箭、隐藏、沉默、眩晕四种
    • 任务T:实现后裔(射箭、隐藏、沉默)和廉颇(隐藏、沉默、眩晕)
    • 行动A:拆解英雄技能池这个大接口,使得每个技能对应唯一接口,英雄即可通过实现多接口的方式完成设计
      image-20220317111228634.png
    • 结果R:(无?)

06 依赖倒置

  • 定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象
  • 上级定义标准,下级设计细节
  • 示例场景:抽奖策略设计
    • Before:用哪种策略就要掉哪种方法,写死的
      DrawControl依赖于抽奖策略的具体实现(低层模块),当需要添加新的策略时,DrawControl也需要修改
    • After:使用了统一的接口,通过传参调整具体使用的抽奖策略
      DrawControl依赖于抽奖策略的接口Idraw(抽象)

image-20220317171743007.png