第10章 · 播客 第4集
设计模式 · J2EE 模式与模式使用原则
🎙️ 本集播客:第4集:本章收官。J2EE 两大模式逐角色拆解——Intercepting Filter 的 FilterManager、FilterChain、Target,Session Facade 为什么能减少远程调用 EJB 的次数;它和 Decorator 的相似之处是高频考点。然后是使用设计模式的原则:场合、力度、重构时机。末集附全章必背清单,四要素、三分类、代表模式意图与易混对一网打尽。 语音由微软 Edge 神经网络语音预生成(女声·晓伊,男声·云希)。点击下方任意对话可直接从该句开始播,当前句朗读时下一句已预先加载,无缝衔接。
明
阿明
行为型的观察者、中介者都过完了,23 个 GoF 模式算是啃到尾了。可我翻目录发现后面还有「其他设计模式」,说的是 J2EE?
雅
小雅
对。GoF 之后人们继续对设计模式进行发掘,总结出更多的模式;在 J2EE 应用领域,人们对使用 J2EE 框架开发的应用程序也总结出一系列设计模式。注意它跟 GoF 三分类是两套体系:《Core J2EE Patterns》一书把模式分为表现层模式、业务层模式和综合层模式。所以题目问「三类中解决对象创建问题的是哪个」,选项里放「J2EE 设计模式」纯粹是干扰项,答案只能是创建型。
明
阿明
懂了,创建型、结构型、行为型是 GoF 的分法,J2EE 是另一拨。本章重点讲哪几个?
雅
小雅
两个:Intercepting Filter 筛选器,和 Session Facade。先说 Intercepting Filter 意图解决的问题:在使用 MVC 架构进行 Web 应用开发时,通常需要对来自于客户的请求进行一些预处理,如验证客户身份、验证请求来源、对请求解码等,然后再传递给控制器。如果把这些预处理都交由控制器来完成,将增加控制器的复杂度,而且难以维护和扩展。
明
阿明
那把预处理写进每个 Servlet 里总行了吧?
雅
小雅
也不行,原文举的就是这个反例:在真正响应客户端请求前经常需要预处理,如客户身份验证、客户 Session 的合法性验证、字符集转码、客户请求记录等,如果写进每一个 Servlet,预处理的代码就「侵入」了真正的处理程序,使得代码变得更加难以维护。
雅
小雅
Intercepting Filter 的结构就三个角色:FilterManager 负责调度整个 FilterChain,它将在请求到达 Target 前拦截请求,并传递给 FilterChain,由 FilterChain 中的过滤器依次进行预处理;直到请求经过最后一个过滤器后才完成了全部的预处理,然后由 FilterManager 把请求转发给实际的目标。
明
阿明
一环扣一环过筛子,过完最后一道才放行到真正的处理逻辑。
雅
小雅
效果:它使得预处理的逻辑和真正的处理逻辑分离,进行实际处理的 Target 只需要关心具体的逻辑,而同请求相关的预处理都放在 FilterManager 中进行;同时解除了这两类处理的耦合性,扩展、修改预处理过程变得容易,系统具有更好的维护性和扩展性。
雅
小雅
接下来是本模式最可能出题的一句话,原文专家提示:仔细观察 Intercepting Filter 模式就可以发现,它与 Decorator 模式有些类似,都是在真正的处理对象前增加一层扩展对象的操作,只不过实现起来有很大的差别。所以题目问「Intercepting Filter 与哪个模式在实现上有相似之处」,选 Decorator。
明
阿明
干扰项 Singleton、Observer、Mediator 都没有这层「加一层再转发」的关系。对了,这个模式只能用在 J2EE 里吗?
雅
小雅
不是。原文明确说虽然它作为 J2EE 模式出现在有关文献中,但实际上有很广泛的应用,使用 .Net 框架等其他的 Web 应用时也可以使用 Intercepting Filter。引申考点:如果对于不同的请求需要使用不同的 FilterChain——例如按客户不同的编码方式选不同的解码程序——可以用工厂模式来动态创建 Filter 对象;这也是原文「把几种模式综合起来解决复杂问题」的现成例子。
明
阿明
第二个 Session Facade,听名字就是 Facade 模式的亲戚。
雅
小雅
它是 Facade 在 EJB 开发领域的引申。先补背景:EJB 是一种分布式构件,J2EE 将对 EJB 的访问定义为远程访问。例如使用 Servlet 访问 EJB 时,即使 Servlet 和 EJB 部署于同一台主机,但由于两者分置于不同的容器——Servlet 在 Web 容器中,EJB 在 EJB 容器中——Servlet 的默认访问方式也是通过本机的回播地址 127.0.0.1 访问 EJB,从而造成 EJB 访问的效率问题。
明
阿明
同一台机器也得当远程调用走网络,怪不得要尽量减少对 EJB 调用的次数。
雅
小雅
这是第一层动机,还有第二层:EJB 中封装了复杂的业务对象,把这些业务对象的全部暴露给外部也不利于系统的维护。两条合起来——人们把 Session Bean 和 Facade 模式结合起来,封装业务逻辑的接口,形成了 Session Facade 模式。
雅
小雅
具体做法是:一般使用 Session Bean 作为业务逻辑的接口,在 Session Bean 中进行事务处理,确保客户对 Session Bean 的每一次访问即完成一次业务操作。效果是除了获得 Facade 模式的优点外,还可以减少远程调用 EJB 的次数,降低网络接口的负载,提高性能。
明
阿明
这道题我刷到过:Session Facade 在 EJB 开发中的主要作用,A 是用 Session Bean 封装业务逻辑、减少远程调用 EJB 的次数、提高性能。
雅
小雅
就选它。另外三个干扰项都能打回去:B「截取客户请求进行预处理」是 Intercepting Filter 的活;C「保证系统中只有一个 EJB 实例」是 Singleton;D「将请求封装为对象」是 Command。这两个 J2EE 模式的意图必须一字不差地记。
明
阿明
模式讲完了,是不是随时用越好?我看很多项目一上来就全套设计模式伺候。
雅
小雅
这正是本章最后一块、也是原文反复敲黑板的:不能滥用设计模式,尤其在一些简单系统中。原文举了三个不可取的例子:系统中的对象都用工厂模式创建、系统中的工具类都设计成 Singleton、两个对象间的通信还要硬加上一层 Mediator,这些都只能毫无价值地提高系统复杂度,反而不利于系统的理解与维护。
雅
小雅
展开说就是两条。第一,设计模式有其应用的场合,不相宜的场合乱用设计模式有害无益;第二,设计模式主要解决对象之间相互通信、相互依赖的结构关系,架构设计师需要把握好使用设计模式的力度,过度的使用设计模式不但不会提高软件的复用性,反而会让架构变得混乱而难以维护。
明
阿明
所以「应尽可能多地使用以提高架构质量」「所有系统都应使用全部 23 种 GoF 模式」「可以解决所有软件设计问题」这些说法全错。
雅
小雅
全错,标准答案还有正着说的一半:除了最初的设计外,重构也是一个很好的时机,可以在重构时根据需要逐步应用设计模式改良系统,提高维护性和复用性。记「不能滥用 + 重构是好时机」就够了。
明
阿明
上面那几个反例其实还是 Singleton、Mediator 各自相关讨论里的原话吧?
雅
小雅
对,前后是呼应的。Singleton 相关讨论说:它仅适用于系统中至多有一个实例的情况,应避免滥用,过度设计的 Singleton 同静态方法的工具类一样毫无必要,反而可能降低效率;它也不支持继承,而且只能保证单一系统内的单一实例,分布式环境保不住全局唯一。Decorator 同理,装饰类若仅在单一地方使用或只做了简单处理,就要考虑有没有必要用。学习姿势也别忘:最重要的是理解而不是生搬硬套,MVC 既可以看作一种设计模式也可以看作一种架构风格。
明
阿明
全章四集听完了,给我来一份必背清单吧,我照着默写。
雅
小雅
好,从概念起头。四要素一字不能错:模式名称、问题、解决方案、效果,别把「效果」换成「实现」——实现是本章额外补充的描述方式,不属于四要素。架构偏整体全局,模式偏类与对象之间的关系,两者是面向不同层次问题的解决方案。
雅
小雅
三类分类对着三个问题:创建型解决对象创建的问题,结构型解决类与对象的组织结构,行为型解决类与对象如何相互通信。各记代表:创建型 Factory Method 延迟创建、运行期由子类决定实例化哪个类;Abstract Factory 根据不同配置或上下文环境加载具有相同接口的不同类实例,避免类名硬编码;Builder 逐步构造复杂对象;Prototype 深复制原型;Singleton 保证一个类仅有一个实例、提供单一全局访问点,构造函数设为 protected 或 private、通过 getInstance() 获得。
明
阿明
结构型我来报:Adapter 解决接口不相容、把类的接口转化为客户期望的接口;Bridge 抽象与实现分离;Composite 树形结构组合对象;Facade 在原有系统前增加一层、封装子系统接口对外提供一致访问;Flyweight 共享大量细粒度对象,省空间但时间开销变大;Proxy 提供访问代理,控制权限、地址、访问方式。
雅
小雅
行为型挑高频的:Observer 中 Subject 保存观察者列表 observers,attach、detach 增删观察者,notify() 通知所有观察者,update() 是观察者收到通知后执行的方法,别把这两个弄混;Mediator 封装一组对象间的通信,缺点是 Colleague 较多且关系复杂时中介类变得非常复杂难以维护,一般仅在较小范围内使用;Command 将请求封装为对象;Strategy 让对象中算法的变化独立于客户;Chain of Responsibility 把响应请求的对象组织成一条链传递请求。
明
阿明
易混对我记了三组:Decorator 和 Facade 都加一层,前者动态为方法增功能、避免继承大量子类,后者封装子系统降耦合;J2EE 这边 Intercepting Filter 像 Decorator、都是真正的处理对象前加一层,Facade 发展成 Session Facade、用 Session Bean 封装业务逻辑减少远程调用 EJB 的次数提高性能;再加上 MVC 里用的 Observer。
雅
小雅
再补几个容易漏的细节:10.2 节一共详细讨论了 7 种模式——Abstract Factory、Singleton、Decorator、Facade/Session Facade、Mediator、Observer、Intercepting Filter;Observer 广播是双刃剑,要避免广播扩大,在 update() 中尤其不能更新自己观察的 Subject 的状态,否则消息无穷循环。最后是使用原则:不滥用、看场合、控力度,重构是应用设计模式的好时机。
明
阿明
这下四要素、三分类、代表意图、易混对、使用原则全串起来了,设计模式这章我可以合上了。
雅
小雅
合上之前把 12 道练习做一遍,错哪题回对应那集重听。下一章进入测试评审方法,评审的形式、测试的分类与阶段,比这章更偏记忆。