外观形式

Facade门面形式

外观形式是一种结构型规划形式, 能为程序库、 结构或其他杂乱类供给一个简略的接口。

遇到的问题

假设你必须在代码中运用某个杂乱的库或结构中的很多目标。 正常情况下, 你需求负责所有目标的初始化作业、 办理其依赖联系并按正确的顺序执行办法等。

终究, 程序中类的业务逻辑将与第三方类的完成细节严密耦合, 使得了解和维护代码的作业很难进行。

解决方案

外观类为包括许多活动部件的杂乱子体系供给一个简略的接口。 与直接调用子体系比较, 外观供给的功用或许比较有限, 但它却包括了客户端真实关心的功用。

假如你的程序需求与包括几十种功用的杂乱库整合, 但只需运用其中十分少的功用, 那么运用外观形式会十分方便,

例如, 上传猫咪搞笑短视频到社交媒体网站的使用或许会用到专业的视频转化库, 但它只需运用一个包括 encode(filename, format)办法 (以文件名与文件格局为参数进行编码的办法) 的类即可。 在创立这个类并将其连接到视频转化库后, 你就具有了自己的第一个外观。

真实世界类比

2023 跟我一起学设计模式: 外观模式(facade)

电话购物。

当你经过电话给商店下达订单时, 接线员便是该商店的所有服务和部门的外观。 接线员为你供给了一个同购物体系、 支付网关和各种送货服务进行互动的简略语音接口。

外观形式结构

2023 跟我一起学设计模式: 外观模式(facade)

  1. 外观 (Facade) 供给了一种拜访特定子体系功用的便捷方法, 其了解怎么重定向客户端请求, 知晓怎么操作一切活动部件。

  2. 创立附加外观 (Additional Facade) 类能够防止多种不相关的功用污染单一外观, 使其变成又一个杂乱结构。 客户端和其他外观都可运用附加外观。

  3. 杂乱子体系 (Complex Subsystem) 由数十个不同目标构成。 假如要用这些目标完结有意义的作业, 你必须深化了解子体系的完成细节, 比方按照正确顺序初始化目标和为其供给正确格局的数据。

    子体系类不会意识到外观的存在, 它们在体系内运作而且相互之间可直接进行交互。

  4. 客户端 (Client) 运用外观替代对子体系目标的直接调用。

伪代码

在本例中, 外观形式简化了客户端与杂乱视频转化结构之间的交互。

2023 跟我一起学设计模式: 外观模式(facade)

运用单个外观类隔离多重依赖的示例

你能够创立一个封装所需功用并躲藏其他代码的外观类, 然后无需使悉数代码直接与数十个结构类进行交互。 该结构还能将未来结构升级或替换所形成的影响最小化, 由于你只需修正程序中外观办法的完成即可。

// 这里有杂乱第三方视频转化结构中的一些类。我们不知晓其中的代码,因此无法
// 对其进行简化。
class VideoFile
// ……
class OggCompressionCodec
// ……
class MPEG4CompressionCodec
// ……
class CodecFactory
// ……
class BitrateReader
// ……
class AudioMixer
// ……
// 为了将结构的杂乱性躲藏在一个简略接口背面,我们创立了一个外观类。它是在
// 功用性和简洁性之间做出的权衡。
class VideoConverter is
    method convert(filename, format):File is
        file = new VideoFile(filename)
        sourceCodec = (new CodecFactory).extract(file)
        if (format == "mp4")
            destinationCodec = new MPEG4CompressionCodec()
        else
            destinationCodec = new OggCompressionCodec()
        buffer = BitrateReader.read(filename, sourceCodec)
        result = BitrateReader.convert(buffer, destinationCodec)
        result = (new AudioMixer()).fix(result)
        return new File(result)
// 使用程序的类并不依赖于杂乱结构中成千上万的类。相同,假如你决议替换结构,
// 那只需重写外观类即可。
class Application is
    method main() is
        convertor = new VideoConverter()
        mp4 = convertor.convert("funny-cats-video.ogg", "mp4")
        mp4.save()

外观形式合适使用场景

假如你需求一个指向杂乱子体系的直接接口, 且该接口的功用有限, 则能够运用外观形式。

子体系通常会跟着时间的推动变得越来越杂乱。 即便是使用了规划形式, 通常你也会创立更多的类。 虽然在多种景象中子体系或许是更灵敏或易于复用的, 但其所需的配置和样板代码数量将会增长得更快。 为了解决这个问题, 外观将会供给指向子体系中最常用功用的快捷方法, 能够满足客户端的大部分需求。

假如需求将子体系安排为多层结构, 能够运用外观。

创立外观来定义子体系中各层次的入口。 你能够要求子体系仅运用外观来进行交互, 以削减子体系之间的耦合。

让我们回到视频转化结构的比方。 该结构能够拆分为两个层次: 音频相关和视频相关。 你能够为每个层次创立一个外观, 然后要求各层的类必须经过这些外观进行交互。 这种方法看上去与中介者形式十分类似。

完成方法

  1. 考虑能否在现有子体系的基础上供给一个更简略的接口。 假如该接口能让客户端代码独立于很多子体系类, 那么你的方向便是正确的。
  2. 在一个新的外观类中声明并完成该接口。 外观应将客户端代码的调用重定向到子体系中的相应目标处。 假如客户端代码没有对子体系进行初始化, 也没有对其后续生命周期进行办理, 那么外观必须完结此类作业。
  3. 假如要充分发挥这一形式的优势, 你必须确保所有客户端代码仅经过外观来与子体系进行交互。 此后客户端代码将不会受到任何由子体系代码修正而形成的影响, 比方子体系升级后, 你只需修正外观中的代码即可。
  4. 假如外观变得过于臃肿, 你能够考虑将其部分行为抽取为一个新的专用外观类。

外观形式优缺点

  • 你能够让自己的代码独立于杂乱子体系。

  • 外观或许成为与程序中所有类都耦合的[上帝目标]。

代码示例

  • Golang 外观形式讲解和代码示例 – 掘金 ()