JavaScript设计模式-结构型-外观模式
概要
外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。
问题
假设你必须在代码中使用某个复杂的库或框架中的众多对象。 正常情况下, 你需要负责所有对象的初始化工作、 管理其依赖关系并按正确的顺序执行方法等。
最终, 程序中类的业务逻辑将与第三方类的实现细节紧密耦合, 使得理解和维护代码的工作很难进行。
解决方案
外观类为包含许多活动部件的复杂子系统提供一个简单的接口。 与直接调用子系统相比, 外观提供的功能可能比较有限, 但它却包含了客户端真正关心的功能。
如果你的程序需要与包含几十种功能的复杂库整合, 但只需使用其中非常少的功能, 那么使用外观模式会非常方便。
比如,浏览器通过接口stream
流来完成文件下载,会用到不同文件类型的逻辑处理,还有下载动作以及其他复杂步骤,客户端要完成下载这个stream
流文件,需要依次按正确步骤调用这些复杂方法。
我们以创建一个封装所需功能并隐藏其他代码的外观类, 从而避免客户端直接与较多的负责类进行交互。
该结构还能将未来框架升级或更换所造成的影响最小化, 因为你只需修改程序中外观方法的实现即可。
外观模式模式结构图
-
外观 (Facade) 提供了一种访问特定子系统功能的便捷方式, 其了解如何重定向客户端请求, 知晓如何操作一切活动部件。
-
创建附加外观 (Additional Facade) 类可以避免多种不相关的功能污染单一外观, 使其变成又一个复杂结构。 客户端和其他外观都可使用附加外观。
-
复杂子系统 (Complex Subsystem) 由数十个不同对象构成。 如果要用这些对象完成有意义的工作, 你必须深入了解子系统的实现细节, 比如按照正确顺序初始化对象和为其提供正确格式的数据。
子系统类不会意识到外观的存在, 它们在系统内运作并且相互之间可直接进行交互。
-
客户端 (Client) 使用外观代替对子系统对象的直接调用。
举例
案例一,下载不同类型的stream文件
在本例中, 外观模式简化了客户端与复杂文件下载类之间的交互。
/**
* 这里有复杂功能和一些独立板块类。我们不知晓其中的代码,因此无法
* 对其进行简化。
* buffer处理为xlsx Blob
*/
class XlsxReaderBlob {
constructor(buffer) {
this.blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
}
}
/**
* buffer处理为image Blob
*/
class ImageReaderBlob {
constructor(buffer) {
this.blob = new Blob([buffer], { type: 'image/*' })
}
}
/**
* 处理blob文件下载
*/
class DownloadMixer {
constructor(fileName, blob) {
this.fileName = fileName
this.blob = blob
}
exec() {
let aLink = document.createElement('a')
aLink.href = URL.createObjectURL(this.blob)
aLink.setAttribute('download', this.fileName)
document.body.appendChild(aLink)
aLink.click()
document.body.removeChild(aLink)
}
}
/**
* 为了将功能的复杂性隐藏在一个简单接口背后,创建了一个外观类。它是在
* 功能性和简洁性之间做出的权衡。
* 流转换器
*/
class StreamConverter {
downLoad(buffer, fileName, fileType) {
let blobSource
if (fileType = 'xlsx') {
blobSource = new XlsxReaderBlob(buffer)
} else if (fileType == 'image') {
blobSource = new ImageReaderBlob(buffer)
}
let link = new DownloadMixer(fileName, blobSource.blob)
link.exec()
}
}
/**
* 应用程序的类并不依赖于复杂独立类中成千上万的类。同样,如果你决定更换功能板块,
* 那只需重写外观类即可
* [clientCode 客户端]
*/
function clientCode() {
let fileTarget = new StreamConverter()
fileTarget.downLoad('buffer', '1.jpg', 'xlsx')
}
clientCode()
output
download image type file success.
总结
优缺点
- 优点
- 松散耦合:外观模式松散了客户端与子复杂类之间的耦合关系,利于模块更容易扩展和维护,未来更换依赖复杂类, 只需修改程序中外观方法的实现即可。
- 简单易用;客户端不需要了解子系统\子复杂类内部各模块的具体实现,只要跟外观层交互,委托外观层去实现即可。
- 缺点
- 最大缺点是违背了开闭原则,当增加或移除子系统时需要修改外观类。
适合应用场景
- 为一个复杂系统提供一个给外部访问的简单接口。
- 当客户端与多个子系统之间存在很大的依赖性时,引入外观模式将客户端与子系统解耦。
- 在分层结构系统构建时,引入外观模式定义系统中每层入口点,层与层之间通过外观建立联系,降低层之间的耦合度。