外观模式(Facade),他隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。这种类型的设计模式属于结构性模式。为子系统中的一组接口提供了一个统一的访问接口,这个接口使得子系统更容易被访问或者使用。类似的实际例子有消息中间件,把一个数据丢到消息中间件,谁需要,谁去消息后中间件去拿。这种设计模式可以用于解耦。
我们举个栗子,你要去工厂制造一辆汽车,你需要先从门口去一楼制造引擎,再从门口去二楼制造底盘,最后从门口去五楼制造变速箱,然后去四楼制造轮胎,这样整个流程会特别繁琐,外观模式就提供一个门面接口(Facade),门面内把原来的逻辑流程进行封装,你只需要从门口去到这个门面,门面内部分工帮你去管理汽车的制造流程。回归代码世界,如果我们不使用门面模式,需要调用发动机制造的对象、底盘制造的对象变速箱制造的对象等等,这样你的业务逻辑便显得杂乱无章并且代码各个子系统之间的耦合度很高,显然这样做不是最优方案。
将汽车制造流程逻辑进行封装,只提供一个汽车制造接口,当客户访问汽车接口(Facade)时,工厂内部流水线制造组装完成一辆用户所需的汽车。这样做的优点就是:把多个对象之间的调用交互变为一个对象与一个接口之间的交互,降低代码耦合度。与用户分别调用子系统模块相比,客户不能自己定义引擎、底盘等配件的型号,所以门面模式提供的功能有限,但是它可以快速且简便的提供客户真正关心的需求。
假设我们有一个需求:将ogg格式的视频文件转换为mp4格式,那么我们需要读取ogg格式的文件流,将流转换为音频流、视频流,写入文件,再进行音频流、视频流组合,最后完成转换,当然我们可以单个流程进行调用,下面的代码中有两种方式的具体体现。
代码已经开源至Github
https://github.com/FirstMrRight/Design_pattern
Codec.java
/**
* @author Liutx
* @date 2020/12/26 22:37
* @Description 解码器接口
*/
public interface Codec {
}
MPEG4CompressionCodec.java
/**
* @author Liutx
* @date 2020/12/26 22:38
* @Description MPEG4解码器
*/
public class MPEG4CompressionCodec implements Codec {
/**
* 该解码器能够行使的功能
*/
public String type = "mp4";
}
OggCompressionCodec.java
/**
* @author Liutx
* @date 2020/12/26 22:40
* @Description ogg解码器
*/
public class OggCompressionCodec implements Codec {
public String type = "ogg";
}
BitrateReader.class
/**
* @author Liutx
* @date 2020/12/26 22:48
* @Description 字节读取转换为Video
*/
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;
}
}
AudioMixer.java
/**
* @author Liutx
* @date 2020/12/26 22:50
* @Description 混音器,组合视频、音频
*/
public class AudioMixer {
public File fix(VideoFile result){
System.out.println("AudioMixer: fixing audio...");
return new File("tmp");
}
}
VideoFile.java
/**
* @author Liutx
* @date 2020/12/26 22:33
* @Description
*/
public class VideoFile {
private String name;
private String codecType;
public VideoFile(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCodecType() {
return codecType;
}
public void setCodecType(String codecType) {
this.codecType = codecType;
}
}
VideoConversionFacade.java
/**
* @author Liutx
* @date 2020/12/26 22:58
* @Description 门面模式封装内部流程、对外提供接口
*/
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;
}
}
ClientDemo.java (调用)
public class ClientDemo {
public static void main(String[] args) {
VideoConversionFacade converter = new VideoConversionFacade();
File mp4Video = converter.convertVideo("video.ogg", "mp4");
System.out.println("===================================");
VideoFile file = new VideoFile("video.ogg");
CodecFactory.extract(file);
BitrateReader.read(file,new OggCompressionCodec());
BitrateReader.convert(file,new MPEG4CompressionCodec());
AudioMixer audioMixer = new AudioMixer();
audioMixer.fix(file);
}
}
Console 输出:
VideoConversionFacade: conversion started.
CodecFactory: extracting ogg audio...
BitrateReader: reading file...
BitrateReader: writing file...
AudioMixer: fixing audio...
VideoConversionFacade: conversion completed.
===================================
CodecFactory: extracting ogg audio...
BitrateReader: reading file...
BitrateReader: writing file...
AudioMixer: fixing audio...
总结:
使用门面模式可以非常优雅的实现代码的调用,当然我们也可以自己使用单独调用对象的方式实现相同的功能,但是这种方式不仅显得代码杂乱无章,而且这种方式一看就非常不环保。