最近學習設計模式和TypeScript,發現網上的資料略顯硬核,不太容易理解記憶,常常看完就忘。做爲一名遊戲玩家,發現遊戲中的不少場景都能和相應的設計模式相關聯,不只便於理解,更利於合理地運用設計模式。因爲我的水平有限,只整理我的以爲比較有趣的設計模式,每一個模式採用哲學三問進行講解。若是對你有幫助的話,歡迎點贊和收藏💖,圖片源自網絡,侵刪。node
單例模式git
Whatgithub
Game:在DNF一塊兒組團刷本的日子中,副本BOSS看做爲一個單例,玩家能夠經過各類技能或者平A去消耗BOSS。算法
而且該本中的全部玩家都是對同一個BOSS形成傷害設計模式
How網絡
TS版本dom
class Boss{ private static instance: Boss = null; private hp: number = 1000; getInjured(harm: number){ this.hp -= harm; } getHp(): number{ return this.hp; } static getInstance(): Boss{ // 若是已經實例化了那麼直接返回 if(!this.instance){ this.instance = new Boss(); } return this.instance; } } class Player{ constructor(private name: string){} attack(harm: number,boss: Boss): void{ boss.getInjured(harm); console.log(`我是一名${this.name},打出了${harm}傷害,BOSS還剩${boss.getHp()}血`) } } let p1: Player = new Player('鬼泣'); let p2: Player = new Player('街霸'); let p3: Player = new Player('阿修羅'); // 對同一個boss瘋狂輸出 p1.attack(100,Boss.getInstance()); p2.attack(80,Boss.getInstance()); p3.attack(120,Boss.getInstance()); // 我是一名鬼泣,打出了100傷害,BOSS還剩900血 // 我是一名街霸,打出了80傷害,BOSS還剩820血 // 我是一名阿修羅,打出了120傷害,BOSS還剩700血
JS版本性能
var Boss = /** @class */ (function () { function Boss() { this.hp = 1000; } Boss.prototype.getInjured = function (harm) { this.hp -= harm; }; Boss.prototype.getHp = function () { return this.hp; }; Boss.getInstance = function () { // 若是已經實例化了那麼直接返回 if (!this.instance) { this.instance = new Boss(); } return this.instance; }; Boss.instance = null; return Boss; }()); var Player = /** @class */ (function () { function Player(name) { this.name = name; } Player.prototype.attack = function (harm, boss) { boss.getInjured(harm); console.log("我是一名" + this.name + ",打出了" + harm + "傷害,BOSS還剩" + boss.getHp() + "血"); }; return Player; }()); var p1 = new Player('鬼泣'); var p2 = new Player('街霸'); var p3 = new Player('阿修羅'); // 對同一個boss瘋狂輸出 p1.attack(100, Boss.getInstance()); p2.attack(80, Boss.getInstance()); p3.attack(120, Boss.getInstance()); // 我是一名鬼泣,打出了100傷害,BOSS還剩900血 // 我是一名街霸,打出了80傷害,BOSS還剩820血 // 我是一名阿修羅,打出了120傷害,BOSS還剩700血
Why單元測試
優勢學習
單例模式提供了對共享資源訪問的惟一接口,避免了對共享資源的多重佔用
單例模式只會實例化一次對象,節約內存開銷
缺點
GC
自動回收應用場景:針對一些靜態的共享資源,能夠採用單例模式對其進行訪問。今後處能夠看出舉的遊戲例子自己不太適合用單例模式(由於它是一個頻繁變更的對象)
策略模式
What
Game:在爐石傳說遊戲中,盜賊的兩套經典卡牌奇蹟賊和爆牌賊,雖然都是盜賊玩家取勝的法寶,但這兩套牌的取勝之道卻大相徑庭。奇蹟賊依靠一回合的極限操做每每能轉危爲安,讓對面忽然去世;而爆牌賊則是以疲勞和爆對面key牌的運營方式取勝。
How
TS版本
interface Deck{ name: string; kill(): void; } class MiracleDeck implements Deck{ name: string = '奇蹟賊'; kill(){ console.log(`${this.name}:十七張牌就是能秒你`); } } class ExplosiveDeck implements Deck{ name: string = '爆牌賊'; kill(){ console.log(`${this.name}:我要爆光你的牌庫!`) } } class Robber{ private deck: Deck; setDeck(deck: Deck){ this.deck = deck; } winTheGame(): void{ this.deck.kill(); }; } let rb = new Robber(); rb.setDeck(new MiracleDeck()); rb.winTheGame(); rb.setDeck(new ExplosiveDeck()); rb.winTheGame(); // 奇蹟賊:十七張牌就是能秒你 // 爆牌賊:我要爆光你的牌庫!
JS版本
var MiracleDeck = /** @class */ (function () { function MiracleDeck() { this.name = '奇蹟賊'; } MiracleDeck.prototype.kill = function () { console.log(this.name + "十七張牌就是能秒你"); }; return MiracleDeck; }()); var ExplosiveDeck = /** @class */ (function () { function ExplosiveDeck() { this.name = '爆牌賊'; } ExplosiveDeck.prototype.kill = function () { console.log(this.name + "我要爆光你的牌庫!"); }; return ExplosiveDeck; }()); var Robber = /** @class */ (function () { function Robber() { } Robber.prototype.setDeck = function (deck) { this.deck = deck; }; Robber.prototype.winTheGame = function () { this.deck.kill(); }; ; return Robber; }()); var rb = new Robber(); rb.setDeck(new MiracleDeck()); rb.winTheGame(); rb.setDeck(new ExplosiveDeck()); rb.winTheGame(); // 奇蹟賊:十七張牌就是能秒你 // 爆牌賊:我要爆光你的牌庫!
Why
優勢
算法能夠自由切換,可擴展性好
缺點
策略類會增多而且每一個策略類都須要向外暴露
應用場景:多態場景,如分享功能,分享到不一樣平臺的內部實現是不相同的
代理模式
What
定義:爲一個對象提供一個代理者,以便控制對它的訪問
解釋:好比明星和經紀人的關係,經紀人會幫助明星處理商演贊助等細節問題,明星負責簽字就好
Game:做爲一個FM
懶人玩家,只想把時間花在看模擬比賽上,其餘不想作的事就很開心地甩給助理教練啦
How
TS版本
interface Match{ play(): void; } class Manager implements Match{ play(): void{ console.log('比賽開始了,是由皇家馬德里對陣拜仁慕尼黑。。。') } } class Cotch implements Match{ private manager: Manager = new Manager(); beforePlay(): void{ console.log('佈置戰術'); console.log('球隊訓話'); } afterPlay(): void{ console.log('賽後採訪'); } play(): void{ this.beforePlay(); this.manager.play(); this.afterPlay(); } } let proxy: Cotch = new Cotch(); proxy.play(); // 佈置戰術 // 球隊訓話 // 比賽開始了,是由皇家馬德里對陣拜仁慕尼黑。。。 // 賽後採訪
JS版本
var Manager = /** @class */ (function () { function Manager() { } Manager.prototype.play = function () { console.log('比賽開始了,是由皇家馬德里對陣拜仁慕尼黑。。。'); }; return Manager; }()); var Cotch = /** @class */ (function () { function Cotch() { this.manager = new Manager(); } Cotch.prototype.beforePlay = function () { console.log('佈置戰術'); console.log('球隊訓話'); }; Cotch.prototype.afterPlay = function () { console.log('賽後採訪'); }; Cotch.prototype.play = function () { this.beforePlay(); this.manager.play(); this.afterPlay(); }; return Cotch; }()); var proxy = new Cotch(); proxy.play(); // 佈置戰術 // 球隊訓話 // 比賽開始了,是由皇家馬德里對陣拜仁慕尼黑。。。 // 賽後採訪
Why
優勢
缺點
發佈-訂閱模式
What
定義:對象間的一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,當一個對象發生改變時,全部依賴於它的對象都將獲得通知
解釋:好比JS中的addEventListener
Game:進遊戲後你會訂閱隊友和敵人的生存狀態,當生存狀態發生變化時,系統會及時通知你
How
TS版本
class EventEmitter{ private events: Object = {}; // 存儲事件 private key: number = 0; // 事件的惟一標識key on(name: string,event: any): number{ event.key = ++this.key; this.events[name] ? this.events[name].push(event) : (this.events[name] = []) && this.events[name].push(event); return this.key; } off(name: string,key: number){ if(this.events[name]){ this.events[name] = this.events[name].filter(x => x.key !== key); }else{ this.events[name] = []; } } emit(name: string,key?: number){ if(this.events[name].length === 0 ) throw Error(`抱歉,你沒有定義 ${name}監聽器`) if(key){ this.events[name].forEach(x => x.key === key && x()); }else { this.events[name].forEach(x => x()); } } } let player: EventEmitter = new EventEmitter(); player.on('friendDied',function(): void{ console.log('你可愛的隊友已被擊殺'); }) player.on('enemyDied',function(): void{ console.log('打的好呀,小帥哥') }) // 模擬戰況 let rand: number; let k: number = 1; while(k < 10){ rand = Math.floor(Math.random() * 10); if(rand % 2 === 0){ player.emit('friendDied'); }else{ player.emit('enemyDied'); } k++; } // 你可愛的隊友已被擊殺 // 打的好呀,小帥哥 // 你可愛的隊友已被擊殺 // 你可愛的隊友已被擊殺 // 打的好呀,小帥哥 // 你可愛的隊友已被擊殺 // 你可愛的隊友已被擊殺 // 打的好呀,小帥哥 // 打的好呀,小帥哥
JS版本
var EventEmitter = /** @class */ (function () { function EventEmitter() { this.events = {}; // 存儲事件 this.key = 0; // 事件的惟一標識key } EventEmitter.prototype.on = function (name, event) { event.key = ++this.key; this.events[name] ? this.events[name].push(event) : (this.events[name] = []) && this.events[name].push(event); return this.key; }; EventEmitter.prototype.off = function (name, key) { if (this.events[name]) { this.events[name] = this.events[name].filter(function (x) { return x.key !== key; }); } else { this.events[name] = []; } }; EventEmitter.prototype.emit = function (name, key) { if (this.events[name].length === 0) throw Error("\u62B1\u6B49\uFF0C\u4F60\u6CA1\u6709\u5B9A\u4E49 " + name + "\u76D1\u542C\u5668"); if (key) { this.events[name].forEach(function (x) { return x.key === key && x(); }); } else { this.events[name].forEach(function (x) { return x(); }); } }; return EventEmitter; }()); var player = new EventEmitter(); player.on('friendDied', function () { console.log('你可愛的隊友已被擊殺'); }); player.on('enemyDied', function () { console.log('打的好呀,小帥哥'); }); // 模擬戰況 var rand; var k = 1; while (k < 10) { rand = Math.floor(Math.random() * 10); if (rand % 2 === 0) { player.emit('friendDied'); } else { player.emit('enemyDied'); } k++; } // 你可愛的隊友已被擊殺 // 打的好呀,小帥哥 // 你可愛的隊友已被擊殺 // 你可愛的隊友已被擊殺 // 打的好呀,小帥哥 // 你可愛的隊友已被擊殺 // 你可愛的隊友已被擊殺 // 打的好呀,小帥哥 // 打的好呀,小帥哥
Why
優勢
很好的實現事件推送,創建一套完備的觸發機制
缺點
應用場景:JS中的addEventListener和Redux中的數據流模型
中介者模式
What
定義:一箇中介對象來封裝一系列對象之間的交互,使原有對象之間的耦合鬆散,且能夠獨立地改變它們之間的交互。
解釋:簡單來講就是模塊之間通訊的中間商
Game:FIFA Online中,你能夠在交易系統上上架球員卡,而後該商品會被交易系統轉發給其餘玩家
How
TS版本
abstract class Shop { //存儲 public fifaers: Object = {} //註冊 public register(name: string, fifaer: Fifaer): void { if (this.fifaers[name]) { console.error(`${name}名稱已存在`); } else { this.fifaers[name] = fifaer; fifaer.setMedium(this); } } //轉發 public relay(fifaer: Fifaer, message?: any): void { Object.keys(this.fifaers).forEach((name: string) => { if (this.fifaers[name] !== fifaer) { this.fifaers[name].receive(message); } }) } } //抽象玩家類 abstract class Fifaer { protected mediator: Shop; public setMedium(mediator: Shop): void { this.mediator = mediator; } public receive(message?: any): void { console.log(this.constructor.name + "收到請求:", message); } public send(message?: any): void { console.log(this.constructor.name + "上架新卡:", message); this.mediator.relay(this, message); //請中介者轉發 } } //具體中介者 class ConcreteShop extends Shop { constructor() { super() } } //具體玩家1 class Fifaer1 extends Fifaer { constructor() { super() } public receive(message?: any): void { console.log(`${message} 對於我來講太貴了`) } } //具體玩家2 class Fifaer2 extends Fifaer { constructor() { super() } public receive(message?: any): void { console.log(`${this.constructor.name}: ${message} 對於我來講剛恰好`) } } //具體玩家3 class Fifaer3 extends Fifaer { constructor() { super() } public receive(message?: any): void { console.log(`${this.constructor.name}: ${message} 對於我來講太便宜了`) } } let shop: Shop = new ConcreteShop(); let f1: Fifaer = new Fifaer1(); let f2: Fifaer = new Fifaer2(); let f3: Fifaer = new Fifaer3(); shop.register('Ronaldo',f1); shop.register('Messi',f2); shop.register('Torres',f3); f1.send('託雷斯的巔峯卡: 1E ep'); // Fifaer1上架新卡: 託雷斯的巔峯卡: 1E ep // Fifaer2: 託雷斯的巔峯卡: 1E ep 對於我來講剛恰好 // Fifaer3: 託雷斯的巔峯卡: 1E ep 對於我來講太便宜了
JS版本
var Shop = /** @class */ (function () { function Shop() { //存儲 this.fifaers = {}; } //註冊 Shop.prototype.register = function (name, fifaer) { if (this.fifaers[name]) { console.error(name + "\u540D\u79F0\u5DF2\u5B58\u5728"); } else { this.fifaers[name] = fifaer; fifaer.setMedium(this); } }; //轉發 Shop.prototype.relay = function (fifaer, message) { var _this = this; Object.keys(this.fifaers).forEach(function (name) { if (_this.fifaers[name] !== fifaer) { _this.fifaers[name].receive(message); } }); }; return Shop; }()); //抽象玩家類 var Fifaer = /** @class */ (function () { function Fifaer() { } Fifaer.prototype.setMedium = function (mediator) { this.mediator = mediator; }; Fifaer.prototype.receive = function (message) { console.log(this.constructor.name + "收到請求:", message); }; Fifaer.prototype.send = function (message) { console.log(this.constructor.name + "上架新卡:", message); this.mediator.relay(this, message); //請中介者轉發 }; return Fifaer; }()); //具體中介者 var ConcreteShop = /** @class */ (function (_super) { __extends(ConcreteShop, _super); function ConcreteShop() { return _super.call(this) || this; } return ConcreteShop; }(Shop)); //具體玩家1 var Fifaer1 = /** @class */ (function (_super) { __extends(Fifaer1, _super); function Fifaer1() { return _super.call(this) || this; } Fifaer1.prototype.receive = function (message) { console.log(message + " \u5BF9\u4E8E\u6211\u6765\u8BF4\u592A\u8D35\u4E86"); }; return Fifaer1; }(Fifaer)); //具體玩家2 var Fifaer2 = /** @class */ (function (_super) { __extends(Fifaer2, _super); function Fifaer2() { return _super.call(this) || this; } Fifaer2.prototype.receive = function (message) { console.log(this.constructor.name + ": " + message + " \u5BF9\u4E8E\u6211\u6765\u8BF4\u521A\u521A\u597D"); }; return Fifaer2; }(Fifaer)); //具體玩家3 var Fifaer3 = /** @class */ (function (_super) { __extends(Fifaer3, _super); function Fifaer3() { return _super.call(this) || this; } Fifaer3.prototype.receive = function (message) { console.log(this.constructor.name + ": " + message + " \u5BF9\u4E8E\u6211\u6765\u8BF4\u592A\u4FBF\u5B9C\u4E86"); }; return Fifaer3; }(Fifaer)); var shop = new ConcreteShop(); var f1 = new Fifaer1(); var f2 = new Fifaer2(); var f3 = new Fifaer3(); shop.register('Ronaldo', f1); shop.register('Messi', f2); shop.register('Torres', f3); f1.send('託雷斯的巔峯卡: 1E ep'); // Fifaer1上架新卡: 託雷斯的巔峯卡: 1E ep // Fifaer2: 託雷斯的巔峯卡: 1E ep 對於我來講剛恰好 // Fifaer3: 託雷斯的巔峯卡: 1E ep 對於我來講太便宜了
Why
優勢
減小類之間的依賴,將本來一對多的依賴變成一對一的依賴(即單個玩家買東西只需去交易市場而不須要去找持有該物品的玩家)
缺點
中介者可能會變得很大,邏輯複雜
應用場景:多個對象解耦合,判斷標準是類圖中是否存在了網狀結構
裝飾器模式
What
定義:不改變現有對象結構的狀況下,動態地給該對象增長一些職責(即增長其額外功能)的模式
解釋:使用裝飾器模式能在不改變源代碼的基礎上,對源代碼的功能進行拓展
Game:鬼泣4中,但丁在暴揍各大領主得到許多道具,所以解鎖幾種戰鬥模式,如槍神模式
How
TS版本
@blademasterDecoration @gunslingerDecoration @tricksterDecoration @royalGuardDecoration class Dante { sayHi() { console.log(`My name is: Dante`) } } // 劍聖模式 function blademasterDecoration(target: any){ target.prototype.blademaster = function(){console.log('I am blademaster!')} } // 槍神模式 function gunslingerDecoration(target){ target.prototype.gunslinger = function(){console.log('I am gunslinger!')} } // 騙術師模式 function tricksterDecoration(target){ target.prototype.trickster = function(){console.log('I am trickster!')} } // 皇家守衛模式 function royalGuardDecoration(target){ target.prototype.royalGuard = function(){console.log('I am royalGuard!')} } let dante: Dante = new Dante(); dante.blademaster(); dante.gunslinger(); dante.trickster(); dante.royalGuard(); // I am blademaster! // I am gunslinger! // I am trickster! // I am royalGuard!
JS版本
var Dante = /** @class */ (function () { function Dante() { } Dante.prototype.sayHi = function () { console.log("My name is: Dante"); }; Dante = __decorate([ blademasterDecoration, gunslingerDecoration, tricksterDecoration, royalGuardDecoration ], Dante); return Dante; }()); // 劍聖模式 function blademasterDecoration(target) { target.prototype.blademaster = function () { console.log('I am blademaster!'); }; } // 槍神模式 function gunslingerDecoration(target) { target.prototype.gunslinger = function () { console.log('I am gunslinger!'); }; } // 騙術師模式 function tricksterDecoration(target) { target.prototype.trickster = function () { console.log('I am trickster!'); }; } // 皇家守衛模式 function royalGuardDecoration(target) { target.prototype.royalGuard = function () { console.log('I am royalGuard!'); }; } var dante = new Dante(); dante.blademaster(); dante.gunslinger(); dante.trickster(); dante.royalGuard(); // I am blademaster! // I am gunslinger! // I am trickster! // I am royalGuard!
Why
優勢
可以更加靈活地擴展類的功能
缺點
多層次的裝飾嵌套,增大了代碼的理解難度
應用場景:想要添加新的功能同時不修改原生的代碼
適配器模式
What
定義:將一個類的接口轉換成客戶但願的另一個接口,使得本來因爲接口不兼容而不能一塊兒工做的那些類能一塊兒工做
解釋:簡單來講就是打補丁,兼容一些舊的接口
Game:LOL中卡茲克登場時,空中能夠釋放w秒人,號稱飛天螳螂。由於過於變態與英雄平衡機制不兼容,因而給他打了個補丁,W自己改動不大隻是不容許在空中釋放了。
How
TS版本
interface TargetW{ request(): void; } // 源接口 class OriginW{ normalRequest(): void{ console.log('個人w可以治療、減速'); } flyRequest(): void{ console.log('個人w能在空中釋放'); } } class AdapterW extends OriginW implements TargetW{ constructor(){ super(); } request(): void{ console.log('取消了w在空中釋放的機制'); this.normalRequest(); } } let target: TargetW = new AdapterW(); target.request(); // 取消了w在空中釋放的機制 // 個人w可以治療、減速
JS版本
var OriginW = /** @class */ (function () { function OriginW() { } OriginW.prototype.normalRequest = function () { console.log('個人w可以治療、減速'); }; OriginW.prototype.flyRequest = function () { console.log('個人w能在空中釋放'); }; return OriginW; }()); var AdapterW = /** @class */ (function (_super) { __extends(AdapterW, _super); function AdapterW() { return _super.call(this) || this; } AdapterW.prototype.request = function () { console.log('取消了w在空中釋放的機制'); this.normalRequest(); }; return AdapterW; }(OriginW)); var target = new AdapterW(); target.request(); // 取消了w在空中釋放的機制 // 個人w可以治療、減速
Why
優勢
將目標類和源接口解耦,有不錯的靈活性和可擴展性
缺點
做爲補丁,出現過多增大系統的複雜度。
應用場景:兼容舊的接口時
組合模式
What
定義:有時又叫做部分-總體模式,它是一種將對象組合成樹狀的層次結構的模式,用來表示「部分-總體」的關係,使用戶對單個對象和組合對象具備一致的訪問性。
解釋:將對象之間的關係以🌲的形式進行表現
Game:最終幻想13中技能樹的結構,每一個加點方向爲樹幹,每一個技能點爲樹葉
How
TS版本
interface SkillTree{ add(st: SkillTree): void; operation(): void; } class SkillDirection implements SkillTree{ private children: SkillTree[] = []; constructor(private name: string){} add(node: SkillTree): void{ this.children.push(node); } operation(): void{ console.log(`你選擇${this.name}方向的技能樹`) this.children.forEach((x: SkillTree) => x.operation()) } } class SkillPoint implements SkillTree{ constructor(private name: string){} add(node: SkillTree): void{}; operation(): void{ console.log(`你已學習技能點 ${this.name}`) } } let tree1: SkillTree = new SkillDirection('治療'); let tree2: SkillTree = new SkillDirection('傷害'); let leaf1: SkillTree = new SkillPoint('全體加血'); let leaf2: SkillTree = new SkillPoint('單體攻擊'); tree2.add(leaf2); tree1.add(tree2); tree1.add(leaf1); tree1.operation(); // 你選擇治療方向的技能樹 // 你選擇傷害方向的技能樹 // 你已學習技能點 單體攻擊 // 你已學習技能點 全體加血
JS版本
var SkillDirection = /** @class */ (function () { function SkillDirection(name) { this.name = name; this.children = []; } SkillDirection.prototype.add = function (node) { this.children.push(node); }; SkillDirection.prototype.operation = function () { console.log("\u4F60\u9009\u62E9" + this.name + "\u65B9\u5411\u7684\u6280\u80FD\u6811"); this.children.forEach(function (x) { return x.operation(); }); }; return SkillDirection; }()); var SkillPoint = /** @class */ (function () { function SkillPoint(name) { this.name = name; } SkillPoint.prototype.add = function (node) { }; ; SkillPoint.prototype.operation = function () { console.log("\u4F60\u5DF2\u5B66\u4E60\u6280\u80FD\u70B9 " + this.name); }; return SkillPoint; }()); var tree1 = new SkillDirection('治療'); var tree2 = new SkillDirection('傷害'); var leaf1 = new SkillPoint('全體加血'); var leaf2 = new SkillPoint('單體攻擊'); tree2.add(leaf2); tree1.add(tree2); tree1.add(leaf1); tree1.operation(); // 你選擇治療方向的技能樹 // 你選擇傷害方向的技能樹 // 你已學習技能點 單體攻擊 // 你已學習技能點 全體加血
Why
優勢
局部調用和總體調用區別不大,換言之調用者沒必要擔憂調用對象是一個簡單的仍是一個複雜的結構
缺點
使得設計更加複雜。客戶端須要花更多時間理清類之間的層次關係樹幹直接使用了實現類,限制了接口
應用場景:所描述的對象符合樹的結構,如文件管理系統
狀態模式
What
定義:對有狀態的對象,把複雜的「判斷邏輯」提取到不一樣的狀態對象中,容許狀態對象在其內部狀態發生改變時改變其行爲
解釋:不使用if-else | switch-case
進行判斷,而是經過傳入狀態對象進行狀態切換
Game:只狼中葦名一心的三個狀態,完美虐殺玩家
How
每打過一個階段,葦名就切換狀態
TS版本
interface State{ change(context: WeiMingYiXin): void; } class StateOne implements State{ change(context: WeiMingYiXin): void{ console.log('葦名弦一郎階段'); } } class StateTwo implements State{ change(context: WeiMingYiXin): void{ console.log('劍聖葦名一心階段'); } } class StateThree implements State{ change(context: WeiMingYiXin): void{ console.log('雷電法王葦名一心階段'); } } class WeiMingYiXin{ constructor(private state: State){}; setState(state: State): void{ this.state = state; } request(): void{ this.state.change(this); } } let ctx: WeiMingYiXin = new WeiMingYiXin(new StateOne()); ctx.request(); ctx.setState(new StateTwo()); ctx.request(); ctx.setState(new StateThree()); ctx.request(); // 葦名弦一郎階段 // 劍聖葦名一心階段 // 雷電法王葦名一心階段
JS版本
var StateOne = /** @class */ (function () { function StateOne() { } StateOne.prototype.change = function (context) { console.log('葦名弦一郎階段'); }; return StateOne; }()); var StateTwo = /** @class */ (function () { function StateTwo() { } StateTwo.prototype.change = function (context) { console.log('劍聖葦名一心階段'); }; return StateTwo; }()); var StateThree = /** @class */ (function () { function StateThree() { } StateThree.prototype.change = function (context) { console.log('雷電法王葦名一心階段'); }; return StateThree; }()); var WeiMingYiXin = /** @class */ (function () { function WeiMingYiXin(state) { this.state = state; } ; WeiMingYiXin.prototype.setState = function (state) { this.state = state; }; WeiMingYiXin.prototype.request = function () { this.state.change(this); }; return WeiMingYiXin; }()); var ctx = new WeiMingYiXin(new StateOne()); ctx.request(); ctx.setState(new StateTwo()); ctx.request(); ctx.setState(new StateThree()); ctx.request(); // 葦名弦一郎階段 // 劍聖葦名一心階段 // 雷電法王葦名一心階段
Why
優勢
能夠很方便新增和切換狀態
缺點
增長了不少的類,結構和實現較爲複雜
應用場景:對象的行爲依賴於它的某些屬性值,狀態的改變將致使行爲的變化
抽象工廠模式
What
定義:是一種爲訪問類提供一個建立一組相關或相互依賴對象的接口,且訪問類無須指定所要產品的具體類就能獲得同族的不一樣等級的產品的模式結構
解釋:類比於現實中的工廠,抽象工廠能夠生產多個品類的產品
Game:紅色警惕中,盟軍和蘇軍的空軍工廠和陸軍工廠分別能產出不一樣的軍備和士兵
How
TS版本
interface AbstractAirForce{ create(): void; } interface AbstractGroundForce{ create(): void; } interface AbstractGroup{ createAirForce(): AbstractAirForce; createGroundForce(): AbstractGroundForce; } class MAirForce implements AbstractAirForce{ create(): void{ console.log('戰鬥機已創立') } } class MGroundForce implements AbstractGroundForce{ create(): void{ console.log('幻影坦克已創立') } } class SAirForce implements AbstractAirForce{ create(): void{ console.log('飛艇已創立') } } class SGroundForce implements AbstractGroundForce{ create(): void{ console.log('犀牛坦克已創立') } } class MGroup implements AbstractGroup{ createAirForce(): AbstractAirForce{ return new MAirForce(); } createGroundForce(): AbstractGroundForce{ return new MGroundForce(); } } class SGroup implements AbstractGroup{ createAirForce(): AbstractAirForce{ return new SAirForce(); } createGroundForce(): AbstractGroundForce{ return new SGroundForce(); } } let mGroup: AbstractGroup = new MGroup(); let sGroup: AbstractGroup = new SGroup(); mGroup.createAirForce().create(); mGroup.createGroundForce().create(); sGroup.createGroundForce().create() sGroup.createGroundForce().create(); // 戰鬥機已創立 // 幻影坦克已創立 // 犀牛坦克已創立 // 犀牛坦克已創立
JS版本
var MAirForce = /** @class */ (function () { function MAirForce() { } MAirForce.prototype.create = function () { console.log('戰鬥機已創立'); }; return MAirForce; }()); var MGroundForce = /** @class */ (function () { function MGroundForce() { } MGroundForce.prototype.create = function () { console.log('幻影坦克已創立'); }; return MGroundForce; }()); var SAirForce = /** @class */ (function () { function SAirForce() { } SAirForce.prototype.create = function () { console.log('飛艇已創立'); }; return SAirForce; }()); var SGroundForce = /** @class */ (function () { function SGroundForce() { } SGroundForce.prototype.create = function () { console.log('犀牛坦克已創立'); }; return SGroundForce; }()); var MGroup = /** @class */ (function () { function MGroup() { } MGroup.prototype.createAirForce = function () { return new MAirForce(); }; MGroup.prototype.createGroundForce = function () { return new MGroundForce(); }; return MGroup; }()); var SGroup = /** @class */ (function () { function SGroup() { } SGroup.prototype.createAirForce = function () { return new SAirForce(); }; SGroup.prototype.createGroundForce = function () { return new SGroundForce(); }; return SGroup; }()); var mGroup = new MGroup(); var sGroup = new SGroup(); mGroup.createAirForce().create(); mGroup.createGroundForce().create(); sGroup.createGroundForce().create(); sGroup.createGroundForce().create(); // 戰鬥機已創立 // 幻影坦克已創立 // 犀牛坦克已創立 // 犀牛坦克已創立
Why
優勢
增長新的具體工廠和產品族很方便,無須修改已有系統,符合「開閉原則」
缺點
增長新的產品等級結構很複雜,須要修改抽象工廠和全部的具體工廠類
應用場景:當須要建立的對象是一系列相互關聯或相互依賴的產品族時,即可以使用抽象工廠模式
單一職責原則(SRP)
What
一個對象(方法)只作一件事情。
How
Why
最小知識原則(LKP)
What
設計程序時,應當減小對象之間的交互,避免出現a.foo1(b).foo2(c).foo3(d)
的狀況出現
若是兩個對象之間沒必要直接通訊,那麼這兩個對象就不要發生直接聯繫,而是引入一個第三方對象,來承擔這些對象之間的通訊做用
How
例如使用中介者模式時,兩個對象並不直接聯繫,而是經過中介者的接口進行聯繫。就像電商網站,消費者和商家經過平臺進行交易而不是直接交易
Why
開放-封閉原則(OCP)
What
添加新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼
How
if-else | switch-case
等大量條件分支語句Why
bug
而引入更多的bug
,而新增代碼是一種更加明智的選擇《JS設計模式與開發實踐》