工厂模式

1. 简单工厂

简单工厂:使用一个类或对象封装实例化操作

假如我们有个自行车商店类 BicycleShop,它提供了销售自行车的方法可以选择销售两类自行车 Speedster,ComfortCruiser,那么确定要销售那种自行车就可以通过简单工厂来实现。

注意:后面的代码中会用到 Javascript设计模式(1) 里面讲到的接口类 Interface 和 继承函数 extend

var Speedster = function() {}
Speedster.prototype = {
assemble: function() {}
} var ComfortCruiser = function() {}
ComfortCruiser.prototype = {
assemble: function() {}
} // 简单工厂模式
var BicycleFactory = {
createBicycle: function(model) {
var bicycle;
switch(model) {
case 'The Speedster':
bicycle = new Speedster();
break;
case 'The Comfort Cruiser':
default:
bicycle = new ComfortCruiser()
}
//检查是否实现了相应父类的方法
// Interface.ensureImplement(bicycle, Bicycle)
return bicycle
}
} var BicycleShop = function() {};
BicycleShop.prototype = {
sellBicycle: function(model) {
var bicycle = BicycleFactory.createBicycle(model); // 通过简单工厂来实例化成员对象
bicycle.assemble()
return bicycle
}
} // usage
var shop = new BicycleShop()
var b1 = shop.sellBicycle('aaaa') // 可提供车型的所有信息都集中在一个地方管理

但是这里有个问题,就是所有的 BicycleShop 实例是能销售简单工厂对象里面的自行车,而现实生活中,不同的自行车商店可能会销售不同的自行车,而且这样实现会使得 BicycleFactory 这个简单工厂对象里面的 createBicycle 方法太大,试想像一下,全世界有多少种自信车,这里就会有多少个 case 分支。所以像这种销售自行车的场景,工厂模式会更适用

2. 工厂模式

这里的工厂指的是一个将其成员对象的实例化推迟到子类中进行的类

如果在不同时间或环境中实例的成员变量可能是不同实例时,我们一般就会用到工厂模式来处理这个实例化操作,一般会在类中会指定用以实例化成员变量的工厂方法 createXXX 供类的其他方法调用,所以这些方法就能通过统一的接口获取一个拥有已知接口的对象而不需要关系它是什么实现的,然后在该类的实例中会具体实现这个方法。

var AcmeSpeedster = function() {}
AcmeSpeedster.prototype = {
assemble: function() {}
}
var AcmecomfortCruiser = function() {}
AcmecomfortCruiser.prototype = {
assemble: function() {}
} // BicycleShop 就是一个工厂,它是一个抽象类,需要在继承后实现里面用于实例化的工厂方法
var BicycleShop = function() {};
BicycleShop.prototype = {
sellBicycle: function(model) {
var bicycle = this.createBicycle(model)
bicycle.assemble()
return bicycle;
},
// 工厂方法
createBicycle: function(model) {
throw new Error('Unsupported operation on an abstract class.')
}
} var AcmeBicycleShop = function() {};
extend(AcmeBicycleShop, BicycleShop);
AcmeBicycleShop.prototype.createBicycle = function(model) {
var bicycle;
switch(model) {
case 'The Speedster':
bicycle = new AcmeSpeedster();
break;
case 'The Comfort Cruiser':
default:
bicycle = new AcmecomfortCruiser();
}
// Interface.ensureImplement(bicycle, Bicycle)
return bicycle
} // usage
var ashop = new AcmeBicycleShop()
ashop.sellBicycle() // 对 Bycycle 进行一般性操作的代码可以全部写在父类 BicycleShop 中,而具体的 Bicycle 对象进行实例化的工作则别留到子类中。

3. 示例:XHR工厂

下面使用工厂模式封装了一个简单的 Ajax API,它可以兼容各大浏览器获取 xhr 对象,并提供了发送请求的 require 方法

