设计模式预计阅读 10 分钟3601 字2800 次浏览

设计模式修炼之路 ㉞ 六大原则再回首——殊途同归的总账

全系列收官篇。没有新模式,没有代码——只有一本记了三十二个模式的账。

目录

全系列收官篇。没有新模式,没有代码——只有一本记了三十二个模式的账。

㉝ 收官时说:三十二个模式各治一种痛,可每次诊断病灶、开出药方,反复露面的都是几张熟面孔。① 初识那篇速览过六大原则——当时它们只是六行背了就忘的定义;三十二篇走完再回头看,每一行背后都是一路踩过的坑。

本篇把账本摊开:六条原则,一条条对——它们各自在哪些篇里救过场、打过折、或者连名字都没报过;最后算总账:为什么说它们殊途同归。

一、先还一笔账:原则和模式是什么关系

三样东西要分清:

  • 原则是"为什么"——单一职责、开闭、里氏替换、依赖倒置、接口隔离、迪米特,六条判词,说的是代码"该长什么样",但不给施工图
  • 模式是"怎么办"——三十二个模式,每个都是针对一类问题的标准施工法,图纸齐全、验收明确
  • 反面教材是"病历"——本系列每篇先演事故再开药方,那些事故拆开看,全是对某条原则的违反

关系一句话:原则是法律,模式是判例,反面教材是案发现场。 顺带一笔:GoF 那本红皮书正文开篇其实只立了两条祖训——"面向接口编程,而不是面向实现"和"组合优于继承";六大原则可以看作这两条加上开闭原则的展开版。下面逐条对账。

二、开闭原则——全场 MVP

定义一句话:对扩展开放,对修改关闭——新需求来了,加新代码,别改老代码。

它是全系列出场率最高的原则,几乎每篇的"疗效"栏都有它:

  • ⑮ 策略:"if-else 消失,开闭原则达成。新活动上线 = 加一个类 + 注册一行,老方法从此不动"
  • ⑯ 观察者:"开闭原则的达成方式和 ⑮ 一模一样:新变化 = 新类,不是改旧类"
  • ⑩ 装饰器:"新增一种配料 = 新写一个装饰器类,SimpleCoffee、Milk、Mocha 全都不用动"
  • ⑨ 组合:"实现 FileComponent 就能进树,全树所有遍历、统计、打印代码一行不改"
  • ⑥ 适配器:"两头都不改。Target 不动、Adaptee 不动,中间加一层就能协同工作"
  • ⑧ 过滤器:"加'品牌'筛选 = 新建一个 BrandCriteria 完事"
  • ⑦ 桥接:"加三角形只动形状树,加紫色只动颜色树,谁也不碰谁"
  • ⑭ 模板:加变体就是加子类,extends 一个类填几个空就完事

反面病历同样声势浩大:⑦ 的继承爆炸,是"一处新增,处处开工,开闭原则碎了一地";㉒ 中介者的网状依赖,是"赵六进群那一刻,张三、李四、王五的代码全都得改——开闭原则阵亡"。

还有两笔特别值得学的账。一笔是 ③ 工厂三兄弟的演进——本身就是开闭原则的得失史:简单工厂每加一个产品要改一处 switch,工厂方法把"加产品"变成"加子类",开闭到手。另一笔是 ⑲ 状态模式"开闭原则的成色"那节的诚实账:"状态模式对开闭原则的兑现是'打折扣'的"——新状态是不改 if-else 矩阵了,但流转图上有边指向它的旧状态类,都得动。开闭不是非黑即白的达标线,是每张设计图上的成色刻度。

三、单一职责——每个模式的入场券

定义一句话:一个类只有一个变化的理由。

它低调得多,但从没缺席。模范生名单:⑩ 装饰器"每个装饰器只干一件事,Milk 只管加牛奶——单一职责原则的模范生";⑳ 迭代器"聚合管存储,迭代器管遍历,单一职责各安其位";㉑ 备忘录"角色管打包还原,管理器管保管调度,各自单一职责"。

真正的重头戏在 J2EE 八式——整卷都是单一职责的连续剧,每篇都在搬一次家:㉙ 把"怎么存"从业务对象家里搬走("业务对象不该懂数据库");㉚ 把横切关注点从每个控制器家里搬进门神;㉜ 把"怎么找服务"从每家业务代码搬进定位器——"技术细节全进了定位器,业务方法里只剩业务";㉝ 把"运数据"从实体身上拆出来,数据走 DTO,行为留在服务器。J2EE 模式几乎没发明新东西,它们做的事本质都一样:把混在一起的两件事拆开,各自安家。

反面病历:⑨ 组合模式那张代价表里,单一职责和里氏替换一起躺枪——透明式让叶子被迫背着 add(child),"错误拖到运行时才炸"。

四、里氏替换与依赖倒置——继承与依赖的两条纪律

这两条管的都是"关系":一个管继承关系,一个管依赖关系。

里氏替换:子类必须能无痛顶替父类。 全系列它正面对账只有一次——⑨ 组合模式的透明式 vs 安全式:透明式为了"叶子容器一视同仁",让叶子也实现 add(child),客户端拿着 leaf.add() 编译期不报错、运行时才炸——"违背单一职责与里氏替换,错误拖到运行时才炸"。为什么它出场这么少?因为系列一路的答案都是 GoF 第二祖训"组合优于继承":⑩ 装饰器"我是组件 + 我有组件",⑦ 桥接把继承爆炸拆成两棵独立的树,⑮⑲ 策略和状态用委托不用继承。继承用得越少,里氏的考场越少——它像安全带,系好了就没人在意它。

