外观模式(学习笔记)
1. 意图
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
2. 动机
将一个系统划分成若干个子系统有利于降低系统的复杂性。一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小。达到该目标的途径之一就是引入一个外观对象,他为子系统中较一般的设施提供了一个单一而简单的界面

例如有一个编程环境,它允许应用程序访问它的编译子系统。这个编译子系统包含了若干个类来实现这一编译器,如Scanner,Parser,ProgramNode,BytecodeStream和ProgramNodeBuilder。但是对于大多数编译器的用户并不关心语法分析和代码生成这样的细节,他们只是希望编译一些代码。编译子系统提供了一个Compiler类,这个类定义了一个编译器功能的统一接口。Compiler类是一个外观,它给用户提供了一个单一而简单的编译子系统接口。编译器的外观可以方便大多数程序员使用,同时对少数懂得如何使用底层功能的人,它并不隐藏这些功能
3. 适用性
- 当要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂,大多数模式使用时都会产生更多更小的类。这使得子系统更具有可复用性,也更容易对子系统进行定制,但也给一些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,以方便大多数用户的使用
- 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性
- 当需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,可以让它们仅通过Facade进行通信,从而简化了它们之间的依赖关系(有点类似中介者模式)
4. 结构
5. 效果
1) 它对客户屏蔽子系统组件,因而减少了客户处理的对象数目并使得子系统使用起来更加方便
2) 实现了子系统和客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。Facade模式可以消除复杂的循环依赖关系
3) 如果需要,该模式并不限制使用子系统类
6. 代码实现
some_complex_media_library/VideoFile.java
package facade.some_complex_media_library; /** * @author GaoMing * @date 2021/7/19 - 20:59 */ public class VideoFile { private String name; private String codecType; public VideoFile(String name) { this.name = name; this.codecType = name.substring(name.indexOf(".") + 1); } public String getCodecType() { return codecType; } public String getName() { return name; } }
some_complex_media_library/Codec.java
package facade.some_complex_media_library; /** * @author GaoMing * @date 2021/7/19 - 20:59 */ public interface Codec { }
some_complex_media_library/MPEG4CompressionCodec.java
package facade.some_complex_media_library; /** * @author GaoMing * @date 2021/7/19 - 21:00 */ public class MPEG4CompressionCodec implements Codec{ public String type = "mp4"; }
some_complex_media_library/OggCompressionCodec.java
package facade.some_complex_media_library; /** * @author GaoMing * @date 2021/7/19 - 21:00 */ public class OggCompressionCodec implements Codec{ public String type = "ogg"; }
some_complex_media_library/CodecFactory.java
package facade.some_complex_media_library; /** * @author GaoMing * @date 2021/7/19 - 21:01 */ public class CodecFactory { public static Codec extract(VideoFile file) { String type = file.getCodecType(); if (type.equals("mp4")) { System.out.println("CodecFactory: extracting mpeg audio..."); return new MPEG4CompressionCodec(); } else { System.out.println("CodecFactory: extracting ogg audio..."); return new OggCompressionCodec(); } } }
some_complex_media_library/BitrateReader.java
package facade.some_complex_media_library; /** * @author GaoMing * @date 2021/7/19 - 21:01 */ public class BitrateReader { public static VideoFile read(VideoFile file, Codec codec) { System.out.println("BitrateReader: reading file..."); return file; } public static VideoFile convert(VideoFile buffer, Codec codec) { System.out.println("BitrateReader: writing file..."); return buffer; } }
some_complex_media_library/AudioMixer.java
package facade.some_complex_media_library; import java.io.File; /** * @author GaoMing * @date 2021/7/19 - 21:01 */ public class AudioMixer { public File fix(VideoFile result){ System.out.println("AudioMixer: fixing audio..."); return new File("tmp"); } }
facade/VideoConversionFacade.java: 外观提供了进行视频转换的简单接口
package facade.facade; import facade.some_complex_media_library.*; import java.io.File; /** * @author GaoMing * @date 2021/7/19 - 21:02 */ public class VideoConversionFacade { public File convertVideo(String fileName, String format) { System.out.println("VideoConversionFacade: conversion started."); VideoFile file = new VideoFile(fileName); Codec sourceCodec = CodecFactory.extract(file); Codec destinationCodec; if (format.equals("mp4")) { destinationCodec = new OggCompressionCodec(); } else { destinationCodec = new MPEG4CompressionCodec(); } VideoFile buffer = BitrateReader.read(file, sourceCodec); VideoFile intermediateResult = BitrateReader.convert(buffer, destinationCodec); File result = (new AudioMixer()).fix(intermediateResult); System.out.println("VideoConversionFacade: conversion completed."); return result; } }
Demo.java: 客户端代码
package facade; import facade.facade.VideoConversionFacade; import java.io.File; /** * @author GaoMing * @date 2021/7/19 - 20:59 */ public class Demo { public static void main(String[] args) { VideoConversionFacade converter = new VideoConversionFacade(); File mp4Video = converter.convertVideo("youtubevideo.ogg", "mp4"); // ... } }
执行结果
VideoConversionFacade: conversion started.
CodecFactory: extracting ogg audio...
BitrateReader: reading file...
BitrateReader: writing file...
AudioMixer: fixing audio...
VideoConversionFacade: conversion completed.
7. 与其他模式的关系
- 外观模式为现有对象定义了一个新接口,适配器模式则会试图运用已有的接口。适配器通常只封装一个对象,外观通常会作用于整个对象子系统上
- 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂模式来代替外观
- 享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统
-
外观和中介者模式的职责类似:它们都尝试在大量紧密耦合的类中组织起合作
- 外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。子系统本身不会意识到外观的存在。子系统中的对象可以直接进行交流
- 中介者将系统中组件的沟通行为中心化。各组件只知道中介者对象, 无法直接相互交流 - 外观类通常可以转换为单例模式类,因为在大部分情况下一个外观对象就足够了
- 外观与代理模式的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。代理与其服务对象遵循同一接口,使得自己和服务对象可以互换,在这一点上它与外观不同
8. 已知应用
- javax.faces.context.FacesContext 在底层使用了 LifeCycle、ViewHandler 和 NavigationHandler 这几个类,但绝大多数客户端不知道
- javax.faces.context.ExternalContext 在内部使用了 ServletContext、HttpSession、HttpServletRequest、HttpServletResponse 和其他一些类
识别方法:外观可以通过使用简单接口,但将绝大部分工作委派给其他类的类来识别。通常情况下,外观管理着其所使用的对象的完整生命周期

浙公网安备 33010602011771号