工廠模式是最常見的一種創建型設計模式,通常說的工廠模式指的是工廠方法模式,是使用頻率最高的工廠模式。簡單工廠模式又稱為靜態工廠方法模式,不屬于GoF 23種設計模式,它屬于類創建型模式,是其它工廠模式的入門。
ONEGAME游戲公司計劃開發一條游戲生產線,該生產線可以向玩家提供不同類型的游戲,例如:RGP游戲、MMORGP游戲、MOBA游戲以及FPS游戲等。為了提供這些游戲,游戲公司需要創建一個游戲工廠,來創建這些游戲的實例。
ONEGAME游戲公司提出了初始設計方案,就是將所有類型的游戲的實現代碼封裝到一個Game類中,然后通過Game工廠來創建實例。實現代碼如下:
class Game{ private type: string;//游戲類別 constructor(type: string, data: any) { this.type = type; if(type.toLocaleLowerCase() === 'fps'){ // 初始化FPS游戲 }else if(type.toLocaleLowerCase() === 'rpg'){ // 初始化RPG游戲 }else if(type.toLocaleLowerCase() === 'moba'){ // 初始化MOBA游戲 } } play(){ if(this.type.toLocaleLowerCase() === 'fps'){ // 玩FPS游戲 }else if(this.type.toLocaleLowerCase() === 'rpg'){ // 玩RPG游戲 }else if(this.type.toLocaleLowerCase() === 'moba'){ // 玩MOBA游戲 } }}
上面的代碼實現了游戲的創建和玩游戲的功能,但是這樣的設計存在以下問題:
為了解決上面的問題,我們可以對Game類進行重構,將其拆分成多個游戲類,每個游戲類只負責自己的初始化和玩游戲的功能,這樣就可以避免代碼臃腫和違反單一職責原則的問題。但是這樣做還是無法解決對象創建和使用無法分離的問題,我們可以通過簡單工廠模式來解決這個問題。
簡單工廠的設計思想就是,將創建不同對象的相關的代碼封裝到不同的類中,即具體產品類,這樣就可以避免代碼的臃腫和違反單一職責原則的問題。將它們的公共代碼抽象到和封裝到一個抽象產品類中,每個具體類都是抽象產品類的子類。然后通過一個工廠類來創建這些具體產品類的實例,通過傳入的參數不同創建對應的具體產品對象。
簡單工廠模式:定義一個工廠類,通過傳入參數來創建不同的具體產品類的實例,被創建的實例都具有共同的父類。
簡單工廠模式結構包括三個角色:
使用簡單工廠模式優化上面的代碼,以實現一個游戲工廠為為例,實現可以生產不同類型的游戲為目的。首先定義一個抽象產品類Game,然后定義具體產品類FPSGame、RPGGame、MOBAGame,最后定義一個工廠類GameFactory,通過傳入不同的參數來創建不同的游戲實例。
// 游戲接口:抽象產品類interface Game { play(): void;}// 各種游戲的具體實現類:具體產品類// FPS游戲class FPSGame implements Game{ play() { console.log('FPS游戲'); }}// RPG游戲class RPGGame implements Game { play() { console.log('RPG游戲'); }}// MOBA游戲class MOBAGame implements Game { play() { console.log('MOBA游戲'); }}// 游戲工廠:創建具體產品類的實例的工廠類class GameFactory { static createGame(type: string): Game { this.type = type; switch (this.type) { case 'RPG': return new RPGGame(); case 'MOBA': return new MOBAGame(); case 'FPS': return new FPSGame(); default: throw new Error('Unknown game type'); } }}
用戶實際使用創建對應的游戲:
// 獲取RGP游戲const rgpGame = GameFactory.createGame('RPG');rgpGame.play();// 獲取MOBA游戲const mobaGame = GameFactory.createGame('MOBA');mobaGame.play();
在實際使用中,客戶端代碼只需要傳入類型參數,就可以獲取得到對應的游戲對象,而不需要關系對象的具體實現。這就符合簡單工廠模式的設計思想。
在上面的實現中,工廠類的創建方法返回的是Game接口類型,缺點是客戶端得到的對象類型信息不全,對此可以使用泛型來改進:
// 游戲接口:抽象產品類interface Game { play(): void;}class FPSGame implements Game { //...}class RPGGame implements Game { //...}class MOBAGame implements Game { //...}class GameFactory{ static createGame<T extends Game>(type: string): T{ //... }}
這樣在客戶端代碼得到的對象類型信息更加準確。
const rgpGame = GameFactory.createGame<RPGGame>('RPG');// rgpGame的類型是RPGGame,而不是Game
上面的代碼中,所有的產品類都需要實現 Game 接口,這樣會存在代碼重復的問題。我們可以引入一個泛型接口 IGame來改進:
interface IGame<T> { play(): void; info(): T; }class RPGGame implements IGame<string> { play() { // ... } info() { return 'RPG'; }}class MOBAGame implements IGame<string> { play() { // ... } info() { return 'MOBA'; }}class FPSGame implements IGame<string> { // ...}
這樣每個產品類就可以定制自己的 info 方法返回值類型了。
上面的代碼還存在問題:所有產品類都需要實現 play 方法,這會導致重復代碼。我們可以使用抽象類來解決這個問題:
abstract class GameBase { play() { // 默認游戲邏輯 } }class RPGGame extends GameBase implements IGame<string> { info() { return 'RPG'; }}class MOBAGame extends GameBase implements IGame<string> { // ...}class FPSGame extends GameBase implements IGame<string> { // ...}
這樣產品類就不需要重復實現 play 方法了,只需要繼承 GameBase 并實現 info 方法即可。
上面的代碼中,工廠類的創建方法需要傳入一個類型參數,這樣會導致客戶端代碼需要知道具體的類型參數,這樣就會破壞簡單工廠模式的封裝性。我們可以使用配置文件來解決這個問題:
class GameConfig { static gameTypes = { 'RPG': RPG, 'MOBA': MOBA, 'FPS': FPS }}
工廠類讀取配置創建對象:
class GameFactory { static createGame(type: string) { const Constructor = GameConfig.gameTypes[type]; if (!Constructor) { throw new Error('Unknown type'); } return new Constructor(); }}
這樣當需要新增游戲類型時,只需要在配置類中添加新的類型和類即可,工廠類的代碼無需修改。
我們還可以通過依賴注入進一步解耦:
@injectable()class GameFactory { constructor( @inject(GameConfig.gameTypes.RPG) private rpgGame: Game, @inject(GameConfig.gameTypes.MOBA) private mobaGame: Game, @inject(GameConfig.gameTypes.FPS) private fpsGame: Game ) {} createGame(type: string) { switch(type) { // ... } }}
這樣工廠類不再負責創建對象,而是通過注入的方式獲取對象實例,大大提升了靈活性。
下面是使用 TypeScript 深入解析簡單工廠模式的示例,通過工廠類和產品類的抽象與解耦,可以實現創建對象邏輯的集中和優化,提高代碼的靈活性和擴展性。TypeScript 通過接口、泛型和抽象類等特性增強了簡單工廠模式的實現。掌握設計模式對編寫優雅可擴展的 TypeScript 代碼很有幫助。
// 游戲接口interface Game { play(): void;}// 泛型游戲接口 interface IGame<T> { play(): void; info(): T;}// 抽象游戲類abstract class GameBase { play() { console.log('Playing game...'); }}// RPG游戲類class RPG extends GameBase implements IGame<string> { info() { return 'RPG'; }}// MMORPG游戲類 class MMORPG extends GameBase implements IGame<string> { info() { return 'MMORPG'; }}// FPS游戲類class FPS extends GameBase implements IGame<string> { info() { return 'FPS'; }}// 配置類class GameConfig { static gameTypes = { 'RPG': RPG, 'MMORPG': MMORPG, 'FPS': FPS }}// 工廠類class GameFactory { static createGame(type: string) { const Constructor = GameConfig.gameTypes[type]; if (!Constructor) { throw new Error('Unknown type'); } return new Constructor(); }}// 客戶端const rpgGame = GameFactory.createGame<RPG>('RPG');rpgGame.play();console.log(rpgGame.info());const fpsGame = GameFactory.createGame<FPS>('FPS');fpsGame.play();console.log(fpsGame.info());
簡單工廠模式是一種創建對象的設計模式,它通過工廠類來創建產品對象,主要目的是將對象創建的過程封裝起來,便于管理和維護。
而單例模式是一種確保某個類只有一個實例的設計模式,它的目的是在整個軟件系統中,對某個類只創建一個對象實例,避免浪費資源。
簡單工廠模式是通過工廠類的靜態方法創建對象實例,可以創建多個實例。
單例模式是在類中定義一個靜態變量保存單例實例,并通過一個靜態方法來獲取這個實例,確保只創建一個實例。
簡單工廠模式用于創建同一類產品的不同對象實例,客戶端無需知道具體產品類的類名。
單例模式用于創建對唯一實例有需求的對象,如線程池、緩存、日志對象等。
小結一下,簡單工廠模式關注創建不同實例,單例模式關注如何只創建一個實例。二者解決的問題和應用場景不同,但可以結合使用,工廠類可以返回單例對象。
通過上面的示例,我們使用 TypeScript 從多個方面對簡單工廠模式進行了深入解析,包括:
簡單工廠模式的優點:
簡單工廠模式的缺點:
簡單工廠模式通過工廠類和產品類的解耦,可以實現創建對象邏輯的集中化和優化,是非常常用和靈活的一種設計模式。TypeScript 通過接口、泛型和抽象類等特性,可以更優雅地實現簡單工廠模式,提高代碼的復用性和擴展性。
本文鏈接:http://www.www897cc.com/showinfo-26-73-0.html三言兩語說透設計模式的藝術-簡單工廠模式
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com