第10章 · 播客 第1集
设计模式 · 设计模式概述与创建型模式
🎙️ 本集播客:第1集:四人组与模式的四要素、GoF 三大分类各自解决什么问题,再到五个创建型模式——Factory Method、Abstract Factory、Builder、Prototype、Singleton 逐个讲透,含真题「抽象工厂解决什么问题」的原文原句与「构造函数设为私有」这类实现细节。 语音由微软 Edge 神经网络语音预生成(女声·晓伊,男声·云希)。点击下方任意对话可直接从该句开始播,当前句朗读时下一句已预先加载,无缝衔接。
雅
小雅
阿明,架构师的招牌章节来了:设计模式。这章考试不考你写代码,考的是你能不能说清「这个模式到底解决什么问题」。先来一道真题:描述一个设计模式时,至少需要包含四个方面,你说是哪四个?
明
阿明
模式名称、要解决的问题、解决方案……第四个,代码示例?
雅
小雅
第四个是效果。原文写得很明确:模式名称、问题、解决方案、效果,这四个方面就是设计模式的四要素。逐个过:名不正则言不顺,每种设计模式都有自己的名字,这是模式名称;设计模式都有其应用的场合,也就是它意图解决的问题,超出了这个问题就不应该再应用这种模式,这是问题;解决问题的方法描述,这是解决方案;每种模式除了提高架构质量,还有更具体的效果描述,这就是最后一个要素——效果。
明
阿明
所以选项里出现「代码」「测试」「实现」「评估」这些词,全是干扰项?
雅
小雅
全是。最阴险的选项是「模式名称、问题、方案、实现」,只把「效果」偷换成「实现」。原文还给了英文对应:Pattern name、Problem、Solution,效果是 Consequence。这个词本身就带「后果」的意思,提醒你效果不只是说好处,还有代价。
明
阿明
这套东西哪来的?就是传说中的四人组发明的?
雅
小雅
分两步。20 世纪 70 年代,Christopher Alexander 提出了城市建筑的模式,他认为:模式就是描述一个不断发生的问题和该问题的解决方案,这概念是从建筑学来的。后来 Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四人写了著名的参考书《设计模式:可复用面向对象软件的基础》,后人称他们为四人组,把书中模式称为 GoF 模式,Gang of Four。
明
阿明
所以设计模式就是他们四个创造的。
雅
小雅
错,原文专门纠正过:设计模式并非这四人所创,它来源于很多项目中的成功设计,四人组只是把这些优雅的设计方式进行抽象、总结、归纳出来。再背一个定义:GoF 将设计模式定义为「对被用来在特定场景下解决一般设计问题的类和互相通信的对象的描述」,通俗地说,就是对某一类问题的通用解决方案。
明
阿明
「一类问题」和「通用解决方案」,这两个词有什么讲究?
雅
小雅
讲究就在这。原文举例:工厂模式是为了解决类创建的问题,适配器模式是为了解决类接口不匹配的问题,把解决 A 问题的模式用在 B 问题上,张冠李戴。而「通用」是说它不绑定具体实现:GoF 书里给的是 C++ 实现,但同样可以用 Java 甚至非面向对象的语言实现,具体应用时可以根据实际情况变化,工厂模式就有很多种变化。
明
阿明
这章 23 个模式光名字就够背了,学习上有什么提醒?
雅
小雅
原文给了两条注意事项,第二条本身就是考点。第一条:学这些模式是一个方面,更要了解模式中的思想——设计模式描述的大多是面向对象的低层设计方案,但包含的是软件设计的思想,同软件架构风格是一致的,比如 MVC 既可以看作一种设计模式,也可以看作一种架构风格。第二条:滥用设计模式会适得其反,不相宜的场合乱用有害无益,过度使用不但不会提高复用性,反而会让架构变得混乱而难以维护;尤其在一些简单系统中不能滥用,而重构也是应用设计模式的好时机。
明
阿明
那 23 个模式怎么分类?总不能一锅粥吧。
雅
小雅
按设计模式要解决的问题,分成三类:创建型、结构型、行为型。原文的推导要会复述:面向对象的设计要解决三个问题——如何管理系统中的对象,如何组织系统中的类与对象,类与对象如何相互通信。三类模式分别对应:创建型解决对象创建的问题;结构型解决系统不断扩张、复用越来越多后类与对象结构变得复杂混乱的问题;行为型解决相互通信的问题。
明
阿明
考题问「解决对象创建问题的是哪一类」,这种送分吧?
雅
小雅
送分,但干扰项要认识:答案创建型设计模式;结构型、行为型各管各的,别张冠李戴;还有个干扰项 J2EE 设计模式——那是 GoF 之后别人按表现层、业务层、综合层另分的一套,不在 GoF 三分类里。原文还提了第二种分法:按模式主要应用于类还是对象来分类,两种分法综合起来得到教材那张分类表。
明
阿明
进入正题吧。创建型有哪几个?
雅
小雅
五个:Factory Method、Abstract Factory、Builder、Prototype、Singleton。先说 Factory Method,工厂方法模式,原文一句话:提供了一种延迟创建类的方法,使用这个方法可以在运行期由子类决定创建哪一个类的实例。关键词两个——延迟创建、子类决定。什么时候用:想把「创建哪个对象」的决定推迟到运行期,交给子类去做。
明
阿明
那 Abstract Factory 呢?名字只差一个词,我总分不清。
雅
小雅
对。原文一句话:抽象工厂模式主要为解决复杂系统中对象创建的问题,提供一致的对象创建接口,来创建一系列具有相似基类或相似接口的对象。注意「一系列」——它不是造一个对象,是造一族产品。结构角色用原文的比喻:Abstract Factory 类接受 Client 的订单,也就是客户端发来的消息;使用不同的车间——Concrete Factory,具体工厂;根据已有的产品模型——Abstract Product,抽象产品;生产出特定的产品 Product,车间与产品一一对应。
明
阿明
「抽象工厂解决了什么问题」这道真题,选项都长得挺像。
雅
小雅
真题就是考原文原句:抽象工厂模式可以解决——根据不同的配置或上下文环境加载具有相同接口的不同类实例。干扰项各个击破:「保证一个类仅有一个实例」是 Singleton;「动态地为对象方法增加功能」是 Decorator;「将请求封装为对象」是 Command。为什么能解决?直接 new 类名会造成类名硬编码,需要按不同运行环境动态加载相同接口但实现不同的类实例;抽象工厂用抽象类定义接口、不同环境实现子类,避免代码同运行环境强绑定,软件才移植得动。
明
阿明
它有什么必须记的结构细节吗?我看原文有个专家提示。
雅
小雅
两句:Create Product 方法与 Abstract Product 一一对应,就像一个工厂有多条产品线;Concrete Factory 的数量同实际的 Product 数量一致。效果记两个词:可配置、动态——实现对象可配置的、动态的创建,提高移植性,尤其运行于多个平台或有不同功能配置版本时。
明
阿明
Builder 和它「非常类似」,那区别在哪?
雅
小雅
原文原话:Builder 模式与 Abstract Factory 模式非常类似,但 Builder 模式是逐步地构造出一个复杂对象,并在最后返回对象的实例。它可以把复杂对象的创建与表示分离,使得同样的创建过程可以创建不同的表示。抓两个词:逐步地构造、最后返回。抽象工厂是一下子把一族产品都造出来,Builder 是一步一步攒一个复杂对象,攒完了才交货。
明
阿明
Prototype 呢?这个名字听着像「原型」,是拿现成对象去复制?
雅
小雅
对。原文:Prototype 模式可以根据原型实例制定创建的对象的种类,并通过深复制这个原型来创建新的对象,注意是深复制。它有同 Abstract Factory 和 Builder 相同的效果,什么时候选它?需要实例化的类在运行期才被指定,而且要避免创建一个与产品层次平行的工厂类层次。优势是运行时能增加或减少原型,比 Abstract Factory 和 Builder 更灵活。
明
阿明
五个里我最有把握的是 Singleton,就是全局只能有一个实例。
雅
小雅
意图对:使用 Singleton 模式可以保证一个类仅有一个实例,从而提供一个单一的全局访问点,别名单件模式或单根模式。为什么需要它?短消息服务、打印机服务,甚至系统配置环境的控制,为避免并发访问造成不一致,希望仅提供一个实例。用全局变量行吗?原文说不行——全局变量仅能保证正确编码时用了唯一实例,系统扩张、开发队伍扩大后,就保证不了有且仅有一个了。
明
阿明
真题考了构造函数:防止外部直接初始化,构造函数要设成什么型?
雅
小雅
原文:通过将 Singleton 类的构造函数设为 protected 型或 private 来防止外部对其直接初始化,答案 protected 或 private,两个都算对。干扰项里 public 正好相反;abstract 构造函数在 Java 里根本不合法,final 也拦不住外部 new,都是凑数的。
明
阿明
那别人要用这个实例,怎么拿?
雅
小雅
必须通过 getInstance 方法,在 getInstance 中仅创建一次 uniqueInstance,就能保证系统中的唯一实例。原文还提到 uniqueInstance 有两种初始化策略:Lazy Initialization 懒汉式和 Early Initialization 饿汉式,名字要认识。
明
阿明
哪三个?
雅
小雅
第一,Singleton 仅能保证单一系统内的单一实例,用分布式构件,比如运行在多个 JVM 下的 EJB,就不能保持整个系统的单一实例。第二,它仅适用于系统中至多有一个实例的情况,应避免滥用,过度设计的 Singleton 同用了静态方法的工具类一样没必要,反而可能降低效率。第三,Singleton 不支持继承,C++ 等支持 Template 的语言可以定义 Singleton 模板提高复用性,Java、C# 就得用别的办法。再记一句辨析:Singleton 不等同于 static 类。
明
阿明
创建型五个过完了。结构型和行为型的名单我也想先混个脸熟,免得下集懵。
雅
小雅
把原文的一句话标签先挂上。结构型七个:Adapter 适配器,解决系统间接口不相容的问题,把类的接口转化为客户程序所希望的接口;Bridge,抽象部分同实现部分相分离;Composite,以树形结构组合对象,使单个对象和组合后的对象具有一致性;Decorator,动态地为对象的某一个方法增加更多的功能,不必继承出新的子类;Facade,为一组类提供一致的访问接口,封装内部具有不同接口的类;Flyweight,共享大量的细粒度对象,节省空间但时间开销会变大;Proxy,为对象提供访问代理,控制访问权限、地址、方式。
明
阿明
判断题有了抓手:问到「树形结构」就是 Composite,「抽象和实现分离」是 Bridge,「代理控制访问」是 Proxy。
明
阿明
判断题有了抓手:问到「树形结构」就是 Composite,「抽象和实现分离」是 Bridge,「代理控制访问」是 Proxy。
雅
小雅
行为型标签继续挂:Interpreter 解释遵循给定语言和文法的句子;Template Method 定义操作模板,一些步骤在子类中实现;Chain of Responsibility 把响应请求的对象组织成一条链传递请求;Command 将请求封装为对象;Iterator 顺序访问对象集合中的各元素;Mediator 用中介对象封装对象间通信,减少耦合;Memento 捕获并恢复对象状态且不破坏封装;Observer 把对象状态广播到一组观察者;State 在内部状态改变时改变行为;Strategy 让算法的变化独立于客户;Visitor 在不改变各元素类的前提下定义作用于元素的新操作。
明
阿明
还有个 J2EE 的分类,它算哪边的?
雅
小雅
不算 GoF 的边。原文说《Core J2EE Patterns》一书把设计模式分为表现层模式、业务层模式和综合层模式,另一套图谱,Intercepting Filter、Session Facade 都在这边,最后一集讲。先把本集必背清一遍。
雅
小雅
四要素:模式名称、问题、解决方案、效果,第四个是效果不是实现;起源:70 年代 Christopher Alexander 提出城市建筑的模式,四人组的书是《设计模式:可复用面向对象软件的基础》;三大分类:创建型管对象创建,结构型管组织,行为型管通信;Factory Method 延迟创建、子类决定;Abstract Factory 按配置或上下文环境加载相同接口的不同类实例;Builder 逐步构造、最后返回;Prototype 深复制原型;Singleton 构造函数设 protected 或 private、getInstance 获取、分布式下保不住唯一、不支持继承。
明
阿明
这套清单在手,前四道题应该都稳了。
雅
小雅
不止。下一集讲结构型里的重头戏:Decorator 装饰模式——它动态扩展功能、避免大量子类的优点,还有它跟 Intercepting Filter 的相似之处都是真题;以及 Facade 外观模式和它在 EJB 里的变身 Session Facade。角色、效果、缺点一个不落。