概念
在面向對象中,工廠表示一個負責創建其他類型對象的類。
工廠具有:
- 鬆耦合
- 客戶端無需瞭解創建對象的類,但是照樣可以使用它來創建對象。
- 可以輕鬆的在工廠中添加其他類來創建其他類型的對象。
工廠模式有3種變體:
- 簡單工廠:允許接口創建對象,但不會暴露對象的創建邏輯。
- 工廠方法:允許接口創建對象,但使用哪個類來創建對象是交給子類決定。
- 抽象工廠:抽象工廠是一個能夠創建一系列相關對象而無需指定其具體類的接口。它可以提供其他工廠的對象,在內部創建其他對象。
實現
簡單工廠
- 工廠根據傳入的參數,返回或者創建不同的對象
# ABCMeta是Python的特殊的元類,用來生成抽象類
from abc import ABCMeta, abstractmethod
# 動物類,定義say方法,但不實現
class Animal(metaclass=ABCMeta):
@abstractmethod
def say(self):
pass
# 狗類,繼承動物,重寫say方法
class Dog(Animal):
def say(self):
print('i am dog')
# 貓類,繼承動物,重寫say方法
class Cat(Animal):
def say(self):
print('i am cat')
# 工廠類
class ForestFactory(object):
# say方法的統一接口,傳入子類對象,調用他們的say方法
def say_something(self, object_type):
return eval(object_type)().say()
if __name__ == '__main__':
ff = ForestFactory()
animal = 'Cat'
ff.say_something(animal)
'''結果
i am cat'''
工廠方法
- 定義一個接口來創建對象,工廠本身並不創建對象,而交給子類完成,子類決定要實例化哪些類。
- Factory方法的創建時通過繼承而不是通過實例化。
- 工廠方法更加具有可定製性,它可以返回相同的實例或者子類,而不是某種類型的對象。
假設每個頁面都有一塊區域顯示個人信息,但是內容不同,設計代碼如下:
from abc import ABCMeta, abstractmethod
# 一個區表示哪方面內容,抽象的
class Section(metaclass=ABCMeta):
@abstractmethod
def describe(self):
pass
# 接下來創建多個區域,用來分別顯示不同的區域(簡化只打印出來)
# 個人區
class PersonalSection(Section):
def describe(self):
print('personal section')
# 音樂部分
class AlbumSection(Section):
def describe(self):
print('album')
# 專利部分
class PatentSection(Section):
def describe(self):
print('patent')
# 出版部分
class PublicationSection(Section):
def describe(self):
print('publication')
# 接下來創建抽象類,提供工廠方法create_profile
class Profile(metaclass=ABCMeta):
def __init__(self):
self.sections = []
self.create_profile()
@abstractmethod
def create_profile(self):
pass
def get_sections(self):
return self.sections
def add_sections(self, section):
self.sections.append(section)
# 接下來創建兩個具體實現工廠方法的子類
class ConcreteCreator1(Profile):
def create_profile(self):
# 添加個人區域、專利區域、出版區域
self.add_sections(PersonalSection())
self.add_sections(PatentSection())
self.add_sections(PublicationSection())
class ConcreteCreator2(Profile):
def create_profile(self):
# 添加個人區域、音樂區域
self.add_sections(PersonalSection())
self.add_sections(AlbumSection())
if __name__ == '__main__':
# 要創建ConcreteCreator1這個對象
profile_type = 'ConcreteCreator1'
profile = eval(profile_type)()
print(type(profile).__name__)
print(profile.get_sections())
'''結果
ConcreteCreator1
[<__main__.PersonalSection object at 0x10a052358>, <__main__.PatentSection object at 0x10a052390>, <__main__.PublicationSection object at 0x10a0523c8>]'''
工廠方法優點
- 更好的靈活性,代碼更通用,因爲他不是單純的實例某個類,而是取決於接口
- 鬆耦合,創建對象的代碼和使用它的代碼是分開的,添加新類更容易。
抽象工廠
- 抽象工廠主要目的是提供一個接口來創建一系列相關對象,而無需指定具體的類。
- 相比於之前的需要我們去指定創建什麼對象,抽象工廠不需要。
假設一個披薩店提供多種披薩,對應的抽象工廠:
'''抽象工廠:
1.抽象工廠主要目的是提供一個接口來創建一系列相關對象,而無需指定具體的類。
2.相比於之前的需要我們去指定創建什麼對象,抽象工廠不需要。'''
from abc import ABCMeta, abstractmethod
class PizzaFactory(metaclass=ABCMeta):
# 有蔬菜的披薩
@abstractmethod
def create_veg_pizza(self):
pass
# 沒蔬菜的披薩
@abstractmethod
def create_non_veg_pizza(self):
pass
class USAPizzaFactory(PizzaFactory):
# USA披薩店裏有蔬菜的披薩是玉米披薩
def create_veg_pizza(self):
return CornPizza()
# USA店裏沒蔬菜的披薩是牛肉披薩
def create_non_veg_pizza(self):
return BeefPizza()
class ChinaPizzaFactory(PizzaFactory):
# 中國披薩店裏有蔬菜的披薩是水果披薩
def create_veg_pizza(self):
return FruitsPizza()
# 中國披薩店裏沒有蔬菜的披薩是羊肉披薩
def create_non_veg_pizza(self):
return MuttonPizza()
# 接下來定義4種披薩和他們的父類(有蔬菜和無蔬菜)
class VegPizza(metaclass=ABCMeta):
@abstractmethod
def prepare(self, veg_pizza):
pass
# 沒蔬菜的披薩在有蔬菜的披薩上面加肉就可以
class NonVegPizza(metaclass=ABCMeta):
@abstractmethod
def serve(self, veg_pizza):
pass
class CornPizza(VegPizza):
def prepare(self):
print(type(self).__name__, '來了')
class BeefPizza(NonVegPizza):
def serve(self, veg_pizza):
print(type(self).__name__, '來了,牛肉是加在', type(veg_pizza).__name__,'裏面的')
class FruitsPizza(VegPizza):
def prepare(self):
print(type(self).__name__, '來了')
class MuttonPizza(NonVegPizza):
def serve(self, veg_pizza):
print(type(self).__name__, '來了,羊肉是加在', type(veg_pizza).__name__,'裏面的')
class PizzaStore(object):
def __init__(self):
pass
def make_pizzas(self):
# 創建的是所有對象(所有披薩),而不是單個指定對象
for factory in [USAPizzaFactory(), ChinaPizzaFactory()]:
self.factory = factory
self.non_veg_pizza = self.factory.create_non_veg_pizza()
self.veg_pizza = self.factory.create_veg_pizza()
# 調用
self.veg_pizza.prepare()
self.non_veg_pizza.serve(self.veg_pizza)
pizza = PizzaStore()
pizza.make_pizzas()
工廠方法和抽象工廠的比較
工廠方法 | 抽象工廠 |
---|---|
開放一個創建對象的方法 | 包含一個或多個工廠方法來創建一系列對象 |
使用繼承和子類來決定創建什麼對象 | 實用組合將創建對象的任務交給其他類 |
創建一個產品 | 創建相關係列(一羣組)產品 |