解答問題前,瞭解什麼是工廠模式我以爲更重要些。 工廠模式其實也稱建立模式,是用於建立對象的一種方式。能夠說就是用來代替 new 實例化對象,決定了實例化哪個類,從而解決解耦問題。javascript
舉個例子:html
一個工廠接到一筆訂單(傳參),而後根據這個訂單類型(參數)來安排產品線(實例化哪一個類),固然客戶能夠要求一些產品的工藝屬性(抽象工廠)。這其中廠長(工廠模式)只負責調度,即安排產品零件流水線。你應該知道的是,工廠有個特色就是產出體量大、類似度高的產品。若是你要作單必定製化的產品,那這筆訂單給工廠就不適用了。java
new
關鍵字;不暴露建立對象的具體邏輯,而是將將邏輯封裝在一個函數中,那麼這個函數就能夠被視爲一個工廠。node
也能夠叫靜態工廠模式,用一個工廠對象建立同一類對象類的實例。現實生活中,用戶在平臺仍是分等級的,角色不一樣,權限也不一樣。jquery
1.ES5 實現git
// 0.0.2/es5.sample.factory.js
function Role(options){
this.role = options.role;
this.permissions = options.permissions;
}
Role.prototype.show = function (){
var str = '是一個' + this.role + ', 權限:' + this.permissions.join(', ');
console.log(str)
}
function sampleFactory(role){
switch(role) {
case 'admin':
return new Role({
role: '管理員',
permissions: ['設置', '刪除', '新增', '建立', '開發', '推送', '提問', '評論']
});
break;
case 'developer':
return new Role({
role: '開發者',
permissions: ['開發', '推送', '提問', '評論']
});
break;
default:
throw new Error('參數只能爲 admin 或 developer');
}
}
// 實例
const xm = sampleFactory('admin');
xm.show();
const xh = sampleFactory('developer');
xh.show();
const xl = sampleFactory('guest');
xl.show();
複製代碼
2.ES6 實現github
// 0.0.2/sample.factory.js
class SampleFactory {
constructor(opt) {
this.role = opt.role;
this.permissions = opt.permissions;
}
// 靜態方法
static create(role) {
switch (role) {
case 'admin':
return new SampleFactory({
role: '管理員',
permissions: ['設置', '刪除', '新增', '建立', '開發', '推送', '提問', '評論']
});
break;
case 'developer':
return new SampleFactory({
role: '開發者',
permissions: ['開發', '推送', '提問', '評論']
});
break;
default:
throw new Error('參數只能爲 admin 或 developer');
}
}
show() {
const str = `是一個${this.role}, 權限:${this.permissions.join(', ')}`;
console.log(str);
}
}
// 實例
const xm = SampleFactory.create('admin');
xm.show();
const xh = SampleFactory.create('developer');
xh.show();
const xl = SampleFactory.create('guest');
xl.show();
複製代碼
或編程
// 0.0.2/sample.factory1.js
class Role {
constructor(options) {
this.role = options.role;
this.permissions = options.permissions;
}
show() {
const str = `是一個${this.role}, 權限:${this.permissions.join(', ')}`;
console.log(str);
}
}
class SampleFactory {
constructor(role) {
this.role = role;
}
// 靜態方法
static create(role) {
switch (role) {
case 'admin':
return new Role({
role: '管理員',
permissions: ['設置', '刪除', '新增', '建立', '開發', '推送', '提問', '評論']
});
break;
case 'developer':
return new Role({
role: '開發者',
permissions: ['開發', '推送', '提問', '評論']
});
break;
default:
throw new Error('參數只能爲 admin 或 developer');
}
}
}
// 實例
const xm = SampleFactory.create('admin');
xm.show();
const xh = SampleFactory.create('developer');
xh.show();
const xl = SampleFactory.create('guest');
xl.show();
複製代碼
或設計模式
// 0.0.2/sample.factory2.js
class Role {
constructor(options) {
this.role = options.role;
this.permissions = options.permissions;
}
show() {
const str = `是一個${this.role}, 權限:${this.permissions.join(', ')}`;
console.log(str);
}
}
class SampleFactory {
constructor(role) {
if(typeof this[role] !== 'function') {
throw new Error('參數只能爲 admin 或 developer');
}
return this[role]();
}
admin() {
return new Role({
role: '管理員',
permissions: ['設置', '刪除', '新增', '建立', '開發', '推送', '提問', '評論']
});
}
developer() {
return new Role({
role: '開發者',
permissions: ['開發', '推送', '提問', '評論']
});
}
}
// 實例
const xm = new SampleFactory('admin');
xm.show();
const xh = new SampleFactory('developer');
xh.show();
const xl = new SampleFactory('guest');
xl.show();
複製代碼
上例中,sampleFactory
就是一個簡單工廠,2個實例對應不一樣的權限,調用工廠函數時,只需傳遞 admin
或 developer
就可獲取對應的實例對象。緩存
1.簡單工廠函數適用場景
2.簡單工廠函數不適用場景
create
內包含了全部建立對象(構造函數)的判斷邏輯代碼,若是要增長新的構造函數還須要修改函數 create
(判斷邏輯代碼),當可選參數 role
變得更多時,那函數 create
的判斷邏輯代碼就變得臃腫起來,難以維護。將實際建立對象工做推遲到子類當中,核心類就成了抽象類。這樣添加新的類時就無需修改工廠方法,只須要將子類註冊進工廠方法的原型對象中便可。
1.安全模式類,能夠屏蔽使用類的錯誤形成的錯誤
// 0.0.2/secure.function.factory.js
function Factory(){
if(!(this instanceof Factory)) {
return new Factory();
}
}
Factory.prototype.show = function(){
console.log('factory show');
}
var f = new Factory();
f.show();
複製代碼
2.ES5 實現,ES5 沒有像傳統建立類的方式那樣建立抽象類,因此工廠方法模式只需參考其核心思想便可。可將工廠方法看作一個實例化對象工廠類(採用安全模式類),將建立對象的基類放在工廠方法類的原型中便可。當須要添加新類時,只需掛載在 FunctionFactory.prototype
上,無需修改工廠方法,也實現了 OCP 原則。
// 0.0.2/es5.function.factory.js
function FunctionFactory(role) {
if(!(['admin', 'developer'].indexOf(role) > -1)){
throw new Error('參數只能爲 admin 或 developer');
}
// 安全的工廠方法
if (this instanceof FunctionFactory) {
return this[role]();
}
return new FunctionFactory(role);
}
FunctionFactory.prototype.show = function () {
var str = '是一個' + this.role + ', 權限:' + this.permissions.join(', ');
console.log(str)
}
FunctionFactory.prototype.admin = function (permissions) {
this.role = '管理員';
this.permissions = ['設置', '刪除', '新增', '建立', '開發', '推送', '提問', '評論'];
}
FunctionFactory.prototype.developer = function (permissions) {
this.role = '開發者';
this.permissions = ['開發', '推送', '提問', '評論'];
}
var xm = FunctionFactory('admin');
xm.show();
var xh = new FunctionFactory('developer');
xh.show();
var xl = new FunctionFactory('guest');
xl.show();
複製代碼
3.ES6 實現,因爲 ES6 中尚未 abstract
,就用 new.target
來模擬出抽象類(new.target
指向被 new
執行的構造函數),判斷 new.target
是否指向了抽象類,若是是就報錯。
// 0.0.2/function.factory.js
class FunctionFactoryBase { // 抽象類
constructor(role) {
if (new.target === FunctionFactoryBase) {
throw new Error('抽象類不能實例');
}
this.role = role;
}
}
class FunctionFactory extends FunctionFactoryBase { // 子類
constructor(role) {
super(role);
}
static create(role) {
switch (role) {
case 'admin':
return new FunctionFactory({
role: '管理員',
permissions: ['設置', '刪除', '新增', '建立', '開發', '推送', '提問', '評論']
});
break;
case 'developer':
return new FunctionFactory({
role: '開發者',
permissions: ['開發', '推送', '提問', '評論']
});
break;
default:
throw new Error('參數只能爲 admin 或 developer');
}
}
show() {
const { role, permissions } = this.role;
const str = `是一個${role}, 權限:${permissions.join(', ')}`;
console.log(str)
}
}
// let xl = new FunctionFactoryBase(); // 此行會報錯,註釋後方可正常執行後面
let xm = FunctionFactory.create('admin');
xm.show()
let xh = FunctionFactory.create('developer');
xh.show()
let xl = FunctionFactory.create('guest');
xl.show()
複製代碼
抽象工廠只留對外的口子,不作事,留給外界覆蓋(子類重寫接口方法以便建立的時候指定本身的對象類型)。主要用於對產品類簇的建立,不直接生成實例(簡單工廠模式和工廠方法模式都是生成實例)。
// 0.0.2/abstract.factory2.js
// 抽象工廠
function AbstractFactory(subType, superType) {
if (typeof AbstractFactory[superType] === 'function') {
//緩存類
function F() { }
//繼承父類屬性和方法
F.prototype = new AbstractFactory[superType]();
//將子類 constructor 指向子類(本身)
subType.prototype.constructor = subType;
//子類原型繼承緩存類(父類)
subType.prototype = new F();
} else {
//不存在該抽象類拋出錯誤
throw new Error('抽象類不存在')
}
}
// 抽象類
AbstractFactory.Phone = function () {
this.type = 'Phone';
}
AbstractFactory.Phone.prototype = {
showType: function () {
return new Error('Phone 抽象方法 showType 不能調用');
},
showPrice: function () {
return new Error('Phone 抽象方法 showPrice 不能調用');
},
showColor: function () {
return new Error('Phone 抽象方法 showColor 不能調用');
}
}
AbstractFactory.Pad = function () {
this.type = 'Pad';
}
AbstractFactory.Pad.prototype = {
showType: function () {
return new Error('Pad 抽象方法 showType 不能調用');
},
showPrice: function () {
return new Error('Pad 抽象方法 showPrice 不能調用');
},
showColor: function () {
return new Error('Pad 抽象方法 showColor 不能調用');
}
}
// 抽象工廠實現對抽象類的繼承
function Iphone(type, price, color) {
this.type = type;
this.price = price;
this.color = color;
}
//抽象工廠實現對 Phone 抽象類的繼承
AbstractFactory(Iphone, 'Phone');
Iphone.prototype.showType = function () {
return this.type;
}
Iphone.prototype.showPrice = function () {
return this.price;
}
Iphone.prototype.showColor = function () {
return this.color;
}
function Ipad(type, price, color) {
this.type = type;
this.price = price;
this.color = color;
}
AbstractFactory(Ipad, 'Pad');
Ipad.prototype.showType = function () {
return this.type;
}
Ipad.prototype.showPrice = function () {
return this.price;
}
Ipad.prototype.showColor = function () {
return this.color;
}
// 實例
var iphone5s = new Iphone('iphone 5s', 3000, '白色');
console.log('今天剛買了' + iphone5s.showType() + ',價格是' + iphone5s.showPrice() + ',' + iphone5s.showColor())
var iphone8s = new Iphone('iphone 8s', 8000, '白色');
console.log('今天剛買了' + iphone8s.showType() + ',價格是' + iphone8s.showPrice() + ',' + iphone8s.showColor())
var ipad = new Ipad('ipad air', 2000, '騷紅色');
console.log('今天剛買了' + ipad.showType() + ',價格是' + ipad.showPrice() + ',' + ipad.showColor())
複製代碼
1.jQuery源碼-工廠模式
// 0.0.2/jquery.factory.js
// 工廠模式
class jQuery {
constructor(selector) {
let slice = Array.prototype.slice;
let dom = slice.call(document.querySelectorAll(selector));
let len = dom ? dom.length : 0;
for (let i = 0; i < len; i++) {
this[i] = dom[i];
}
this.length = len
this.selector = selector || ''
}
addClass(name) {
console.log(name)
}
html(data) {
}
// 省略多個 API
}
// 工廠模式
window.$ = function(selector) {
return new jQuery(selector);
}
// 實例
const $li = $('li')
$li.addClass('item');
複製代碼
2.React.createElement
實現
// jsx
var profile = (
<div> <img src='https://raw.githubusercontent.com/ruizhengyun/images/master/cover/ruizhengyun.cn_.png' className="profile" /> <h3>{[user.firstName, user.lastName].join(' ')}</h3> </div> ); // 實現 var profile = React.createElement('div', null, React.createElement('img', { src: 'https://raw.githubusercontent.com/ruizhengyun/images/master/cover/ruizhengyun.cn_.png', className: 'profile' }), React.createElement('h3', null, [user.firstName, user.lastName].join(' ')) ); // 源碼 class Vnode(tag, attrs, children) { // ... } React.createElement = function(tag, attrs, children) { return new Vnode(tag, attrs, children); } 複製代碼