依赖倒置:依赖抽象,不依赖具体。 它是"面向接口编程"的祖师爷,系列里到处是它的标本:② 单例篇就埋过"依赖倒置"的伏笔,⑥ 适配器应验了一次——"Phone.charging(Voltage5V) 的参数是接口——手机依赖的是抽象而不是具体电源";⑯ 观察者的广播循环,"observer.onOrderPaid(orderId) 这行代码就是'依赖倒置'的最小标本"。往大处看:⑭ 模板的好莱坞原则长成了控制反转,是 Spring IoC 的源头之一;J2EE 八式整卷都在依赖倒置——㉗ 让表示层依赖业务话术接口,㉙ 让业务依赖 DAO 接口,㉜ 让客户端只认服务的名字。每一处"参数是接口、字段是接口",都是它的一次无声执法。

五、接口隔离与迪米特——两位没报名字的常客

接口隔离:别让调用方依赖它用不着的方法。 全系列它一次名都没报过,却一直在场:⑧ 过滤器的 Criteria,一个接口只装一个条件;㉛ 拦截过滤器的 Filter 契约两句话——放行就 next,小到能背下来;㉙ DAO 接口只说业务语言,半个 SQL 的影子都没有;JDK 的 Comparable、Runnable、Callable,清一色单方法小接口。它的哲学:接口是给调用方的承诺,承诺越少,承诺越硬。

迪米特法则(最少知道原则):只跟直接朋友说话。 同样全程匿名,同样无处不在:⑪ 外观是它的专职模式——客户端只认识 SLF4J 一个门面,门后十几个日志框架互不认识;㉒ 中介者把网状依赖收成星型,同事们彼此不认识,只认识中介;㉘ 组合实体"依赖对象没有远程身份",远程方只见主对象;㉝ 客户端只认识 DTO,实体长什么样它根本不知道。反面病历:㉝ 的细粒度接口,UI 直接摸到字段级——知道的越多,对方一改你越疼。知道的越少,对方内部翻天,你越稳。

六、殊途同归——一张总账表

原则 一句话 全场代表作 反面病历
开闭 对扩展开放,对修改关闭 ⑮ 策略:新变化 = 新类,不是改旧类 ⑦ "一处新增,处处开工";㉒ 赵六进群全员改
单一职责 一个类只有一个变化的理由 ⑩ 装饰器:每个装饰器只干一件事 ㉙ 之前:业务对象懂数据库
里氏替换 子类必须无痛顶替父类 ⑮ 策略族:任何实现都能顶上,调用方无感 ⑨ 透明式:错误拖到运行时才炸
依赖倒置 依赖抽象,不依赖具体 ⑯ 观察者:广播一行,只认接口 每一处把 new 具体类写死的地方
接口隔离 不依赖用不着的方法 ㉛ Filter 契约两句话 胖接口:塞给调用方一坨用不着的
迪米特 只跟直接朋友说话 ⑪ 外观:全站只认识一个门 ㉝ 反面:UI 摸到字段级

为什么说殊途同归?把六句话翻译成同一个问题——变化来的时候,波及面有多大? 开闭说:改动别扩散到老代码;单一职责说:一个变化只落在一个类上;里氏替换说:换实现别炸调用方;依赖倒置说:上层别认识下层的细节;接口隔离说:别把用不着的也捆进来;迪米特说:你只需要认识直接朋友。六条原则,六种说法,一个意思:把变化关进小笼子。

模式就是造笼子的标准工艺。每本讲模式的书都念同一句心法——找到变化,封装变化——三十二个模式,拆开看每个都在回答"这类变化用什么笼子装":算法会变,⑮ 装;步骤会变,⑭ 装;状态会变,⑲ 装;结构会变,⑨ 装;职责会变,⑩ 装;连"对象从哪儿来"会变,③⑤ 都备好了笼子。原则划定笼子的验收标准,模式提供笼子的标准图纸——这就是全部关系。

写在最后

  • 原则是为什么,模式是怎么办,反面教材是案发现场——三十四篇,账都记在同一本上
  • 开闭是目标,其余五条是路径;⑲ 教的那课要记住:开闭是成色刻度,不是达标线
  • 单一职责是每个模式的入场券,J2EE 八式整卷都是职责搬家
  • 继承用得越少,里氏替换越安全——GoF 第二祖训"组合优于继承"一路都在执法
  • 接口隔离和迪米特全程匿名却无处不在——好原则像空气,用时方知在场
  • 殊途同归:六条原则一个问题——变化来了,波及面多大?把变化关进小笼子

三十四篇至此收官。从 ② 那把双重校验锁开始,到 ㉝ 那份拎起来就能走的快照——模式会过时:㉜ 输给了依赖注入,蓝皮书的远程八式也被 RPC 框架收编成了注解。原则不会。下次面对一个两百行的类、一串嵌套的 if-else、一张谁也不敢动的表,不必急着想起某个模式的名字;先问一句:这里什么在变?变化关进笼子了吗?——答案出来的地方,模式自然会来赴约。修炼之路,到此功成;剩下的,在代码里见。

花

花开空白

开发者 · 西安

相关文章

评论(0)

还没有评论,来抢沙发吧

发表评论