ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

前端精读周刊:Facade 外观模式——用一个高层接口屏蔽子系统复杂度的结构型设计模式

前端精读周刊:Facade 外观模式——用一个高层接口屏蔽子系统复杂度的结构型设计模式 文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载本篇为前端精读周刊「设计模式」系列第 176 期聚焦结构型模式中的 Facade外观模式。文章以原精读为骨架结合周刊内 《源码学习》 中 react-redux 门面模式的真实案例讲清为什么需要外观、外观解决了什么、什么时候不该用外观三个核心问题。读完你将掌握外观模式的意图与适用场景、单向依赖的结构特征、TypeScript 落地写法以及它与抽象工厂的取舍边界。模式定位结构型模式中的门面Facade外观模式属于结构型模式是日常开发中被高频使用的一种设计模式。其官方意图可以浓缩为一句话为子系统中的一组接口提供一个一致的界面Facade 模式定义了一个高层接口这个接口使得这一子系统更加容易使用。理解这段意图需要抓住两个关键词一组接口——外观模式服务的对象从来不是单个类而是由多个类、多个模块组成的子系统更加容易使用——外观不改变子系统内部的能力只改变调用者接触子系统的方式把复杂度挡在门面之后。这正好对应 GoF 对门面模式的核心描述门面不新增功能它只是把子系统内部错综复杂的协作关系收敛为一个让外部更容易理解的入口。这也是它被称为门面/Facade的原因——你看到的只是漂亮的大门门后有多少房间、多少走廊与你无关。三个生活化例子先感受什么时候该用外观设计模式需要放在日常场景里才能真正理解。下面三个例子分别对应信息检索型子系统政务流程型子系统多 App 协同型子系统覆盖了外观模式最常见的三种使用动机。图书管理员屏蔽信息复杂度图书馆是一个复杂的系统图书虽然按一定规则摆放但只有内部人员清楚这套规则。作为初次到访的读者想快速找到一本书最直接的办法是问图书管理员而不是先学习图书馆的设计——否则你可能需要在各个楼宇间来回奔走借书流程也会拉得很长。图书管理员就是图书馆子系统的 Facade读者只需要向其提出诉求不需要关心管理员如何与内部书架系统、借还系统打交道。这里的关键点是——图书馆内部的复杂性并没有消失只是被管理员Facade消化掉了。最多跑一次便民服务屏蔽流程复杂度浙江省推出的最多跑一次服务非常方便很多办事流程被简化无论是证件办理还是业务受理几乎只要跑一次需要持续数天的后续流程也会通过短信或 App 完成。这同样符合外观模式的定义政府系统内部的办事流程可能没有太大变化但通过抽象出便民办事处Facade市民只需与这一个窗口对接而无需在车管所、驾校之间来回奔波。背后要做的事一件都没少只是便民办事处替你做了——这正是外观模式的本质不是减少工作量而是集中工作量。iPhone 快捷指令屏蔽App 分布复杂度手机里的 App 非常多想熟练使用每个功能就得知道每个功能藏在哪个 App 的哪个入口。而快捷指令功能可以把 App 的某些能力单独提取出来重组为一套新的功能组用户只需接触拍照付款计算这类语义化动作不用关心背后调用的是支付宝还是微信、系统内置相机还是第三方摄像 App更不用关心这些 App 内部的功能入口在哪里——对接全部在快捷指令中自动完成。快捷指令同样是一种外观模式它为手机 App 功能这个分散的子系统提供了一个统一的、语义化的高层接口。三个例子的共同规律例子被屏蔽的子系统Facade 角色屏蔽的复杂度类型图书管理员图书馆书架 借还体系图书管理员信息分布复杂度最多跑一次车管所、驾校等政务流程便民办事处跨机构流程复杂度iPhone 快捷指令支付宝、微信、相机等 App快捷指令动作组多 App 入口分布复杂度可以提炼出外观模式的一个通用判据当调用者只想用结果、而不想知道过程时就存在引入外观的动机。意图再解释屏蔽复杂性而不是消灭复杂性回到那段官方意图——为降低一个拥有多个接口的子系统内部复杂性我们需要一个外观来屏蔽内部的复杂性外观模式定义的是一个高层接口这个接口直连子系统的内部实现调用这个高层接口的人不需要关心子系统内部的实现对不想了解子系统内部实现的人来说提高了易用度同时外观模式是可选而非强制的想要深度定制时完全可以绕过外观直接使用子系统提供的类。并不是有了外观就必须通过外观调用而是根据实际需要判断采用哪种调用方式。这引出了外观模式一条非常重要的设计原则外观是增量而不是替代。子系统的原接口保持完整、可被直接访问外观只是在它前面加了一层更友好的入口。这样既保证了易用性又保住了定制能力避免出现为了简单而砍掉能力的极端。结构分析单向依赖是 Facade 模式的骨架原精读给出了外观模式的结构图图中 Facade 直接指向子系统中的类其最值得记住的结构特征是Facade 直接指向子系统中的类而子系统的类不会反向指向 Facade。展开来说方向性依赖箭头永远是Facade → 子系统子系统对 Facade 的存在完全无感知。这意味着把 Facade 从项目中删除子系统依然可以独立运行——外观是可拆卸的。组合关系Facade 通常持有子系统中多个类的引用并在内部编排它们的协作顺序把多个类的多次交互压缩成一次调用。不反向引用子系统类不引用 Facade是为了避免出现外观依赖子系统、子系统又依赖外观的环保证子系统可以被独立复用与测试。这条单向依赖约束是外观模式与中介者Mediator模式的关键分野——中介者模式中参与者之间互相通信、共同依赖中介者而外观模式只存在一条门面指向内部的依赖带。TypeScript 代码例子为编译器子系统做一层门面原精读给出了一个 TypeScript 示例用三个互相嵌套的类代表一个被拆解开的子系统再用一个外观类把组合关系收敛起来。下面是完整继承并补全可运行细节的版本// 假设一个子系统是三个类结合使用的为了抽象而解耦开了 class C { public run() { console.log(词法分析 Tokenize...) } } class B { private c: C constructor(c: C) { this.c c } public run() { this.c.run() console.log(语法分析 Parse...) } } class A { private b: B constructor(b: B) { this.b b } public run() { this.b.run() console.log(代码生成 Generate...) } } // 它们组合成了一种常用功能 // 使用外观模式屏蔽子类细节外部只需面对一个 Compile class Compile { public run() { const parser new A(new B(new C)) parser.run() } } // 外部使用只认识 Compile不认识 A、B、C const compile new Compile() compile.run() // 输出 // 词法分析 Tokenize... // 语法分析 Parse... // 代码生成 Generate...对照原精读的要点这个例子解释了三个层次子系统被刻意拆开A → B → C的构造链模拟了词法分析 → 语法分析 → 代码生成这样一条有依赖顺序的流水线。之所以拆开是为了让每个环节可以独立演进、独立测试。外观负责编排Compile.run()内部完成了new A(new B(new C))的组装与调用顺序编排外部调用者只需要知道Compile类不需要了解背后的A、B、C及其组合关系。深度定制仍然可能如果某个调用方需要跳过B直接拿C的能力完全可以绕过Compile直接new C()使用——这正是外观是可选而非强制的代码形态。补充说明原精读的示例代码中A、B、C未定义run方法parser.run()在严格模式下无法通过编译。上面版本为每个类补齐了run实现并把输出设计成一条可观察的调用链便于读者直观看到外观内部如何按序调度子系统代码语义与原精读完全一致。如果希望外观更进一步收敛参数还可以把它设计成参数化入口例如compile.run(sourceCode, options)内部再根据参数决定是否走缓存、是否输出 AST 等——外观的职责始终是把复杂决策藏在门后。结合周刊证据在真实源码中识别 Facade 模式本仓库是「前端精读周刊」其定位与设计模式系列在 readme.md 中即有说明周刊结合大厂工作经验解读前沿技术与源码其中设计模式是一块完整的独立模块设计模式 目录下共 23 篇覆盖 23 种模式。而外观模式的价值正体现在周刊的源码解读实践中——阅读他人框架源码时识别 Facade 是快速抓住框架入口的高效方式。在 《源码学习》 一文中作者以 react-redux 的connect为案例展示了这一思路比如从生成 connect 函数的createConnect我们就可以学习到 Facade Pattern——门面模式。这里的对应关系是createConnect收敛了connectHOC、mapStateToPropsFactories、mapDispatchToPropsFactories、mergePropsFactories、selectorFactory等一系列工厂与配置项对外暴露一个稳定的connect入口业务方只需要调用connect(mapStateToProps, mapDispatchToProps)(Component)完全不用关心内部如何挑选、组合这些工厂函数——这正是为一个拥有多个接口的子系统提供一个一致的界面的工程化体现。从源码结构看这样的门面在真实框架中随处可见识别它的三个信号是一个对外 API 背后同时引用了多个内部模块该 API 把选择 编排 组装收敛在函数体内外部不可见内部模块互相独立、不反向依赖该 API满足单向依赖特征。把这个识别方法反过来用就得到了外观模式的设计指引当你发现自己的业务代码反复出现为了做一件事要先 new 三个对象再按顺序调用时就该考虑为它加一层门面了。弊端与适用边界外观不是万能入口原精读明确指出外观模式并不适合所有场景至少存在两类反例其一子系统足够易用时再包一层外观就是画蛇添足。如果子系统的接口本身已经足够简单、稳定调用者的心智负担很小此时引入 Facade 反而多了一层间接跳转增加了代码阅读成本属于典型的过度设计。其二系统难以抽象出通用功能时外观可能无所适从。外观的价值在于一个高层接口能覆盖大部分使用场景。如果子系统的使用方式高度碎片化每种用法都只覆盖一小撮调用者那么设计出来的高层接口适用范围会很窄外观的意义就非常小——门面盖不住内部五花八门的房间就谈不上简化。此外从维护视角看外观还承担着变更隔离的职责只要子系统内部实现发生变化而对外承诺的接口不变所有经由外观的调用方都不会受影响。这既是外观的优势也意味着外观接口本身是契约一旦被大量调用方依赖其演进就应当遵循向后兼容的原则。与抽象工厂的对比谁更适合隐藏子类实现原精读在总结中特别提到抽象工厂模式也可以代替外观模式实现隐藏子类具体实现的效果但外观模式的描述更具有通用性。结合周刊中另一篇 《设计模式 - Abstract Factory 抽象工厂》 可以更清楚地看到二者的分工抽象工厂专注于创建这一环节它把创建一系列相关或相互依赖的对象的细节隐藏起来调用方只知道拿到了一组产品不知道具体是哪个ConcreteFactory产出的。它隐藏的是对象的创建过程。外观模式则面向使用这一环节它把多个已存在的对象如何协作完成一项业务的细节隐藏起来调用方只调用一个高层方法。它隐藏的是对象的协作流程。用原文的例子理解抽象工厂回答的是折线图到底用 Echarts 还是 G2 画的外观回答的是点击表格单元格后表格、模态框、折线图三者如何联动。所以在实际设计中二者往往叠加使用而非互斥外观的底层可以调用抽象工厂来获得具体的产品实现二者分别管理怎么造出来与怎么用起来两个维度的复杂度。正因为外观模式只约束接口一致性而不约束子系统内部如何创建对象它的适用范围更宽这也是原精读称其描述更具有通用性的原因。总结外观模式是一把收敛复杂度的钥匙它要做的不是消灭子系统的复杂度而是把复杂度集中到一个门面之后让大多数调用者用最小的心智成本完成业务同时保留绕过门面直接定制的能力做到易用与灵活兼得。值得记住的四句话意图为子系统的多个接口提供一个一致的、更易用的高层接口结构Facade 单向指向子系统子系统不反向引用 Facade取舍子系统够简单或无法抽象出通用接口时外观反而有害对照抽象工厂隐藏创建细节外观隐藏协作细节二者可叠加使用。在工程实践中当你发现调用方需要反复记忆A、B、C 怎么组合才能完成一件事时就应当像周刊源码解读中 react-redux 的createConnect那样把一个门面立起来——这是结构型模式里投入产出比最高、也最容易在日常代码中落地的设计决策。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐java-design-patterns 中的 Facade外观模式用统一接口驯服复杂子系统java design patterns 中的 Facade外观模式用统一接口驯服复杂子系统 本文基于 java design patterns 仓库中示例工程教程Java 设计模式之 Facade外观模式为复杂子系统提供统一入口 —— java-design-patterns 仓库深度解读Java 设计模式之 Facade外观模式为复杂子系统提供统一入口 —— java design patterns 仓库深度解读 Facade外观模式示例工程教程java-design-patterns 项目中的 Facade外观模式实战用统一接口简化复杂子系统java design patterns 项目中的 Facade外观模式实战用统一接口简化复杂子系统 Facade外观是 Gang of Four 定示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表