门面模式为一组复杂的子系统接口提供了一个更高级的统一接口,通过它可以更容易地访问子系统接口。
欢迎来到 TypeScript 设计模式系列,该系列介绍了使用 TypeScript 进行 Web 开发的一些有用的设计模式。
之前的文章如下:
设计模式对于 web 开发人员来说非常重要,掌握它们可以让我们写出更好的代码。在本文中,我将使用 TypeScript 来介绍 Facade 模式。
Facade 模式为一组复杂的子系统接口提供了一个更高级的统一接口,通过它可以更容易地访问子系统接口,它可以通过引入一个新的 Facade 类来降低原始系统的复杂性,并减少客户机类和子系统类之间的耦合。
Facade 模式要求子系统外部和内部之间的通信通过统一的 Facade 对象进行,Facade 类将客户机与子系统的内部复杂性分离开来,以便客户机只需要处理 Facade 对象,而不需要处理子系统内部的许多对象。
接下来,我将介绍如何使用 Facade 模式,以便更容易地访问子系统接口。为了更好地理解下面的代码,让我们先看看相应的 UML 图:
Facade模式包括以下角色:
-
外观:ShapeFacade
-
子系统:圆形、矩形、三角形
接下来,让我们定义 Shape
接口和三个实现该接口的类,以表示不同的形状。
interface Shape {
draw(): void;
}
class Circle implements Shape {
draw(): void {
console.log("Draw circle");
}
}
class Rectangle implements Shape {
draw(): void {
console.log("Draw rectangle");
}
}
class Triangle implements Shape {
draw(): void {
console.log("Draw triangle");
}
}
定义了不同的形状类之后,让我们创建 ShapeFacade
类:
class ShapeFacade {
private circle: Shape;
private rectangle: Shape;
private triangle: Shape;
constructor() {
this.circle = new Circle();
this.rectangle = new Rectangle();
this.triangle = new Triangle();
}
public drawCircle(): void {
this.circle.draw();
}
public drawRectangle(): void {
this.rectangle.draw();
}
public drawTriangle(): void {
this.triangle.draw();
}
}
通过 ShapeFacade
类,Client 对象可以使用 ShapeFacade
对象绘制不同的形状。
class Client {
private shapeFacade: ShapeFacade;
constructor() {
this.shapeFacade = new ShapeFacade();
}
drawShapes() {
this.shapeFacade.drawCircle();
this.shapeFacade.drawRectangle();
this.shapeFacade.drawTriangle();
}
}
const client = new Client();
client.drawShapes();
为了帮助您更好地理解立面图案的作用,我们用一张图片来展示不使用立面图案和使用立面图案的区别:
在Web项目中,Facade模式的一个常见用例是处理浏览器Web API的兼容性。例如,在不同的浏览器环境中处理事件监听:
function addHandler(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
}
在上面的代码中,我们实现了对不同浏览器添加事件监听器的处理,代码实现起来也很简单,但是有一个问题,就是每次调用的时候都需要判断,这显然是不合理的,对于上面的问题,我们可以通过lazy loading functions来解决。
所谓惰性加载,就是当函数第一次被调用时根据条件执行,第二次被调用时不再判断条件,直接执行函数。
为了实现这个函数,我们可以在满足第一个条件判断的分支中重写被调用的函数:
function addHandler(element, type, handler) {
if (element.addEventListener) {
addHandler = function (element, type, handler) {
element.addEventListener(type, handler, false);
};
} else if (element.attachEvent) {
addHandler = function (element, type, handler) {
element.attachEvent("on" + type, handler);
};
} else {
addHandler = function (element, type, handler) {
element["on" + type] = handler;
};
}
// Ensure that the first call can perform monitoring normally
return addHandler(element, type, handler);
}
除了使用上面的方法,我们还可以使用自执行函数来实现惰性加载:
const addHandler = (function () {
if (document.addEventListener) {
return function (element, type, handler) {
element.addEventListener(type, handler, false);
};
} else if (document.attachEvent) {
return function (element, type, handler) {
element.attachEvent("on" + type, handler);
};
} else {
return function (element, type, handler) {
element["on" + type] = handler;
};
}
})();
最后,我们来总结一下外观模式的使用场景:
-
客户端程序与多个子系统之间存在着很强的依赖关系,引入facade类可以将子系统与客户端和其他子系统解耦,从而提高子系统的独立性和可移植性。
-
在分层结构中,facade模式可以用于定义系统中每个层的入口点,而不是在层之间创建直接连接,而是通过facade类建立连接,减少层之间的耦合。
如果您有任何问题,请随时给我留言。我以后会继续介绍其他的模式,如果您感兴趣,
欢迎关注公众号:文本魔术,了解更多