var SimpleHandle = function() {};
SimpleHandle.prototype = {
require: function(method, url, callback, postVars) {
var xhr = this.createXhrObject();
xhr.onreadystatechange = function() {
if(xhr.readyState !== 4) return;
(xhr.status === 200) ?
callback.success(xhr.responseText, xhr.responseXML):
callback.failure(xhr.status);
}
xhr.open(method, url, true);
if(method != 'POST') postVars = null;
xhr.send(postVars)
},
createXhrObject: function() {
var methods = [
function() { return new XMLHttpRequest() },
function() { return new ActiveXObject('Msxml2.XMLHTTP') },
function() { return new ActiveXObject('Microsoft.XMLHTTP') }
];
for(var i=0, len=methods.length; i<len; i++) {
try {
methods[i]();
} catch(err) {
continue;
} // memoizing : 返回所创建的对象并将自身改为用以创建那个对象的函数
this.createXhrObject = methods[i];
return methods[i]()
} throw new Error('SimpleHandle: Could not create an XHR object.')
}
} // usage
var myHandler = new SimpleHandle();
var callback = {
success: function(responseText) {alert('Success: ' + responseText)},
failure: function(statusCode) {alert('Failure: ' + statusCode)}
}
myHandler.require('POST', 'http://www.baidu.com', callback)

4. 示例 :RSS阅读器

这个例子中会用到上面的 XHR工厂 例子中实现的 Ajax API 来实现一个 RSS 阅读器。

// 这里用到了工厂模式
var SimpleHandle = function() {};
SimpleHandle.prototype = {
require: function(method, url, callback, postVars) {
var xhr = this.createXhrObject();
xhr.onreadystatechange = function() {
if(xhr.readyState !== 4) return;
(xhr.status === 200) ?
callback.success(xhr.responseText, xhr.responseXML):
callback.failure(xhr.status);
}
xhr.open(method, url, true);
if(method != 'POST') postVars = null;
xhr.send(postVars)
},
createXhrObject: function() {
var methods = [
function() { return new XMLHttpRequest() },
function() { return new ActiveXObject('Msxml2.XMLHTTP') },
function() { return new ActiveXObject('Microsoft.XMLHTTP') }
];
for(var i=0, len=methods.length; i<len; i++) {
try {
methods[i]();
} catch(err) {
continue;
} // memoizing : 返回所创建的对象并将自身改为用以创建那个对象的函数
this.createXhrObject = methods[i]
return methods[i]()
} throw new Error('SimpleHandle: Could not create an XHR object.')
}
} // 这里是一个呈现数据的类
var ListDisplay = function(id, parent) {
this.list = document.createElement('ul');
this.list.id = id;
parent.appendChild(this.list)
}
ListDisplay.prototype = {
append: function(text) {
var newEl = document.createElement('li');
this.list.appendChild(newEl);
newEl.innerHTML = text;
return newEl
},
remove: function(el) {
this.list.removeChild(el);
},
clear: function() {
this.list.innerHTML = '';
}
} var FeedReader = function(display, xhrHandler, conf) {
this.display = display;
this.xhrHandler = xhrHandler;
this.conf = conf; this.startUpdates();
}
FeedReader.prototype = {
fetchFeed: function() {
var that = this;
var callback = {
success: function(text, xml) {
that.parseFeed(text, xml)
},
failure: function(status) {
that.showError(status)
}
}
this.xhrHandler.require('GET', this.conf.feedUrl, callback);
},
parseFeed: function(responseText, responseXML) {
this.display.clear()
var items = responseXML.getElementsByTagName('item');
for(var i=0, len=items.length; i<len; i++) {
var title = items[i].getElementsByTagName('title')[0];
var link = items[i].getElementsByTagName('link')[0];
this.display.append('<a href="' + link.firstChild.data + '">'+ title.firstChild.data + '</a>' )
}
},
showError: function(statusCode) {
this.display.clear();
this.display.append('Error fetching feed.')
},
stopUpdates: function() {
clearInterval(this.interval);
},
startUpdates: function() {
this.fetchFeed();
var that = this;
this.interval = setInterval(function() {
that.fetchFeed();
}, this.conf.updateInterval * 1000)
}
} // 这里用到了前面讲过的单利对象,里面有一个 createFeedReader 方法用来将多个小对象组成一个大对象,也是一种工厂模式的使用
var FeedManager = {
createFeedReader: function(conf) {
var displayModule = new ListDisplay(conf.id + '-display', conf.parent)
// Interface.ensureImplements(displayModule, DisplayModule) var xhrHandler = new SimpleHandle()
// Interface.ensureImplements(xhrHandler, AjaxHandler) return new FeedReader(displayModule, xhrHandler, conf)
}
} var conf = {
id: 'cnn-top-stories',
feedUrl: 'https://laike9m.com/blog/rss/', //http://www.adaymag.com/feed/',
updateInterval: 60,
parent: document.getElementById('feed-readers')
} // usage
FeedManager.createFeedReader(conf)

