紅寶書第十二講:詳解JavaScript中的工廠模式與原型模式等各種設(shè)計(jì)模式
資料取自《JavaScript高級(jí)程序設(shè)計(jì)(第5版)》。
查看總目錄:紅寶書學(xué)習(xí)大綱
工廠模式和原型模式解析
一、工廠模式:像訂外賣一樣創(chuàng)建對(duì)象
工廠模式就像一個(gè)“對(duì)象生成器”,只需要告訴它“我要什么”,它就會(huì)自動(dòng)生成并返回對(duì)應(yīng)的對(duì)象。適合需要批量創(chuàng)建復(fù)雜對(duì)象的場(chǎng)景 [1]。
案例:點(diǎn)餐系統(tǒng)
假設(shè)你經(jīng)營(yíng)奶茶店,需要根據(jù)訂單類型生成不同配置的飲品:
// 工廠函數(shù)決定飲品類型和配料
function createDrink(type, name) {
const drink = { name };
if (type === '奶茶') {
drink.base = '紅茶';
drink.toppings = ['珍珠', '奶蓋'];
} else if (type === '果茶') {
drink.base = '四季春茶';
drink.toppings = ['椰果', '寒天'];
}
return drink;
}
// 下單操作
const order1 = createDrink('奶茶', '招牌奶茶');
const order2 = createDrink('果茶', '滿杯鮮橙');
[1]: 參考資料6明確將工廠模式列為JavaScript對(duì)象創(chuàng)建的核心設(shè)計(jì)模式
二、原型模式:共享技能的忍者軍團(tuán)
原型模式的精髓是“多人共用同一套技能”。將公共方法存放在原型(prototype
)中,所有實(shí)例無(wú)需重復(fù)存儲(chǔ)這些方法 [2]。
示例:游戲中的敵人構(gòu)造
// 定義敵人原型
function Enemy(type) {
this.type = type;
}
// 共享攻擊方法(所有敵人都會(huì))
Enemy.prototype.attack = function() {
console.log(`${this.type}發(fā)起攻擊!`);
};
// 創(chuàng)建實(shí)例
const dragon = new Enemy('火龍');
const goblin = new Enemy('哥布林');
dragon.attack(); // 火龍發(fā)起攻擊!
goblin.attack(); // 哥布林發(fā)起攻擊!
// 驗(yàn)證方法共享
console.log(dragon.attack === goblin.attack); // true ?
[2]: 參考資料4通過Person.prototype案例證明原型共享方法的有效性
flowchart LR
Enemy.prototype --> 共享方法attack
dragon實(shí)例 -->|.__proto__| Enemy.prototype
goblin實(shí)例 -->|.__proto__| Enemy.prototype
為什么用原型?
- 內(nèi)存節(jié)省:1000個(gè)實(shí)例共享1個(gè)方法,而非存儲(chǔ)1000份 [3]
- 動(dòng)態(tài)更新:在原型添加新方法,現(xiàn)有實(shí)例即時(shí)生效
例如新增
Enemy.prototype.run = function() { ... }
,所有敵人都能調(diào)用.run()
三、對(duì)比總結(jié)
模式 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|---|
工廠模式 | 需要靈活創(chuàng)建多類型對(duì)象 | 隱藏創(chuàng)建細(xì)節(jié),代碼簡(jiǎn)潔 | 類型識(shí)別困難(如無(wú)法用instanceof ) |
原型模式 | 大量對(duì)象需共享方法或?qū)傩?/td> | 節(jié)省內(nèi)存,動(dòng)態(tài)擴(kuò)展性強(qiáng) | 復(fù)雜屬性需獨(dú)立初始化(如對(duì)象引用) |
[3]: 參考資料3強(qiáng)調(diào)代碼維護(hù)性需通過共享結(jié)構(gòu)實(shí)現(xiàn),這正是原型模式的核心優(yōu)勢(shì)
好的!以下是根據(jù)你的要求修改后的版本,增加了對(duì)比表格、詳細(xì)說明文字以及代碼注釋。
JavaScript 常用設(shè)計(jì)模式解析
一、設(shè)計(jì)模式特點(diǎn)對(duì)比表
設(shè)計(jì)模式 | 主要用途 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|---|
工廠模式 | 創(chuàng)建多種類型的對(duì)象,隱藏創(chuàng)建邏輯,提供統(tǒng)一的創(chuàng)建接口 | 簡(jiǎn)化對(duì)象創(chuàng)建邏輯,易于擴(kuò)展和維護(hù) | 增加了系統(tǒng)的復(fù)雜性,可能需要維護(hù)多個(gè)工廠類 |
單例模式 | 確保一個(gè)類只有一個(gè)實(shí)例,并提供全局訪問點(diǎn) | 避免重復(fù)實(shí)例化,節(jié)省資源,便于管理全局狀態(tài) | 可能導(dǎo)致代碼難以測(cè)試,過度使用會(huì)限制系統(tǒng)的靈活性 |
原型模式 | 通過復(fù)制現(xiàn)有對(duì)象創(chuàng)建新對(duì)象,避免復(fù)雜的初始化過程 | 性能優(yōu)化,減少重復(fù)代碼,易于創(chuàng)建多個(gè)類似對(duì)象 | 可能導(dǎo)致對(duì)象之間的關(guān)系復(fù)雜,難以管理 |
代理模式 | 提供一個(gè)代理對(duì)象來(lái)控制對(duì)實(shí)際對(duì)象的訪問,添加額外邏輯 | 可以在不修改原始對(duì)象的情況下添加功能,如權(quán)限控制、緩存等 | 增加了系統(tǒng)的復(fù)雜性,可能影響性能 |
觀察者模式 | 定義對(duì)象間的一對(duì)多依賴關(guān)系,當(dāng)一個(gè)對(duì)象改變時(shí),所有依賴它的對(duì)象都會(huì)得到通知 | 實(shí)現(xiàn)了對(duì)象間的松耦合,便于擴(kuò)展和維護(hù) | 可能導(dǎo)致通知風(fēng)暴,影響性能,且依賴關(guān)系復(fù)雜 |
策略模式 | 定義一系列算法,封裝起來(lái)并使它們可互換 | 提供了算法的可切換性,易于擴(kuò)展和維護(hù) | 可能導(dǎo)致策略類過多,增加系統(tǒng)的復(fù)雜性 |
裝飾者模式 | 動(dòng)態(tài)地給對(duì)象添加額外的功能,而不改變其結(jié)構(gòu) | 提供了比繼承更靈活的擴(kuò)展方式,易于添加新功能 | 可能導(dǎo)致裝飾器過多,影響性能和可讀性 |
適配器模式 | 將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另一個(gè)接口 | 提高了類的兼容性,避免了修改現(xiàn)有代碼 | 增加了系統(tǒng)的復(fù)雜性,可能隱藏了接口的不一致性 |
模板方法模式 | 定義一個(gè)操作中的算法框架,將某些步驟延遲到子類中 | 提供了代碼復(fù)用性,便于維護(hù)和擴(kuò)展 | 子類可能過度依賴父類,限制了靈活性 |
建造者模式 | 逐步構(gòu)建一個(gè)復(fù)雜的對(duì)象,隱藏構(gòu)建過程 | 提供了構(gòu)建過程的靈活性,易于擴(kuò)展和維護(hù) | 可能增加系統(tǒng)的復(fù)雜性,需要維護(hù)多個(gè)建造者類 |
二、設(shè)計(jì)模式詳細(xì)解析
1. 工廠模式(Factory Pattern)
詳細(xì)說明
工廠模式就像一個(gè)“制造機(jī)器”,它的主要任務(wù)是根據(jù)不同的需求創(chuàng)建不同類型的對(duì)象,而不需要直接使用 new
關(guān)鍵字去實(shí)例化對(duì)象。想象一下,你去玩具店買玩具,你只需要告訴店員你想要什么類型的玩具(比如小汽車、洋娃娃),店員就會(huì)給你制造出對(duì)應(yīng)類型的玩具,而你不需要自己去組裝玩具。工廠模式的核心在于隱藏了對(duì)象的創(chuàng)建細(xì)節(jié),讓使用者只需要關(guān)心對(duì)象的使用,而不需要關(guān)心對(duì)象是如何被創(chuàng)建的。
代碼示例
// 定義一個(gè)基類 Animal
class Animal {
speak() {
return this.makeSound();
}
}
// 定義具體的 Dog 類
class Dog extends Animal {
makeSound() {
return "Woof"; // 狗的叫聲
}
}
// 定義具體的 Cat 類
class Cat extends Animal {
makeSound() {
return "Meow"; // 貓的叫聲
}
}
// 工廠類,負(fù)責(zé)創(chuàng)建不同類型的動(dòng)物對(duì)象
class AnimalFactory {
createAnimal(type) {
switch (type) {
case "dog":
return new Dog(); // 創(chuàng)建 Dog 實(shí)例
case "cat":
return new Cat(); // 創(chuàng)建 Cat 實(shí)例
default:
throw new Error("Unknown animal type"); // 如果類型未知,拋出錯(cuò)誤
}
}
}
// 使用工廠模式創(chuàng)建對(duì)象
const factory = new AnimalFactory();
const dog = factory.createAnimal("dog"); // 創(chuàng)建 Dog 實(shí)例
console.log(dog.speak()); // 輸出:Woof
const cat = factory.createAnimal("cat"); // 創(chuàng)建 Cat 實(shí)例
console.log(cat.speak()); // 輸出:Meow
代碼注釋
-
Animal
是一個(gè)基類,定義了一個(gè)通用的speak
方法。 -
Dog
和Cat
是具體的動(dòng)物類,分別實(shí)現(xiàn)了makeSound
方法。 -
AnimalFactory
是工廠類,通過createAnimal
方法根據(jù)傳入的類型參數(shù)創(chuàng)建對(duì)應(yīng)的動(dòng)物對(duì)象。 - 使用工廠模式時(shí),我們只需要通過工廠類的
createAnimal
方法來(lái)獲取對(duì)象,而不需要直接使用new
關(guān)鍵字。
流程圖
graph TD
A[開始] --> B[創(chuàng)建工廠實(shí)例]
B --> C[調(diào)用工廠方法]
C --> D{根據(jù)類型判斷}
D -- dog --> E[創(chuàng)建Dog實(shí)例]
D -- cat --> F[創(chuàng)建Cat實(shí)例]
E --> G[返回Dog實(shí)例]
F --> H[返回Cat實(shí)例]
G --> I[使用實(shí)例]
H --> I
I --> J[結(jié)束]
2. 單例模式(Singleton Pattern)
詳細(xì)說明
單例模式確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)。想象一下,一個(gè)國(guó)家的總統(tǒng),全國(guó)只能有一個(gè)總統(tǒng),不管你從哪里獲取總統(tǒng)的信息,都是同一個(gè)總統(tǒng)對(duì)象。如果已經(jīng)有一個(gè)總統(tǒng)實(shí)例了,再獲取總統(tǒng)對(duì)象時(shí),就直接返回這個(gè)已經(jīng)存在的實(shí)例,而不是再創(chuàng)建一個(gè)新的。單例模式的核心在于保證全局唯一性,避免重復(fù)實(shí)例化,節(jié)省資源。
代碼示例
// 定義一個(gè) Logger 類
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance; // 如果已經(jīng)存在實(shí)例,直接返回
}
this.messages = []; // 用于存儲(chǔ)日志消息
Logger.instance = this; // 保存當(dāng)前實(shí)例
}
log(message) {
this.messages.push(message); // 添加日志消息
}
getLogs() {
return this.messages; // 獲取所有日志消息
}
}
// 使用單例模式
const logger1 = new Logger();
logger1.log("Hello"); // 添加日志
const logger2 = new Logger();
logger2.log("World"); // 添加日志
console.log(logger1.getLogs()); // 輸出:["Hello", "World"]
console.log(logger1 === logger2); // 輸出:true(logger1 和 logger2 是同一個(gè)實(shí)例)
代碼注釋
-
Logger
類中有一個(gè)靜態(tài)屬性instance
,用于保存唯一的實(shí)例。 - 在構(gòu)造函數(shù)中,如果
Logger.instance
已經(jīng)存在,則直接返回已存在的實(shí)例,否則創(chuàng)建一個(gè)新的實(shí)例并保存到Logger.instance
中。 - 通過
log
方法添加日志消息,通過getLogs
方法獲取所有日志消息。 - 無(wú)論創(chuàng)建多少次
Logger
實(shí)例,始終返回同一個(gè)實(shí)例,確保全局唯一性。
流程圖
graph TD
A[開始] --> B[創(chuàng)建Logger實(shí)例]
B --> C{是否已存在實(shí)例}
C -- 是 --> D[返回已存在的實(shí)例]
C -- 否 --> E[創(chuàng)建新實(shí)例]
E --> F[保存實(shí)例]
F --> G[使用實(shí)例]
D --> G
G --> H[結(jié)束]
3. 原型模式(Prototype Pattern)
詳細(xì)說明
原型模式允許通過復(fù)制現(xiàn)有的對(duì)象來(lái)創(chuàng)建新的對(duì)象,而不需要通過構(gòu)造函數(shù)來(lái)創(chuàng)建。想象一下,你有一個(gè)已經(jīng)配置好的電腦模板,你可以直接復(fù)制這個(gè)模板來(lái)創(chuàng)建新的電腦,而不需要重新配置。原型模式的核心在于通過對(duì)象的復(fù)制來(lái)創(chuàng)建新對(duì)象,避免了復(fù)雜的初始化過程,提高了性能。
代碼示例
// 定義一個(gè)原型對(duì)象
const animalPrototype = {
speak() {
return this.makeSound(); // 調(diào)用 makeSound 方法
},
};
// 通過 Object.create 復(fù)制原型對(duì)象創(chuàng)建 Dog 實(shí)例
const dog = Object.create(animalPrototype);
dog.makeSound = function () {
return "Woof"; // 狗的叫聲
};
// 通過 Object.create 復(fù)制原型對(duì)象創(chuàng)建 Cat 實(shí)例
const cat = Object.create(animalPrototype);
cat.makeSound = function () {
return "Meow"; // 貓的叫聲
};
console.log(dog.speak()); // 輸出:Woof
console.log(cat.speak()); // 輸出:Meow
代碼注釋
-
animalPrototype
是一個(gè)原型對(duì)象,定義了一個(gè)通用的speak
方法。 - 使用
Object.create
方法,通過復(fù)制animalPrototype
來(lái)創(chuàng)建dog
和cat
實(shí)例。 - 每個(gè)實(shí)例都可以通過覆蓋
makeSound
方法來(lái)定義自己的行為。 - 這種方式避免了通過構(gòu)造函數(shù)創(chuàng)建對(duì)象的復(fù)雜性,同時(shí)保持了對(duì)象的繼承關(guān)系。
流程圖
graph TD
A[開始] --> B[創(chuàng)建原型對(duì)象]
B --> C[復(fù)制原型對(duì)象]
C --> D[修改復(fù)制對(duì)象的屬性]
D --> E[使用復(fù)制對(duì)象]
E --> F[結(jié)束]
4. 代理模式(Proxy Pattern)
詳細(xì)說明
代理模式通過創(chuàng)建一個(gè)代理對(duì)象來(lái)控制對(duì)實(shí)際對(duì)象的訪問,代理對(duì)象可以添加額外的邏輯,比如權(quán)限檢查、緩存、遠(yuǎn)程調(diào)用等。想象一下,你去銀行取錢,銀行柜員就是一個(gè)代理,他可以檢查你的身份,確認(rèn)你有權(quán)限取錢后,才會(huì)讓你取錢。代理模式的核心在于在不修改原始對(duì)象的情況下,添加額外的控制邏輯。
代碼示例
// 定義一個(gè)真實(shí)對(duì)象
class RealSubject {
request() {
return "RealSubject: Handling request."; // 真實(shí)對(duì)象的請(qǐng)求方法
}
}
// 定義一個(gè)代理對(duì)象
class Proxy {
constructor(realSubject) {
this.realSubject = realSubject; // 保存真實(shí)對(duì)象的引用
}
request() {
if (this.checkAccess()) { // 檢查權(quán)限
this.realSubject.request(); // 調(diào)用真實(shí)對(duì)象的請(qǐng)求方法
this.logAccess(); // 記錄日志
}
}
checkAccess() {
console.log("Proxy: Logging the time of request."); // 記錄請(qǐng)求時(shí)間
return true; // 假設(shè)權(quán)限檢查通過
}
logAccess() {
console.log("Proxy: Logging the request."); // 記錄請(qǐng)求
}
}
// 使用代理模式
const realSubject = new RealSubject(); // 創(chuàng)建真實(shí)對(duì)象
const proxy = new Proxy(realSubject); // 創(chuàng)建代理對(duì)象
proxy.request(); // 調(diào)用代理的請(qǐng)求方法
代碼注釋
-
RealSubject
是真實(shí)對(duì)象,定義了一個(gè)request
方法。 -
Proxy
是代理對(duì)象,通過request
方法控制對(duì)真實(shí)對(duì)象的訪問。 - 在調(diào)用真實(shí)對(duì)象的
request
方法之前,代理對(duì)象會(huì)執(zhí)行額外的邏輯,如權(quán)限檢查和日志記錄。 - 通過代理模式,可以在不修改真實(shí)對(duì)象的情況下,添加額外的功能。
流程圖
graph TD
A[開始] --> B[創(chuàng)建真實(shí)對(duì)象]
B --> C[創(chuàng)建代理對(duì)象]
C --> D[調(diào)用代理的請(qǐng)求方法]
D --> E[檢查權(quán)限]
E -- 有權(quán)限 --> F[調(diào)用真實(shí)對(duì)象的方法]
F --> G[記錄日志]
G --> H[結(jié)束]
E -- 無(wú)權(quán)限 --> H
5. 觀察者模式(Observer Pattern)
詳細(xì)說明
觀察者模式定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽某一個(gè)主題對(duì)象。當(dāng)主題對(duì)象發(fā)生變化時(shí),所有依賴它的觀察者都會(huì)自動(dòng)收到通知并進(jìn)行更新。想象一下,你關(guān)注了一個(gè)博主,當(dāng)博主發(fā)布新的文章時(shí),你會(huì)收到通知。觀察者模式的核心在于實(shí)現(xiàn)對(duì)象間的松耦合,當(dāng)一個(gè)對(duì)象改變時(shí),其他對(duì)象可以自動(dòng)響應(yīng)。
代碼示例
// 定義一個(gè)主題對(duì)象
class Subject {
constructor() {
this.observers = []; // 保存觀察者列表
}
addObserver(observer) {
this.observers.push(observer); // 添加觀察者
}
removeObserver(observer) {
this.observers = this.observers.filter((obs) => obs !== observer); // 移除觀察者
}
notify() {
this.observers.forEach((observer) => observer.update()); // 通知所有觀察者
}
}
// 定義一個(gè)觀察者對(duì)象
class Observer {
update() {
console.log("Observer: I've been notified!"); // 觀察者收到通知后的響應(yīng)
}
}
// 使用觀察者模式
const subject = new Subject(); // 創(chuàng)建主題對(duì)象
const observer1 = new Observer(); // 創(chuàng)建觀察者1
const observer2 = new Observer(); // 創(chuàng)建觀察者2
subject.addObserver(observer1); // 添加觀察者1
subject.addObserver(observer2); // 添加觀察者2
subject.notify(); // 通知所有觀察者
代碼注釋
-
Subject
是主題對(duì)象,維護(hù)一個(gè)觀察者列表,并提供添加、移除和通知觀察者的方法。 -
Observer
是觀察者對(duì)象,定義了一個(gè)update
方法,用于響應(yīng)通知。 - 當(dāng)主題對(duì)象的狀態(tài)發(fā)生變化時(shí),通過調(diào)用
notify
方法通知所有觀察者。 - 觀察者模式實(shí)現(xiàn)了對(duì)象間的松耦合,便于擴(kuò)展和維護(hù)。
流程圖
graph TD
A[開始] --> B[創(chuàng)建主題對(duì)象]
B --> C[創(chuàng)建觀察者對(duì)象]
C --> D[將觀察者添加到主題]
D --> E[主題狀態(tài)改變]
E --> F[通知所有觀察者]
F --> G[觀察者執(zhí)行更新]
G --> H[結(jié)束]
6. 策略模式(Strategy Pattern)
詳細(xì)說明
策略模式定義了一系列的算法,把它們封裝起來(lái),并使它們可以互換。策略模式讓算法的變化獨(dú)立于使用算法的客戶。想象一下,你有一個(gè)計(jì)算器,可以選擇不同的運(yùn)算方式(如加法、減法)。策略模式的核心在于通過封裝不同的算法,讓客戶端可以在運(yùn)行時(shí)動(dòng)態(tài)選擇算法,而不需要修改代碼。
代碼示例
// 定義一個(gè)上下文對(duì)象
class Context {
constructor(strategy) {
this.strategy = strategy; // 保存策略對(duì)象
}
executeStrategy(a, b) {
return this.strategy(a, b); // 執(zhí)行策略
}
}
// 定義加法策略
function addStrategy(a, b) {
return a + b; // 加法運(yùn)算
}
// 定義減法策略
function subtractStrategy(a, b) {
return a - b; // 減法運(yùn)算
}
// 使用策略模式
const context = new Context(addStrategy); // 使用加法策略
console.log(context.executeStrategy(10, 5)); // 輸出:15
context.strategy = subtractStrategy; // 切換到減法策略
console.log(context.executeStrategy(10, 5)); // 輸出:5
代碼注釋
-
Context
是上下文對(duì)象,保存一個(gè)策略對(duì)象,并通過executeStrategy
方法執(zhí)行策略。 -
addStrategy
和subtractStrategy
是具體的策略,分別實(shí)現(xiàn)加法和減法運(yùn)算。 - 客戶端可以通過動(dòng)態(tài)更換策略對(duì)象來(lái)改變行為,而不需要修改上下文對(duì)象的代碼。
流程圖
graph TD
A[開始] --> B[創(chuàng)建上下文對(duì)象]
B --> C[設(shè)置策略]
C --> D[執(zhí)行策略]
D --> E[輸出結(jié)果]
E --> F[結(jié)束]
7. 裝飾者模式(Decorator Pattern)
詳細(xì)說明
裝飾者模式允許動(dòng)態(tài)地給一個(gè)對(duì)象添加額外的功能,而不需要修改其結(jié)構(gòu)。想象一下,你有一件衣服,你可以通過添加裝飾(如徽章、刺繡)來(lái)改變它的外觀,而不需要改變衣服本身的結(jié)構(gòu)。裝飾者模式的核心在于通過組合的方式,動(dòng)態(tài)地?cái)U(kuò)展對(duì)象的功能。
代碼示例
// 定義一個(gè)組件基類
class Component {
operation() {
return "Component"; // 基類的通用方法
}
}
// 定義具體的組件類
class ConcreteComponent extends Component {
operation() {
return "ConcreteComponent"; // 具體組件的實(shí)現(xiàn)
}
}
// 定義裝飾者類
class Decorator extends Component {
constructor(component) {
super();
this.component = component; // 保存組件對(duì)象
}
operation() {
return `Decorator(${this.component.operation()})`; // 調(diào)用組件對(duì)象的方法,并添加額外邏輯
}
}
// 使用裝飾者模式
const component = new ConcreteComponent(); // 創(chuàng)建具體組件
console.log(component.operation()); // 輸出:ConcreteComponent
const decoratedComponent = new Decorator(component); // 創(chuàng)建裝飾者
console.log(decoratedComponent.operation()); // 輸出:Decorator(ConcreteComponent)
代碼注釋
-
Component
是組件基類,定義了一個(gè)通用的operation
方法。 -
ConcreteComponent
是具體的組件類,實(shí)現(xiàn)了operation
方法。 -
Decorator
是裝飾者類,通過組合的方式保存一個(gè)組件對(duì)象,并在調(diào)用組件對(duì)象的方法時(shí)添加額外的邏輯。 - 通過裝飾者模式,可以在不修改組件類的情況下,動(dòng)態(tài)地?cái)U(kuò)展功能。
流程圖
graph TD
A[開始] --> B[創(chuàng)建組件對(duì)象]
B --> C[創(chuàng)建裝飾者對(duì)象]
C --> D[裝飾者調(diào)用組件方法]
D --> E[輸出結(jié)果]
E --> F[結(jié)束]
8. 適配器模式(Adapter Pattern)
詳細(xì)說明
適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另一個(gè)接口,使原本不兼容的接口能夠兼容。想象一下,你有一個(gè)充電器,但它只能給手機(jī)充電,而你有一個(gè)平板電腦需要充電。適配器模式的核心在于通過一個(gè)適配器對(duì)象,將充電器的接口轉(zhuǎn)換為平板電腦可以使用的接口,從而實(shí)現(xiàn)兼容。
代碼示例
// 定義一個(gè)媒體播放器接口
class MediaPlayer {
play(audioType, fileName) {
console.log(`Playing ${audioType} file. Name: ${fileName}`); // 播放音頻文件
}
}
// 定義一個(gè)高級(jí)媒體播放器接口
class AdvancedMediaPlayer {
playVlc(fileName) {
console.log(`Playing VLC file. Name: ${fileName}`); // 播放 VLC 文件
}
playMp4(fileName) {
console.log(`Playing MP4 file. Name: ${fileName}`); // 播放 MP4 文件
}
}
// 定義一個(gè)適配器類
class MediaAdapter extends MediaPlayer {
constructor(audioType, advancedMediaPlayer) {
super();
this.audioType = audioType; // 保存音頻類型
this.advancedMediaPlayer = advancedMediaPlayer; // 保存高級(jí)媒體播放器對(duì)象
}
play(audioType, fileName) {
if (audioType === "vlc") {
this.advancedMediaPlayer.playVlc(fileName); // 調(diào)用高級(jí)媒體播放器的 playVlc 方法
} else if (audioType === "mp4") {
this.advancedMediaPlayer.playMp4(fileName); // 調(diào)用高級(jí)媒體播放器的 playMp4 方法
}
}
}
// 使用適配器模式
const mediaPlayer = new MediaPlayer(); // 創(chuàng)建媒體播放器對(duì)象
const advancedMediaPlayer = new AdvancedMediaPlayer(); // 創(chuàng)建高級(jí)媒體播放器對(duì)象
const mediaAdapter = new MediaAdapter("vlc", advancedMediaPlayer); // 創(chuàng)建適配器對(duì)象
mediaAdapter.play("vlc", "song.vlc"); // 通過適配器播放 VLC 文件
代碼注釋
-
MediaPlayer
是媒體播放器接口,定義了一個(gè)play
方法。 -
AdvancedMediaPlayer
是高級(jí)媒體播放器接口,定義了playVlc
和playMp4
方法。 -
MediaAdapter
是適配器類,通過繼承MediaPlayer
并保存一個(gè)AdvancedMediaPlayer
對(duì)象,將高級(jí)媒體播放器的接口適配到媒體播放器的接口。 - 通過適配器模式,可以讓客戶端代碼(
MediaPlayer
)調(diào)用高級(jí)媒體播放器的方法,實(shí)現(xiàn)接口的兼容。
流程圖
graph TD
A[開始] --> B[創(chuàng)建MediaPlayer實(shí)例]
B --> C[創(chuàng)建AdvancedMediaPlayer實(shí)例]
C --> D[創(chuàng)建適配器實(shí)例]
D --> E[調(diào)用適配器的play方法]
E --> F{判斷音頻類型}
F -- vlc --> G[調(diào)用playVlc方法]
F -- mp4 --> H[調(diào)用playMp4方法]
G --> I[輸出結(jié)果]
H --> I
I --> J[結(jié)束]
9. 模板方法模式(Template Method Pattern)
詳細(xì)說明
模板方法模式定義了一個(gè)操作中的算法的框架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變算法的結(jié)構(gòu)即可重定義算法的某些特定步驟。想象一下,你有一個(gè)烤面包機(jī),它有一個(gè)固定的烤面包流程(預(yù)熱、烤面包、冷卻),但你可以通過更換不同的面包片來(lái)改變烤面包的結(jié)果。模板方法模式的核心在于通過定義一個(gè)算法框架,讓子類可以在不改變框架的情況下,實(shí)現(xiàn)具體的步驟。
代碼示例
// 定義一個(gè)抽象類
class AbstractClass {
templateMethod() {
this.baseOperation1(); // 執(zhí)行基礎(chǔ)操作1
this.requiredOperations1(); // 執(zhí)行需要子類實(shí)現(xiàn)的操作1
this.baseOperation2(); // 執(zhí)行基礎(chǔ)操作2
this.hook1(); // 執(zhí)行鉤子方法1
this.requiredOperations2(); // 執(zhí)行需要子類實(shí)現(xiàn)的操作2
this.baseOperation3(); // 執(zhí)行基礎(chǔ)操作3
this.hook2(); // 執(zhí)行鉤子方法2
}
baseOperation1() {
console.log("AbstractClass says: I am doing the bulk of the work"); // 基礎(chǔ)操作1
}
baseOperation2() {
console.log("AbstractClass says: But I let subclasses alter some specific steps"); // 基礎(chǔ)操作2
}
baseOperation3() {
console.log("AbstractClass says: But I am doing the bulk of the work anyway"); // 基礎(chǔ)操作3
}
hook1() {} // 鉤子方法1
hook2() {} // 鉤子方法2
}
// 定義一個(gè)具體的子類
class ConcreteClass1 extends AbstractClass {
requiredOperations1() {
console.log("ConcreteClass1 says: Implemented Operation1"); // 子類實(shí)現(xiàn)的操作1
}
requiredOperations2() {
console.log("ConcreteClass1 says: Implemented Operation2"); // 子類實(shí)現(xiàn)的操作2
}
}
// 定義另一個(gè)具體的子類
class ConcreteClass2 extends AbstractClass {
requiredOperations1() {
console.log("ConcreteClass2 says: Implemented Operation1"); // 子類實(shí)現(xiàn)的操作1
}
requiredOperations2() {
console.log("ConcreteClass2 says: Implemented Operation2"); // 子類實(shí)現(xiàn)的操作2
}
hook1() {
console.log("ConcreteClass2 says: Overridden Hook1"); // 子類重寫的鉤子方法1
}
}
// 使用模板方法模式
const template1 = new ConcreteClass1(); // 創(chuàng)建具體子類1
template1.templateMethod(); // 調(diào)用模板方法
const template2 = new ConcreteClass2(); // 創(chuàng)建具體子類2
template2.templateMethod(); // 調(diào)用模板方法
代碼注釋
-
AbstractClass
是抽象類,定義了一個(gè)模板方法templateMethod
,它按順序調(diào)用一系列操作,包括基礎(chǔ)操作和需要子類實(shí)現(xiàn)的操作。 -
ConcreteClass1
和ConcreteClass2
是具體的子類,分別實(shí)現(xiàn)了requiredOperations1
和requiredOperations2
方法。 - 子類可以通過重寫鉤子方法(如
hook1
和hook2
)來(lái)擴(kuò)展模板方法的行為。 - 模板方法模式允許子類在不改變算法結(jié)構(gòu)的情況下,實(shí)現(xiàn)具體的步驟。
流程圖
graph TD
A[開始] --> B[創(chuàng)建抽象類]
B --> C[定義模板方法]
C --> D[調(diào)用基礎(chǔ)操作]
D --> E[調(diào)用子類實(shí)現(xiàn)的操作]
E --> F[調(diào)用鉤子方法]
F --> G[結(jié)束]
10. 建造者模式(Builder Pattern)
詳細(xì)說明
建造者模式將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。想象一下,你正在建造一座房子,你需要逐步完成不同的步驟(如打地基、建墻壁、安裝門窗)。建造者模式的核心在于通過一個(gè)建造者對(duì)象逐步構(gòu)建復(fù)雜對(duì)象,隱藏具體的構(gòu)建過程,讓客戶端只需要關(guān)心最終的結(jié)果。
代碼示例
// 定義一個(gè)復(fù)雜對(duì)象
class Car {
constructor() {
this.type = null; // 車型
this.seats = null; // 座位數(shù)
this.engine = null; // 發(fā)動(dòng)機(jī)類型
this.tripComputer = null; // 是否有行程計(jì)算機(jī)
this.gps = null; // 是否有 GPS
}
}
// 定義一個(gè)建造者類
class CarBuilder {
constructor() {
this.car = new Car(); // 創(chuàng)建一個(gè) Car 實(shí)例
}
setType(type) {
this.car.type = type; // 設(shè)置車型
return this; // 返回當(dāng)前對(duì)象,支持鏈?zhǔn)秸{(diào)用
}
setSeats(seats) {
this.car.seats = seats; // 設(shè)置座位數(shù)
return this; // 返回當(dāng)前對(duì)象,支持鏈?zhǔn)秸{(diào)用
}
setEngine(engine) {
this.car.engine = engine; // 設(shè)置發(fā)動(dòng)機(jī)類型
return this; // 返回當(dāng)前對(duì)象,支持鏈?zhǔn)秸{(diào)用
}
setTripComputer(tripComputer) {
this.car.tripComputer = tripComputer; // 設(shè)置行程計(jì)算機(jī)
return this; // 返回當(dāng)前對(duì)象,支持鏈?zhǔn)秸{(diào)用
}
setGPS(gps) {
this.car.gps = gps; // 設(shè)置 GPS
return this; // 返回當(dāng)前對(duì)象,支持鏈?zhǔn)秸{(diào)用
}
build() {
return this.car; // 返回構(gòu)建好的 Car 實(shí)例
}
}
// 使用建造者模式
const carBuilder = new CarBuilder(); // 創(chuàng)建建造者對(duì)象
const car = carBuilder
.setType("SUV") // 設(shè)置車型為 SUV
.setSeats(4) // 設(shè)置座位數(shù)為 4
.setEngine("V6") // 設(shè)置發(fā)動(dòng)機(jī)類型為 V6
.setTripComputer(true) // 設(shè)置行程計(jì)算機(jī)為 true
.setGPS(true) // 設(shè)置 GPS 為 true
.build(); // 構(gòu)建汽車
console.log(car); // 輸出構(gòu)建好的汽車對(duì)象
代碼注釋
-
Car
是一個(gè)復(fù)雜對(duì)象,包含多個(gè)屬性(如車型、座位數(shù)、發(fā)動(dòng)機(jī)類型等)。 -
CarBuilder
是建造者類,通過一系列的設(shè)置方法(如setType
、setSeats
等)逐步構(gòu)建Car
對(duì)象。 - 每個(gè)設(shè)置方法都返回當(dāng)前建造者對(duì)象,支持鏈?zhǔn)秸{(diào)用,最后通過
build
方法返回構(gòu)建好的Car
對(duì)象。 - 建造者模式隱藏了構(gòu)建過程的細(xì)節(jié),讓客戶端只需要關(guān)心最終的結(jié)果。
流程圖
graph TD
A[開始] --> B[創(chuàng)建建造者實(shí)例]
B --> C[設(shè)置類型]
C --> D[設(shè)置座位數(shù)]
D --> E[設(shè)置發(fā)動(dòng)機(jī)]
E --> F[設(shè)置行程計(jì)算機(jī)]
F --> G[設(shè)置GPS]
G --> H[構(gòu)建汽車]
H --> I[結(jié)束]
希望這些詳細(xì)的說明和代碼注釋能幫助你更好地理解這些設(shè)計(jì)模式!
目錄:總目錄
上篇文章:紅寶書第十一講:超易懂版「ES6類與繼承」零基礎(chǔ)教程:用現(xiàn)實(shí)例子+圖解實(shí)現(xiàn)
腳注
-
《JavaScript高級(jí)程序設(shè)計(jì)(第5版)》索引的"factory pattern"驗(yàn)證工廠模式的代碼組織形式。 ? ?
-
《JavaScript高級(jí)程序設(shè)計(jì)(第5版)》通過Person類與.prototype的關(guān)聯(lián)說明原型共享機(jī)制。 ? ?
-
《JavaScript高級(jí)程序設(shè)計(jì)(第5版)》指出代碼可維護(hù)性的核心在于避免冗余,與原型設(shè)計(jì)理念一致。 ? ?