1仰猖、工廠模式
所謂工廠模式芬探,就是作為一個(gè)工廠的類有一個(gè)對(duì)象以及與它關(guān)聯(lián)的多個(gè)方法,客戶端使用某些參數(shù)調(diào)用此方法厘惦,之后偷仿,工廠會(huì)據(jù)此創(chuàng)建所需類型的對(duì)象哩簿,然后將他們返回給客戶端。
客戶端也可以創(chuàng)建對(duì)象酝静,那為什么需要工廠的存在呢节榜?
因?yàn)楣S擁有以下優(yōu)點(diǎn):
- 松耦合,即對(duì)象的創(chuàng)建可以獨(dú)立于類的實(shí)現(xiàn)
- 對(duì)客戶端十分友好别智,只需知道傳遞的接口宗苍,方法和參數(shù),就可以創(chuàng)建所需的對(duì)象了
- 可以輕松的在工廠中添加其他類來(lái)創(chuàng)建其他類型的對(duì)象薄榛,而無(wú)需更改客戶端代碼讳窟。
- 工廠還可以重用現(xiàn)有對(duì)象。
下面介紹工廠模式的三個(gè)變體:
- 簡(jiǎn)單工廠模式:不符合開(kāi)閉原則敞恋,不屬于23種設(shè)計(jì)模式之一
- 工廠方法模式
- 抽象工廠模式
2丽啡、簡(jiǎn)單工廠模式
簡(jiǎn)單工廠模式(Simple Factory Pattern):又稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式硬猫。在簡(jiǎn)單工廠模式中补箍,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例。簡(jiǎn)單工廠模式專門(mén)定義一個(gè)類來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例啸蜜,被創(chuàng)建的實(shí)例通常都具有共同的父類坑雅。
from abc import ABCMeta, abstractclassmethod
class Animal(metaclass=ABCMeta):
@abstractclassmethod # 抽象類方法
def do_say(self):
pass
class Dog(Animal):
def do_say(self):
print('www')
class Cat(Animal):
def do_say(self):
print('mmm')
class ForestFactory:
def make_sound(self, object_type):
print('object_type的類型:',type(eval(object_type)))
return eval(object_type)().do_say()
if __name__ == '__main__':
ff = ForestFactory()
animal = input('輸入哪種動(dòng)物來(lái)叫?')
ff.make_sound(animal)
輸入哪種動(dòng)物來(lái)叫衬横?Cat
object_type的類型: <class 'abc.ABCMeta'>
mmm
如上裹粤,創(chuàng)建一個(gè)名為
Animal
的抽象產(chǎn)品。Animal是一個(gè)抽象的基類(ABCMeta是Python的特殊元類冕香,用來(lái)生成類Abstract)蛹尝,它帶有方法do_say()
。我們利用Animal接口創(chuàng)建了兩種產(chǎn)品(Cat和Dog)悉尾,并實(shí)現(xiàn)了do_say()
方法來(lái)提供這些動(dòng)物的相應(yīng)的叫聲突那。之后又為了給客戶端提供接口,創(chuàng)建了ForectFactory
類构眯,在類中寫(xiě)了一個(gè)方法make_sound()
愕难,用戶就可以將參數(shù)(Cat或Dog)傳給該方法,就能實(shí)現(xiàn)相應(yīng)的功能惫霸。
在看python實(shí)現(xiàn)代碼時(shí)遇到不少知識(shí)盲區(qū)猫缭,參考了下面幾篇博客:
Python內(nèi)置函數(shù)(19)——eval
Python裝飾器、metaclass壹店、abc模塊學(xué)習(xí)筆記
Python抽象類(abc模塊)
簡(jiǎn)單工廠模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
工廠類含有必要的判斷邏輯猜丹,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)產(chǎn)品類的實(shí)例,客戶端可以免除直接創(chuàng)建產(chǎn)品對(duì)象的責(zé)任硅卢,而僅僅“消費(fèi)”產(chǎn)品射窒;簡(jiǎn)單工廠模式通過(guò)這種做法實(shí)現(xiàn)了對(duì)責(zé)任的分割藏杖,它提供了專門(mén)的工廠類用于創(chuàng)建對(duì)象。
客戶端無(wú)須知道所創(chuàng)建的具體產(chǎn)品類的類名脉顿,只需要知道具體產(chǎn)品類所對(duì)應(yīng)的參數(shù)即可蝌麸,對(duì)于一些復(fù)雜的類名,通過(guò)簡(jiǎn)單工廠模式可以減少使用者的記憶量艾疟。
通過(guò)引入配置文件来吩,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產(chǎn)品類,在一定程度上提高了系統(tǒng)的靈活性蔽莱。缺點(diǎn)
由于工廠類集中了所有產(chǎn)品創(chuàng)建邏輯弟疆,一旦不能正常工作,整個(gè)系統(tǒng)都要受到影響碾褂。
使用簡(jiǎn)單工廠模式將會(huì)增加系統(tǒng)中類的個(gè)數(shù)兽间,在一定程序上增加了系統(tǒng)的復(fù)雜度和理解難度。
系統(tǒng)擴(kuò)展困難正塌,一旦添加新產(chǎn)品就不得不修改工廠邏輯嘀略,在產(chǎn)品類型較多時(shí),有可能造成工廠邏輯過(guò)于復(fù)雜乓诽,不利于系統(tǒng)的擴(kuò)展和維護(hù)帜羊。
簡(jiǎn)單工廠模式由于使用了靜態(tài)工廠方法,造成工廠角色無(wú)法形成基于繼承的等級(jí)結(jié)構(gòu)鸠天。
不符合開(kāi)閉原則讼育,當(dāng)需要?jiǎng)?chuàng)建新產(chǎn)品時(shí),需要修改其他已創(chuàng)建的類稠集。
工廠方法模式
在資料的搜尋過(guò)程中看到一個(gè)例子非常形象奶段,很容易理解,語(yǔ)法也沒(méi)上面的花里胡哨剥纷,雖然可能欠缺了點(diǎn)嚴(yán)謹(jǐn)性痹籍。
class LeiFeng():
def buy_rice(self):
pass
def sweep(self):
pass
class Student(LeiFeng):
def buy_rice(self):
print ('大學(xué)生幫你買米')
def sweep(self):
print ('大學(xué)生幫你掃地')
class Volunteer(LeiFeng):
def buy_rice(self):
print ('社區(qū)志愿者幫你買米')
def sweep(self):
print ('社區(qū)志愿者幫你掃地')
class LeiFengFactory():
def create_lei_feng(self):
pass
class StudentFactory(LeiFengFactory):
def create_lei_feng(self):
return Student()
class VolunteerFactory(LeiFengFactory):
def create_lei_feng(self):
return Volunteer()
if __name__ == '__main__':
myFactory = StudentFactory()
leifeng1 = myFactory.create_lei_feng()
leifeng1.buy_rice()
leifeng1.sweep()
解釋:它先創(chuàng)建了一個(gè)LeiFeng類,它可以買米晦鞋,掃地兩件事蹲缠,接下來(lái)由大學(xué)生和社區(qū)志愿者來(lái)繼承這個(gè)雷鋒精神,也有了這兩個(gè)優(yōu)點(diǎn)悠垛,之后线定,又創(chuàng)建了LeiFengFactory類,來(lái)培養(yǎng)(生產(chǎn))大學(xué)生和社區(qū)志愿者确买,讓大學(xué)生( StudentFactory)和社區(qū)志愿者(VolunteerFactory)繼承這個(gè)培養(yǎng)方案(LeiFengFactory)斤讥,在主函數(shù)中就可以通過(guò)調(diào)用大學(xué)生(StudentFactory)或社區(qū)志愿者(VolunteerFactory)類來(lái)創(chuàng)建實(shí)例,讓他們?nèi)プ龊檬拢?/p>
這個(gè)工廠方法模式相比于簡(jiǎn)單工廠方法的不同之處在于當(dāng)需要?jiǎng)?chuàng)建新的方法時(shí)湾趾,比如需要讓中學(xué)生也加入進(jìn)來(lái)周偎,那么就只需在新建兩個(gè)類即可抹剩,無(wú)需更改別的類撑帖,符合開(kāi)閉原則蓉坎。
class LeiFeng():
def buy_rice(self):
pass
def sweep(self):
pass
class Student(LeiFeng):
def buy_rice(self):
print ('大學(xué)生幫你買米')
def sweep(self):
print ('大學(xué)生幫你掃地')
class Volunteer(LeiFeng):
def buy_rice(self):
print ('社區(qū)志愿者幫你買米')
def sweep(self):
print ('社區(qū)志愿者幫你掃地')
# ------------新加的類---------------
class MiddleStudent(LeiFeng):
def buy_rice(self):
print('中學(xué)生幫你買米')
def sweep(self):
print ('中學(xué)生幫你掃地')
# ----------------------------------
class LeiFengFactory():
def create_lei_feng(self):
pass
class StudentFactory(LeiFengFactory):
def create_lei_feng(self):
return Student()
class VolunteerFactory(LeiFengFactory):
def create_lei_feng(self):
return Volunteer()
# ------------新加的類-----------------
class MiddleStudentFactory(LeiFengFactory):
def create_lei_feng(self):
return MiddleStudent()
# ----------------------------------
if __name__ == '__main__':
myFactory = StudentFactory()
leifeng1 = myFactory.create_lei_feng()
leifeng1.buy_rice()
leifeng1.sweep()
抽象工廠模式
通過(guò)工廠方法模式我們可以看到,當(dāng)各個(gè)方法一多胡嘿,如果為每個(gè)方法都創(chuàng)建一個(gè)類來(lái)對(duì)應(yīng)蛉艾,將會(huì)變得很復(fù)雜,所以引入了抽象的概念衷敌。工廠方法用于創(chuàng)建一個(gè)產(chǎn)品勿侯,那抽象工廠方法就是創(chuàng)建相關(guān)產(chǎn)品的系列,比如航空飛機(jī)缴罗,由多個(gè)工廠不同的零部件助琐,在運(yùn)輸?shù)浇M裝工廠組裝…………
下面以制作游戲?yàn)槔?/p>
在編寫(xiě)一款面向全年齡的游戲,游戲本身需要使用工廠方法進(jìn)行開(kāi)發(fā)面氓。但游戲也需要考慮不同年齡段玩家的需求和口味不同兵钮,所以需要為不同年齡段的玩家針對(duì)游戲進(jìn)行一定的修改。于是在用戶輸入年齡后舌界,運(yùn)行符合其年齡的要求的游戲掘譬。
class Frog:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print('{} the Frog encounters {} and {}!'.format(self,
obstacle, obstacle.action()))
class Bug:
def __str__(self):
return 'a bug'
def action(self):
return 'eats it'
class FrogWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Frog World -------'
def make_character(self):
return Frog(self.player_name)
def make_obstacle(self):
return Bug()
class Wizard:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print(
'{} the Wizard battles against {} and {}!'.format(
self,
obstacle,
obstacle.action()))
class Ork:
def __str__(self):
return 'an evil ork'
def action(self):
return 'kills it'
class WizardWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Wizard World -------'
def make_character(self):
return Wizard(self.player_name)
def make_obstacle(self):
return Ork()
class GameEnvironment:
def __init__(self, factory):
self.hero = factory.make_character()
self.obstacle = factory.make_obstacle()
def play(self):
self.hero.interact_with(self.obstacle)
def validate_age(name):
try:
age = input('Welcome {}. How old are you? '.format(name))
age = int(age)
except ValueError as err:
print("Age {} is invalid, please try again...".format(err))
return (False, err)
return (True, age)
if __name__ == '__main__':
name = input("Hello. What's your name? ")
valid_input = False
while not valid_input:
valid_input, age = validate_age(name)
game = FrogWorld if age < 18 else WizardWorld
environment = GameEnvironment(game(name))
environment.play()
Hello. What's your name? wayne
Welcome wayne. How old are you? 12
------ Frog World -------
wayne the Frog encounters a bug and eats it!
解釋:
1、通過(guò)一個(gè) GameEnvironment 去管理兩個(gè)游戲:FrogWorld呻拌、WizerdWorld 葱轩,根據(jù)用戶輸入的不同的年齡去決定其去玩那個(gè)游戲,是青蛙世界還是巫師世界呢藐握?
2靴拱、而每個(gè)游戲類即 FrogWorld、WizerdWorld 猾普,又分別管理著 兩個(gè)角色 --(Frog青蛙袜炕、Bug臭蟲(chóng))和(Wizerd巫師、Ork怪獸)
通過(guò)這種層層繼承的方式抬闷,將各個(gè)模塊管理好妇蛀,最終根據(jù)客戶端的輸入,來(lái)返回不同的游戲世界笤成。
其實(shí)無(wú)論是工廠模式和抽象工廠模式的思想都是一樣的评架,根據(jù)不同的輸入,調(diào)用相同的接口炕泳,得出不同的結(jié)果纵诞。其內(nèi)部封裝了操作流程,用戶無(wú)需知道其內(nèi)部如何其實(shí)現(xiàn)的如何進(jìn)行選擇培遵,只管輸入便可的得到結(jié)果浙芙。
參考自