5. 工厂模式的适用场合

  • 动态实现

    • 创建一些用不同方法实现统一接口的对象(如上面自行车例子)
    • 可明确的实现统一接口【自行车】,也可隐含的实现(根据网速等环境原因) 【XHR 工厂】
  • 节省设置开销
    • 把实例化对象时重复的操作集中在一个地方
  • 用许多小型对象组成一个大对象 【FeedManager】

6. 工厂模式之弊

  • 如果不会另外更换一个类,或者不需要在运行期间在一系列可互换的类中进行选择,那就不应该使用工厂方法
  • 使用 new 实例化比通过工厂模式实例化更简单易懂
  • 切勿滥用,如果拿不定主意,那就不要用,因为在重构代码的时候还有机会使用

注意

转载、引用,但请标明作者和原文地址

JavaScript设计模式(3)-工厂模式的更多相关文章

  1. JavaScript设计模式之工厂模式

    一.工厂模式概念 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口方法以便创建的时候指定自己的对象类型(抽象工厂). 这个模 ...

  2. JavaScript设计模式--简单工厂模式

    一,介绍 工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类. 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口 ...

  3. javaScript设计模式之----工厂模式

    什么是工厂模式?我们通过一个例子了解一下: 比如我们想要弹出几个字符串 function funA(){ alert('a'); } function funB(){ alert('b'); } fu ...

  4. JavaScript设计模式-10.工厂模式实例xhr

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. 【javascript】javascript设计模式之工厂模式

    1.要解决的问题 2.如何实现 3.与构造函数的区别 4.总结 1.要解决的问题 工厂模式通常用于重复创建相似对象,提供动态创建对象的接口. 2.工厂模式最为设计模式中构造模式之一,通常在类或类的静态 ...

  6. javascript设计模式-抽象工厂模式

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. JavaScript设计模式-9.工厂模式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. JavaScript设计模式--简单工厂模式例子---XHR工厂

    第一步,Ajax操作接口(目的是起一个接口检测作用) (1)引入接口文件 //定义一个静态方法来实现接口与实现类的直接检验 //静态方法不要写出Interface.prototype ,因为这是写到接 ...

  9. JavaScript 设计模式之工厂模式

随机推荐

  1. QT使用painter绘制文字时的居中显示

    在窗体上绘制文字时,在paintEvent()方法里用QPainter进行绘制. 主要获取对字符串打印在屏幕上时占用的像素大小 QPainter p(this); QFont font("宋 ...

  2. 从Vue.js源码角度再看数据绑定

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/an ...

  3. mysql cp复制和mysqldump备份测试

    本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 备份策略 针对不同的场景下, 我们应该制定不同的备份策略对数据库进行 ...

  4. 【学习笔记】Spring JdbcTemplate (3-3-3)

    Spring与JDBC模板(jdbcTemplate) 为了避免直接使用JDBC而带来的复杂冗长的代码 Spring提供的一个强有力的模板类 -- jdbcTemplate简化JDBC操作 并且数据源 ...

  5. PHP对象和接口抽象类注意事项

    Php 的对象的实现: 注:对象名称最好以对象名.class.php来进行命名. 1. 对于静态类的成员不能使用$this->这个来调用其静态类的成员和函数,应该使用self::成员或者方法来进 ...

  6. OSI网络模型

    OSI中的层 功能 TCP/IP协议族 应用层         文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层         数 ...

  7. 老男孩Python全栈开发(92天全)视频教程 自学笔记09

    day9课程内容: 乌班图(ubuntu)64位系统 和 VMware 虚拟机安装(官网收费又麻烦,在网上找资源 安装vmware: vm运行(秘钥找度娘)--文件--新建虚拟机--自定义 下一步-- ...

  8. 计蒜客 数字解码 dp

    思路:dp(i)表示前i个字符的解码方案种数.进行状态转移时需要仔细思考,分情况讨论: 设第i个字符和第i-1个字符组成的数为x. 1.如果x根本不可能出现说明不是合理的编码,直接使dp(i)为0,例 ...

  9. uva12325 暴力枚举

    这题刚开始我就贪心,直接wrong了,贪心适合可以取一个物体部分的题. 还是老实枚举吧,注意枚举要分类,不然可能会超时,还有注意答案是long long AC代码: #include<cstdi ...

  10. 在SpringBoot中配置aop

    前言 aop作为spring的一个强大的功能经常被使用,aop的应用场景有很多,但是实际的应用还是需要根据实际的业务来进行实现.这里就以打印日志作为例子,在SpringBoot中配置aop 已经加入我 ...