工廠模式是最常見(jiàn)的一種創(chuàng)建型設(shè)計(jì)模式,通常說(shuō)的工廠模式指的是工廠方法模式,是使用頻率最高的工廠模式。簡(jiǎn)單工廠模式又稱為靜態(tài)工廠方法模式,不屬于GoF 23種設(shè)計(jì)模式,它屬于類(lèi)創(chuàng)建型模式,是其它工廠模式的入門(mén)。
ONEGAME游戲公司計(jì)劃開(kāi)發(fā)一條游戲生產(chǎn)線,該生產(chǎn)線可以向玩家提供不同類(lèi)型的游戲,例如:RGP游戲、MMORGP游戲、MOBA游戲以及FPS游戲等。為了提供這些游戲,游戲公司需要?jiǎng)?chuàng)建一個(gè)游戲工廠,來(lái)創(chuàng)建這些游戲的實(shí)例。
ONEGAME游戲公司提出了初始設(shè)計(jì)方案,就是將所有類(lèi)型的游戲的實(shí)現(xiàn)代碼封裝到一個(gè)Game類(lèi)中,然后通過(guò)Game工廠來(lái)創(chuàng)建實(shí)例。實(shí)現(xiàn)代碼如下:
class Game{ private type: string;//游戲類(lèi)別 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游戲 } }}
上面的代碼實(shí)現(xiàn)了游戲的創(chuàng)建和玩游戲的功能,但是這樣的設(shè)計(jì)存在以下問(wèn)題:
為了解決上面的問(wèn)題,我們可以對(duì)Game類(lèi)進(jìn)行重構(gòu),將其拆分成多個(gè)游戲類(lèi),每個(gè)游戲類(lèi)只負(fù)責(zé)自己的初始化和玩游戲的功能,這樣就可以避免代碼臃腫和違反單一職責(zé)原則的問(wèn)題。但是這樣做還是無(wú)法解決對(duì)象創(chuàng)建和使用無(wú)法分離的問(wèn)題,我們可以通過(guò)簡(jiǎn)單工廠模式來(lái)解決這個(gè)問(wèn)題。
簡(jiǎn)單工廠的設(shè)計(jì)思想就是,將創(chuàng)建不同對(duì)象的相關(guān)的代碼封裝到不同的類(lèi)中,即具體產(chǎn)品類(lèi),這樣就可以避免代碼的臃腫和違反單一職責(zé)原則的問(wèn)題。將它們的公共代碼抽象到和封裝到一個(gè)抽象產(chǎn)品類(lèi)中,每個(gè)具體類(lèi)都是抽象產(chǎn)品類(lèi)的子類(lèi)。然后通過(guò)一個(gè)工廠類(lèi)來(lái)創(chuàng)建這些具體產(chǎn)品類(lèi)的實(shí)例,通過(guò)傳入的參數(shù)不同創(chuàng)建對(duì)應(yīng)的具體產(chǎn)品對(duì)象。
簡(jiǎn)單工廠模式:定義一個(gè)工廠類(lèi),通過(guò)傳入?yún)?shù)來(lái)創(chuàng)建不同的具體產(chǎn)品類(lèi)的實(shí)例,被創(chuàng)建的實(shí)例都具有共同的父類(lèi)。
簡(jiǎn)單工廠模式結(jié)構(gòu)包括三個(gè)角色:
使用簡(jiǎn)單工廠模式優(yōu)化上面的代碼,以實(shí)現(xiàn)一個(gè)游戲工廠為為例,實(shí)現(xiàn)可以生產(chǎn)不同類(lèi)型的游戲?yàn)槟康摹J紫榷x一個(gè)抽象產(chǎn)品類(lèi)Game,然后定義具體產(chǎn)品類(lèi)FPSGame、RPGGame、MOBAGame,最后定義一個(gè)工廠類(lèi)GameFactory,通過(guò)傳入不同的參數(shù)來(lái)創(chuàng)建不同的游戲?qū)嵗?span style="display:none">jlx28資訊網(wǎng)——每日最新資訊28at.com
// 游戲接口:抽象產(chǎn)品類(lèi)interface Game { play(): void;}// 各種游戲的具體實(shí)現(xiàn)類(lèi):具體產(chǎn)品類(lèi)// 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游戲'); }}// 游戲工廠:創(chuàng)建具體產(chǎn)品類(lèi)的實(shí)例的工廠類(lèi)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'); } }}
用戶實(shí)際使用創(chuàng)建對(duì)應(yīng)的游戲:
// 獲取RGP游戲const rgpGame = GameFactory.createGame('RPG');rgpGame.play();// 獲取MOBA游戲const mobaGame = GameFactory.createGame('MOBA');mobaGame.play();
在實(shí)際使用中,客戶端代碼只需要傳入類(lèi)型參數(shù),就可以獲取得到對(duì)應(yīng)的游戲?qū)ο螅恍枰P(guān)系對(duì)象的具體實(shí)現(xiàn)。這就符合簡(jiǎn)單工廠模式的設(shè)計(jì)思想。
在上面的實(shí)現(xiàn)中,工廠類(lèi)的創(chuàng)建方法返回的是Game接口類(lèi)型,缺點(diǎn)是客戶端得到的對(duì)象類(lèi)型信息不全,對(duì)此可以使用泛型來(lái)改進(jìn):
// 游戲接口:抽象產(chǎn)品類(lèi)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{ //... }}
這樣在客戶端代碼得到的對(duì)象類(lèi)型信息更加準(zhǔn)確。
const rgpGame = GameFactory.createGame<RPGGame>('RPG');// rgpGame的類(lèi)型是RPGGame,而不是Game
上面的代碼中,所有的產(chǎn)品類(lèi)都需要實(shí)現(xiàn) Game 接口,這樣會(huì)存在代碼重復(fù)的問(wèn)題。我們可以引入一個(gè)泛型接口 IGame來(lái)改進(jìn):
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> { // ...}
這樣每個(gè)產(chǎn)品類(lèi)就可以定制自己的 info 方法返回值類(lèi)型了。
上面的代碼還存在問(wèn)題:所有產(chǎn)品類(lèi)都需要實(shí)現(xiàn) play 方法,這會(huì)導(dǎo)致重復(fù)代碼。我們可以使用抽象類(lèi)來(lái)解決這個(gè)問(wèn)題:
abstract class GameBase { play() { // 默認(rèn)游戲邏輯 } }class RPGGame extends GameBase implements IGame<string> { info() { return 'RPG'; }}class MOBAGame extends GameBase implements IGame<string> { // ...}class FPSGame extends GameBase implements IGame<string> { // ...}
這樣產(chǎn)品類(lèi)就不需要重復(fù)實(shí)現(xiàn) play 方法了,只需要繼承 GameBase 并實(shí)現(xiàn) info 方法即可。
上面的代碼中,工廠類(lèi)的創(chuàng)建方法需要傳入一個(gè)類(lèi)型參數(shù),這樣會(huì)導(dǎo)致客戶端代碼需要知道具體的類(lèi)型參數(shù),這樣就會(huì)破壞簡(jiǎn)單工廠模式的封裝性。我們可以使用配置文件來(lái)解決這個(gè)問(wèn)題:
class GameConfig { static gameTypes = { 'RPG': RPG, 'MOBA': MOBA, 'FPS': FPS }}
工廠類(lèi)讀取配置創(chuàng)建對(duì)象:
class GameFactory { static createGame(type: string) { const Constructor = GameConfig.gameTypes[type]; if (!Constructor) { throw new Error('Unknown type'); } return new Constructor(); }}
這樣當(dāng)需要新增游戲類(lèi)型時(shí),只需要在配置類(lèi)中添加新的類(lèi)型和類(lèi)即可,工廠類(lèi)的代碼無(wú)需修改。
我們還可以通過(guò)依賴注入進(jìn)一步解耦:
@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) { // ... } }}
這樣工廠類(lèi)不再負(fù)責(zé)創(chuàng)建對(duì)象,而是通過(guò)注入的方式獲取對(duì)象實(shí)例,大大提升了靈活性。
下面是使用 TypeScript 深入解析簡(jiǎn)單工廠模式的示例,通過(guò)工廠類(lèi)和產(chǎn)品類(lèi)的抽象與解耦,可以實(shí)現(xiàn)創(chuàng)建對(duì)象邏輯的集中和優(yōu)化,提高代碼的靈活性和擴(kuò)展性。TypeScript 通過(guò)接口、泛型和抽象類(lèi)等特性增強(qiáng)了簡(jiǎn)單工廠模式的實(shí)現(xiàn)。掌握設(shè)計(jì)模式對(duì)編寫(xiě)優(yōu)雅可擴(kuò)展的 TypeScript 代碼很有幫助。
// 游戲接口interface Game { play(): void;}// 泛型游戲接口 interface IGame<T> { play(): void; info(): T;}// 抽象游戲類(lèi)abstract class GameBase { play() { console.log('Playing game...'); }}// RPG游戲類(lèi)class RPG extends GameBase implements IGame<string> { info() { return 'RPG'; }}// MMORPG游戲類(lèi) class MMORPG extends GameBase implements IGame<string> { info() { return 'MMORPG'; }}// FPS游戲類(lèi)class FPS extends GameBase implements IGame<string> { info() { return 'FPS'; }}// 配置類(lèi)class GameConfig { static gameTypes = { 'RPG': RPG, 'MMORPG': MMORPG, 'FPS': FPS }}// 工廠類(lèi)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());
簡(jiǎn)單工廠模式是一種創(chuàng)建對(duì)象的設(shè)計(jì)模式,它通過(guò)工廠類(lèi)來(lái)創(chuàng)建產(chǎn)品對(duì)象,主要目的是將對(duì)象創(chuàng)建的過(guò)程封裝起來(lái),便于管理和維護(hù)。
而單例模式是一種確保某個(gè)類(lèi)只有一個(gè)實(shí)例的設(shè)計(jì)模式,它的目的是在整個(gè)軟件系統(tǒng)中,對(duì)某個(gè)類(lèi)只創(chuàng)建一個(gè)對(duì)象實(shí)例,避免浪費(fèi)資源。
簡(jiǎn)單工廠模式是通過(guò)工廠類(lèi)的靜態(tài)方法創(chuàng)建對(duì)象實(shí)例,可以創(chuàng)建多個(gè)實(shí)例。
單例模式是在類(lèi)中定義一個(gè)靜態(tài)變量保存單例實(shí)例,并通過(guò)一個(gè)靜態(tài)方法來(lái)獲取這個(gè)實(shí)例,確保只創(chuàng)建一個(gè)實(shí)例。
簡(jiǎn)單工廠模式用于創(chuàng)建同一類(lèi)產(chǎn)品的不同對(duì)象實(shí)例,客戶端無(wú)需知道具體產(chǎn)品類(lèi)的類(lèi)名。
單例模式用于創(chuàng)建對(duì)唯一實(shí)例有需求的對(duì)象,如線程池、緩存、日志對(duì)象等。
小結(jié)一下,簡(jiǎn)單工廠模式關(guān)注創(chuàng)建不同實(shí)例,單例模式關(guān)注如何只創(chuàng)建一個(gè)實(shí)例。二者解決的問(wèn)題和應(yīng)用場(chǎng)景不同,但可以結(jié)合使用,工廠類(lèi)可以返回單例對(duì)象。
通過(guò)上面的示例,我們使用 TypeScript 從多個(gè)方面對(duì)簡(jiǎn)單工廠模式進(jìn)行了深入解析,包括:
簡(jiǎn)單工廠模式的優(yōu)點(diǎn):
簡(jiǎn)單工廠模式的缺點(diǎn):
簡(jiǎn)單工廠模式通過(guò)工廠類(lèi)和產(chǎn)品類(lèi)的解耦,可以實(shí)現(xiàn)創(chuàng)建對(duì)象邏輯的集中化和優(yōu)化,是非常常用和靈活的一種設(shè)計(jì)模式。TypeScript 通過(guò)接口、泛型和抽象類(lèi)等特性,可以更優(yōu)雅地實(shí)現(xiàn)簡(jiǎn)單工廠模式,提高代碼的復(fù)用性和擴(kuò)展性。
本文鏈接:http://www.tebozhan.com/showinfo-26-73-0.html三言兩語(yǔ)說(shuō)透設(shè)計(jì)模式的藝術(shù)-簡(jiǎn)單工廠模式
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com