在 Javascript 中, 因为函数的简单性、可用性和对外暴露少的特性,所以相对比纯粹的面向对象,使用函数式的方式是首选,特别是在创建对象实力的时候。
相比直接创建一个新的对象,工厂方法包装了一个新实例的创建,将对象的创建从实现中分离出来,从而更好也更灵活地控制对象的创建。
举个例子,当我们需要创建一个音频对象audio
的时候,使用关键字new
是最直接的方式:1
const audio = new Audio(name);
但是,这种创建方式会限定我们产生的对象,而工厂模式则提供了更多我们创建对象的选项,并且还不用暴露创建对象的构造函数,避免被继承或者修改:1
2
3
4
5
6
7
8
9function createAudio(name) {
if(name.match(/\.wav$/)) {
return new WavAudio(name);
} else if(name.match(/\.mp3$/)) {
return new Mp3Audio(name);
} else {
throw new Exception('没有该类型音频!');
}
}
使用闭包封装
很多人都迷惑为什么总会在笔试面试的时候被问到闭包的知识,因为大多数人对于闭包的使用第一反应可能是生成块作用域,然而即使在ES2015
引入块作用域之后,闭包依然还有很多方向的应用,比如在工厂模式中实现私有变量:
1 | function createAnimal(species) { |
这样就可以很好地将封装私有变量了,通过闭包可以让私有变量只能通过getter
和setter
来访问和修改。
使用工厂模式构建 Debugger
工厂模式有一个特别常用的场景,就是根据不同的环境,输出不同的对象,接下来,我们就自己构建一个简单的debugger
,可以在开发模式调试代码,并且在生产模式不输出调试结果:
1 | function createDebugger() { |
这样我们就能在不同的生产环境中初始化不同的对象,从而实现不一样的结果。
可组合的工厂函数
可组合的工厂函数 是一类特殊的工厂函数,它们可以被组合到一起来构建一个功能增强的工厂函数。当我们要创建从多个源继承一些行为和属性的对象,又不想构建复杂类结构的时候,就可以考虑组合工厂函数。
举个例子,我们每个人都有自己的name
、sex
和age
等一些基础属性,然后在我们中有着不同的职业:
- 歌手
- 画家
- 程序猿
再从另外一个维度看,又会有各种爱好者:
- 篮球
- 游戏
- 射击
这样就能组合出:
- 热爱篮球的歌手
- 热爱游戏的歌手
- 热爱射击的歌手
- 等等等等(还有可能喜欢多种项目或是从事多个职业)
所以就需要我们将这些抽象出来的对象组合起来:1
2
3
4
5
6
7
8
9
10
11function compose(objList=[]) {
const composedObj = {};
objList.map(obj => {
Object.keys(obj).map(key => {
composedObj[key] = obj[key];
});
});
return composedObj;
}
const shottingSinger = compose([new Singer, new Shotter]);
当然,这只是个示例,少了很多容错的处理,使用 组合工厂函数 推荐一个比较好的库 stampit:https://www.npmjs.com/package/stampit