在上一篇 什么是设计模式? 中,我们介绍了什么是设计模式,设计模式的基本原则与常见分类。
我们接下来陆续介绍具体的设计模式和使用方法,今天给大家带来的是“工厂模式”的介绍。
工厂模式是一种非常常用的创建型设计模式,其提供了创建对象的最佳方式。在创建对象时,不会对客户端暴露对象的创建逻辑,而是通过使用共同的接口来创建对象。
工厂模式就是隐藏了创建一个实例的复杂度,只需要提供一个简单的接口调用,直接完成创建实例的目的。
工厂模式一般分为三类:
简单工厂模式(Simple Factory Pattern):专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都具有共同的父类。创建型的工厂模式一共分为三种:
工厂方法模式(Factory Method Pattern)又称为工厂模式,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
其实这三种工厂模式的区别,我认为没有太大必要去细分,重点还是在于应用。
实际需求
假如有这样一个需求:
我们想在网页面里插入一些元素,而这些元素类型不固定,可能是图片,也有可能是连接,甚至可能是文本,根据工厂模式的定义,我们需要定义工厂类和相应的子类,我们先来定义子类的具体实现(也就是子函数):
let page = page || {};
page.dom = page.dom || {};
//子函数1:处理文本
page.dom.Text = function () {
this.insert = function (where) {
var txt = document.createTextNode(this.url);
where.appendChild(txt);
};
};
//子函数2:处理链接
page.dom.Link = function () {
this.insert = function (where) {
var link = document.createElement('a');
link.href = this.url;
link.appendChild(document.createTextNode(this.url));
where.appendChild(link);
};
};
//子函数3:处理图片
page.dom.Image = function () {
this.insert = function (where) {
var im = document.createElement('img');
im.src = this.url;
where.appendChild(im);
};
};
那么我们如何定义工厂处理函数呢?其实很简单:
page.dom.factory = function (type) {
return new page.dom[type];
}
使用方式如下:
let o = page.dom.factory('Link');
o.url = 'http://www.cnblogs.com';
o.insert(document.body);
比如一个非常常见的场景,就是 jQuery 的选择器:
class jQuery {
constructor(selector) {
super(selector)
}
// ....
}
window.$ = function(selector) {
return new jQuery(selector)
}
这样的代码非常明显,我们需要构建一个 jQuery 实例时,只需要:
$('selector')
因为 作为一个函数,它直接返回了 new jQuery(selector),开发者不需要再麻烦地使用 new $('selector') 方式。
除了 jQuery 以外,React 开发者常用的 React.createElement 也是工厂模式的体现:
React.createElement('span', null, 'Factory Pattern!')
只要具有组件化思想的类库或者框架,无一例外都会使用工厂模式去创建组件实例,除了提到的 React、jQuery(jQuery UI) 以外,Vue,甚至是更古老的 ExtJS 都不例外。
例如:
class Car {
constructor(options) {
const {doors = 4, state = 'new', color = 'black'} = options
this.doors = doors
this.state = state
this.color = color
}
}
class Truck {
constructor(options) {
const {wheelSize = 'medium', state = 'used', color = 'silver'} = options
this.wheelSize = wheelSize
this.state = state
this.color = color
}
}
class VehicleFactory {
createVehicle(options) {
switch (options.type) {
case 'car':
this.vehicleTarget = Car
break;
case 'truck':
this.vehicleTarget = Truck
break;
default:
this.vehicleTarget = Car
break;
}
return new this.vehicleTarget(options)
}
}
let factory = new VehicleFactory()
let instance1 = factory.createVehicle({
type: 'car',
color: 'yellow',
doors: 4
})
let instance2 = factory.createVehicle({
type: 'truck',
state: 'new',
wheelSize: 'small'
})
尝试:
instance1 instanceof Car
instance2 instanceof Truck
以下几种情景下工厂模式特别有用:
《前端面试题宝典》经过近一年的迭代,现已推出 小程序
和 电脑版刷题网站 (https://fe.ecool.fun/
),欢迎大家使用~
同时,我们还推出了面试辅导的增值服务,可以为大家提供 “简历指导” 和 “模拟面试” 服务,感兴趣的同学可以联系小助手(微信号:interview-fe)进行报名。