三言兩語說透設計模式的藝術-原型模式
1提升對象創建效率:原型模式的智慧
原型模式(Prototype Pattern)是一種創造型設計模式,其核心思想在于通過復制“原型”來創建對象,而非直接實例化。在原型模式中,我們首先創造一個原型對象,接著通過對其進行復制,獲得新的實例。這些原型對象儲存在一個共享的“原型管理器”中,當需要新的對象時,只需從管理器獲取原型的復制。
2原型模式的實現
讓我們以一個具體場景為例,考慮開發一個鴨子養殖游戲。游戲中需要生成兩種鴨子:北京鴨和綠頭鴨。這兩種鴨子共享一些屬性和方法,如叫聲、游泳和渲染模型,但叫聲和渲染模型是不同的。
如果每次都從頭創建鴨子對象,將會產生大量冗余代碼,如下所示:
const duck1 = {
name: '鴨子1',
quack() {},
swim() {},
render() {}
};
const duck2 = {
name: '鴨子2',
quack() {},
swim() {},
render() {}
};
這種方式重復定義相同的 quack 和 swim 方法,效率低下。如果使用簡單工廠模式,代碼如下:
function createDuck(type) {
let duck;
if (type === 'beijing') {
duck = new BeijingDuck();
} else if (type === 'greenheaded') {
duck = new GreenHeadedDuck();
}
duck.swim = function() {
console.log('swimming...');
}
duck.render = function() {
console.log('render duck model...')
}
return duck;
}
const beijingDuck = createDuck('beijing');
const greenDuck = createDuck('greenheaded');
但是,這樣每次創建鴨子對象都需要重新定義 swim 和 render 方法,不夠高效。
更優雅的方式是將共有部分提取為一個原型:
// 鴨子原型
const DuckPrototype = {
swim() {},
render() {}
};
// 創建具體鴨子時復制原型
function createBeijingDuck() {
const duck = Object.create(DuckPrototype);
duck.quack = function() {}
return duck;
}
function createGreenDuck() {
const duck = Object.create(DuckPrototype);
duck.quack = function() {}
return duck;
}
以上代碼展示了如何在鴨子游戲中應用原型模式。通過原型模式,我們只需定義一次共有屬性和方法,然后通過復制原型來實例化新對象。這避免了重復代碼,提高了對象創建效率,并建立了對象之間的關系,方便基于原型進行擴展。
原型模式與相關模式的區別:
- 在工廠方法模式中,對象創建邏輯在工廠內部,而原型模式通過復制原型來創建對象。
- 單例模式生成唯一實例,而原型模式可以生成多個相似實例。
- 建造者模式適用于創建過程復雜的對象,而原型模式適用于對象實例化復雜的情況。
那么,如何使用 TypeScript 實現更優雅的原型模式呢?
// 原型接口
interface Prototype {
clone(): Prototype;
}
// 原型管理器類
class PrototypeManager {
private prototypes: {[key: string]: Prototype} = {};
// 省略方法定義
}
// 具體原型類
class ConcretePrototype implements Prototype {
clone() {
return Object.create(this);
}
}
// 使用
// 初始化原型管理器
const manager = new PrototypeManager();
// 設置原型對象
manager.set('proto', new ConcretePrototype());
// 請求原型復制實例化
const prototypeInstance = manager.get('proto').clone();
以上代碼定義了原型接口、原型管理器類和具體原型類,還提供了簡單的使用示例。從示例中可以看出,原型模式的關鍵是通過原型管理器來復制原型對象從而創建實例。
3原型模式的優缺點
原型模式的主要優點包括:
- 提高實例化對象的效率:通過復制原型對象,避免了重復的初始化操作。
- 隱藏實例化的復雜度:客戶端不需要了解具體的對象創建過程,只需請求原型的復制即可。
- 避免構造函數污染:由于實例化是通過復制原型對象實現的,無需向構造函數中添加不必要的代碼。
- 動態添加和刪除原型:可以在運行時擴展或減少原型對象,客戶端可以直接使用新增的原型來實例化對象。
原型模式的缺點包括:
- 需要定義接口并確保每個具體原型類都實現了該接口,增加了一定的開發成本。
- 需要注意原型實例和原型之間的關系,例如修改原型會影響到其他實例。
4應用場景
原型模式在以下場景中常見應用:
- 對象創建成本較高:通過復制原型來提高效率,例如在游戲中創建怪物對象。
- 系統需要產生預定義原型的實例:例如用于測試的假日期對象或假支付賬單對象。
- 類的初始化需要參數:通過原型模式避免構造函數過于復雜。
- 需要動態地創建復雜對象:客戶端只需要關注如何獲取對象,無需了解具體創建過程。
以下是一些建議,在前端開發中更適合使用原型模式的情況:
1)創建組件類的原型:通過創建組件類的原型對象,使用 Object.create() 方法基于該原型快速創建組件實例,從而避免重復編寫組件邏輯。
// 原型類
const ComponentPrototype = {
// 組件邏輯
render() {
// ...
}
};
// 基于原型創建實例
const ComponentInstance = Object.create(ComponentPrototype);
2)復用和配置原型對象:設計一些可配置的原型對象,如 API 配置對象,然后復制和配置這些原型來生成不同的實例,避免重復創建。
// 可配置的原型
const APIConfigPrototype = {
baseURL: 'https://example.com',
timeout: 1000,
};
// 復制原型并配置生成實例
const config = Object.assign({}, APIConfigPrototype, {
timeout: 3000
});
3)代理原型對象:通過原型對象實現代理,避免每次訪問都重新創建目標對象。
// 原型作為代理對象
const proxyPrototype = {
target: null,
get(key) {
// 懶加載
if (!this.target) {
this.target = createTargetObject();
}
return this.target[key];
}
}
// 使用代理原型
const proxy = Object.create(proxyPrototype);
proxy.foo // 觸發代理邏輯
5原型模式與 JavaScript 中的 Prototype
原型模式和 JavaScript 中的原型繼承(prototype)存在以下主要區別:
- 原型模式是一種設計模式,是抽象的概念,而 JavaScript 中的 prototype 是一種具體的繼承實現機制。
- 原型模式的核心在于原型管理器,用于存儲和管理不同的原型對象。而 JavaScript 中的 prototype 屬性是函數的一個屬性,指向包含由該函數創建的對象共享的屬性和方法的對象。
- 原型模式通過復制原型對象來創建新的實例,原型與實例之間沒有必然的關系。而 JavaScript 中的原型繼承是建立原型和實例之間的關系,實例通過 proto 屬性關聯到原型,可以直接訪問原型的屬性和方法。
- 原型模式可以創建多個相似的對象實例,是一種對象復用的方式。而 JavaScript 中的原型繼承是實現類繼承的方式,通過 prototype 屬性建立原型鏈,主要用于實現對象的繼承和復用。
- 原型模式中,原型對象和實例對象通常相互隔離,修改實例不會影響到原型。而在 JavaScript 原型繼承中,修改原型會影響到所有實例,因為實例引用了原型。
- 原型模式可以動態注冊和刪除原型,而 JavaScript 中的原型關系是在首次實例化時建立的,之后不可更改。
綜上所述,原型模式是一種更抽象和全面的對象復用方式,而 JavaScript 中的原型繼承只是一種具體的繼承實現方式,它們在核心思想上有所不同。然而,它們在提高實例化效率方面具有一定的相似性。
6總結
原型模式是一種創造型設計模式,通過復制現有對象的原型來創建新對象,提高對象創建效率和復用性。核心思想是在一個原型對象的基礎上,通過復制來生成新實例,避免重復初始化。
在實際開發中,原型模式優化對象創建,通過共享原型對象避免重復定義屬性和方法,提高代碼效率。例如,前端可用于創建組件實例,避免重復編寫組件邏輯。
實現原型模式包括創建原型對象,然后通過復制原型創建新實例。原型對象通常存儲于原型管理器中,供需要創建對象時復制使用。
與JavaScript的原型繼承不同,原型模式更抽象靈活。它動態添加刪除原型,避免構造函數污染,隱藏實例化復雜性。注意每具體原型需實現接口,增加開發成本。
總之,原型模式通過復制對象的原型創建新對象,提高對象創建效率和復用性。頻繁創建相似對象場景下,原型模式是有用設計模式。