設(shè)計模式分類(23種設(shè)計模式)
- 創(chuàng)建型
- 單例模式
- 原型模式
- 工廠模式
- 抽象工廠模式
- 建造者模式
- 結(jié)構(gòu)型
- 適配器模式
- 裝飾器模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 行為型
- 觀察者模式
- 迭代器模式
- 策略模式
- 模板方法模式
- 職責(zé)鏈模式
- 命令模式
- 備忘錄模式
- 狀態(tài)模式
- 訪問者模式
- 中介者模式
- 解釋器模式
工廠模式
什么是工廠模式朱浴?一個工廠接到一筆訂單(傳參)邻薯,然后根據(jù)這個訂單類型(參數(shù))來安排產(chǎn)品線(實例化哪個類)世曾,當(dāng)然客戶可以要求一些產(chǎn)品的工藝屬性(抽象工廠)。這其中廠長(工廠模式)只負責(zé)調(diào)度,即安排產(chǎn)品零件流水線。你應(yīng)該知道的是洁墙,工廠有個特點就是產(chǎn)出體量大、相似度高的產(chǎn)品戒财。如果你要做單一定制化的產(chǎn)品热监,那這筆訂單給工廠就不適用了。
舉個例子:
- 編程中饮寞,在一個 A 類中通過 new 的方式實例化了類 B孝扛,那么 A 類和 B 類之間就存在關(guān)聯(lián)(耦合);
- 后期因為需要修改了 B 類的代碼和使用方式列吼,比如構(gòu)造函數(shù)中傳入?yún)?shù),那么 A 類也要跟著修改苦始,一個類的依賴可能影響不大寞钥,但若有多個類依賴了 B 類,那么這個工作量將會相當(dāng)?shù)拇竽把。菀壮霈F(xiàn)修改錯誤理郑,也會產(chǎn)生很多的重復(fù)代碼,這無疑是件非常痛苦的事咨油;
- 這種情況下您炉,就需要將創(chuàng)建實例的工作從調(diào)用方(A類)中分離,與調(diào)用方解耦役电,也就是使用工廠方法創(chuàng)建實例的工作封裝起來(減少代碼重復(fù))赚爵,由工廠管理對象的創(chuàng)建邏輯,調(diào)用方不需要知道具體的創(chuàng)建過程宴霸,只管使用囱晴,而降低調(diào)用者因為創(chuàng)建邏輯導(dǎo)致的錯誤;
使用場景
- 處理大量具有相同屬性的小對象瓢谢;
- 對象的構(gòu)建十分復(fù)雜畸写,需要依賴具體環(huán)境創(chuàng)建不同實例;
工廠模式的三種實現(xiàn)方法: 簡單工廠模式
氓扛、工廠方法模式
枯芬、抽象工廠模式
。
1. 簡單工廠模式
簡單工廠模式
又叫靜態(tài)工廠模式
采郎,由一個工廠對象決定創(chuàng)建某一種產(chǎn)品對象類的實例千所。主要用來創(chuàng)建同一類對象。
//User類
class User {
//構(gòu)造器
constructor(opt) {
this.name = opt.name;
this.viewPage = opt.viewPage;
}
//靜態(tài)方法
static getInstance(role) {
switch (role) {
case 'superAdmin':
return new User({ name: '超級管理員', viewPage: ['首頁', '通訊錄', '發(fā)現(xiàn)頁', '應(yīng)用數(shù)據(jù)', '權(quán)限管理'] });
break;
case 'admin':
return new User({ name: '管理員', viewPage: ['首頁', '通訊錄', '發(fā)現(xiàn)頁', '應(yīng)用數(shù)據(jù)'] });
break;
case 'user':
return new User({ name: '普通用戶', viewPage: ['首頁', '通訊錄', '發(fā)現(xiàn)頁'] });
break;
default:
throw new Error('參數(shù)錯誤, 可選參數(shù):superAdmin蒜埋、admin淫痰、user')
}
}
}
//調(diào)用
let superAdmin = User.getInstance('superAdmin');
let admin = User.getInstance('admin');
let normalUser = User.getInstance('user');
User就是一個簡單工廠,在該函數(shù)中有3個實例中分別對應(yīng)不同的權(quán)限的用戶整份。當(dāng)我們調(diào)用工廠函數(shù)時待错,只需要傳遞superAdmin, admin, user這三個可選參數(shù)中的一個獲取對應(yīng)的實例對象。
簡單工廠的優(yōu)點在于烈评,你只需要一個正確的參數(shù)火俄,就可以獲取到你所需要的對象,而無需知道其創(chuàng)建的具體細節(jié)讲冠。但是在函數(shù)內(nèi)包含了所有對象的創(chuàng)建邏輯(構(gòu)造函數(shù))和判斷邏輯的代碼瓜客,每增加新的構(gòu)造函數(shù)還需要修改判斷邏輯代碼。當(dāng)我們的對象不是上面的3個而是30個或更多時,這個函數(shù)會成為一個龐大的超級函數(shù)谱仪,便得難以維護玻熙。所以,簡單工廠只能作用于創(chuàng)建的對象數(shù)量較少芽卿,對象的創(chuàng)建邏輯不復(fù)雜時使用揭芍。
2. 工廠方法模式
工廠方法模式的本意是將實際創(chuàng)建對象的工作推遲到子類中,這樣核心類就變成了抽象類卸例。這樣添加新的類時就無需修改工廠方法称杨,只需要將子類注冊進工廠方法的原型對象中即可。
class User {
constructor(name = '', viewPage = []) {
if(new.target === User) {
throw new Error('抽象類不能實例化!');
}
this.name = name;
this.viewPage = viewPage;
}
}
class UserFactory extends User {
constructor(name, viewPage) {
super(name, viewPage)
}
create(role) {
switch (role) {
case 'superAdmin':
return new UserFactory( '超級管理員', ['首頁', '通訊錄', '發(fā)現(xiàn)頁', '應(yīng)用數(shù)據(jù)', '權(quán)限管理'] );
break;
case 'admin':
return new UserFactory( '普通用戶', ['首頁', '通訊錄', '發(fā)現(xiàn)頁'] );
break;
case 'user':
return new UserFactory( '普通用戶', ['首頁', '通訊錄', '發(fā)現(xiàn)頁'] );
break;
default:
throw new Error('參數(shù)錯誤, 可選參數(shù):superAdmin筷转、admin姑原、user')
}
}
}
let userFactory = new UserFactory();
let superAdmin = userFactory.create('superAdmin');
let admin = userFactory.create('admin');
let user = userFactory.create('user');
3. 工廠方法模式
抽象工廠只留對外的口子,不做事呜舒,留給外界覆蓋(子類重寫接口方法以便創(chuàng)建的時候指定自己的對象類型)锭汛。主要用于對產(chǎn)品類簇的創(chuàng)建,不直接生成實例(簡單工廠模式和工廠方法模式都是生成實例)袭蝗。
- 抽象類是一種聲明但不能使用的類唤殴,子類必須先實現(xiàn)其方法才能調(diào)用;
- 可以在抽象類中定義一套規(guī)范,供子類去繼承實現(xiàn);
// 抽象工廠
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 不能調(diào)用');
},
showPrice: function () {
return new Error('Phone 抽象方法 showPrice 不能調(diào)用');
},
showColor: function () {
return new Error('Phone 抽象方法 showColor 不能調(diào)用');
}
}
AbstractFactory.Pad = function () {
this.type = 'Pad';
}
AbstractFactory.Pad.prototype = {
showType: function () {
return new Error('Pad 抽象方法 showType 不能調(diào)用');
},
showPrice: function () {
return new Error('Pad 抽象方法 showPrice 不能調(diào)用');
},
showColor: function () {
return new Error('Pad 抽象方法 showColor 不能調(diào)用');
}
}
// 抽象工廠實現(xiàn)對抽象類的繼承
function Iphone(type, price, color) {
this.type = type;
this.price = price;
this.color = color;
}
//抽象工廠實現(xiàn)對 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())
單例模式
一個類只有一個實例晋辆,并提供一個訪問它的全局訪問點渠脉。
詳細可參考:https://segmentfault.com/a/1190000022831974
- 利用閉包實現(xiàn)單例模式
function Singleton(name) {
this.name = name
}
var proxySingle = (function(){
var instance
return function(name) {
if(!instance) {
instance = new Singleton(name)
}
return instance
}
})()
適配器模式
將一個類(對象)的接口(方法或?qū)傩裕┺D(zhuǎn)化成客戶希望的另外一個接口(方法或?qū)傩裕沟迷居捎诮涌诓患嫒荻荒芤黄鸸ぷ鞯哪切╊悾▽ο螅┛梢哉f(xié)作瓶佳。簡單理解就是為兼容而生的 “轉(zhuǎn)換器”芋膘。
class Plug {
getName() {
return 'iphone充電頭';
}
}
class Target {
constructor() {
this.plug = new Plug();
}
getName() {
return this.plug.getName() + ' 適配器Type-c充電頭';
}
}
let target = new Target();
target.getName(); // iphone充電頭 適配器轉(zhuǎn)Type-c充電頭
觀察者模式
定義了一種一對多的關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象霸饲,這個主題對象的狀態(tài)發(fā)生變化時就會通知所有的觀察者對象索赏,使它們能夠自動更新自己,當(dāng)一個對象的改變需要同時改變其它對象贴彼,并且它不知道具體有多少對象需要改變的時候,就應(yīng)該考慮使用觀察者模式埃儿。
// 主題 保存狀態(tài)器仗,狀態(tài)變化之后觸發(fā)所有觀察者對象
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyAllObservers()
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
attach(observer) {
this.observers.push(observer)
}
}
// 觀察者
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}
// 測試
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('02', s)
s.setState(12)