【Python設計模式】03 工廠模式:建立建立物件的工廠

Evan Python發表於2018-10-19

三、工廠模式:建立建立物件的工廠

工廠模式可以說是最常用的設計模式
本章主要介紹建立型設計模式:工廠模式

本章主題

  • 瞭解簡單工廠設計模式
  • 討論工廠方法和抽象工廠方法及其差異
  • 利用Python 程式碼實現真實場景
  • 討論模式的優缺點並進行相應的比較

1. 瞭解工廠模式

物件導向程式設計中,‘工廠’ 表示一個負責建立其他型別物件的類

工廠與客戶端的關係:

  • 工廠的類建立一個物件以及與這個物件關聯的多個方法
  • 客戶端使用某些引數呼叫此方法
  • 工廠會據此建立所需型別的物件,然後將它們返回給客戶端

那麼問題是為什麼不能是客戶端直接建立物件呢?工廠的優點在於:

  • 鬆耦合,即物件的建立可以獨立於類的實現
  • 客戶端無需瞭解建立物件的類,只需要知道需要傳遞的介面、方法和引數,簡化了客戶端的實現
  • 輕鬆地在工廠中新增其他類來建立其他型別的物件,而這無需更改客戶端程式碼
  • 工廠可以重用現有物件。但是客戶端建立物件總是建立一個新的物件

Factory 設計模式有三種變體:

  • 簡單工廠模式:允許介面建立物件,但不會暴露物件的建立邏輯
  • 工廠方法模式:允許介面建立物件,但使用哪個類來建立物件,則交由子類決定
  • 抽象工廠模式:抽象工廠是一個能夠建立一系列相關物件而無需指定/公開其具體類的介面。該模式能夠提供其他工廠的物件,在其內部建立其他物件

2. 簡單工廠模式

工廠可以幫助開發人員建立不同型別的物件,而不是直接將物件例項化

from abc import ABCMeta, abstractmethod

class Animal(metaclass=ABCMeta):
	@abstractmethod
	def do_say(self):
		pass

class Dog(Animal):
	def do_say(self):
		print("Wong Wong!!")

class Cat(Animal):
	def do_say(self):
		print("Miao Miao!!")


## forest factory defined
class ForestFactory(object):
	def make_sound(self, object_type):
		return eval(object_type)().do_say()
	
## client code
if __name__ == '__main__':
	ff = ForestFactory()
	animal = input("Which animal should make_sound Dog or Cat?")
	ff.make_sound(animal)

執行結果:
Which animal should make_sound Dog or Cat?Cat
Miao Miao!!

3. 工廠方法模式

工廠方法模式要點:

  • 我們定義了一個介面來建立,但是工廠本身並不負責建立物件,而是將這個任務交由子類來完成,即子類決定了要例項化哪些類
  • 工廠方法的建立是通過繼承而不是通過例項化來完成的
  • 工廠方法使設計更加具有可定製性。它可以返回相同的例項或子類,而不是某些型別的物件(就像在簡單工廠方法中的那樣)

實現工廠方法:
假設我們想在不同型別的社交網站(例如:LinkedIn、Facebook)上為個人或公司建立簡介
那麼每個簡介都有某些特定的組成章節
LinkedIn: 個人專利或出版作品區 + 個人資訊區
Facebook: 在相簿中度假的照片區 + 個人資訊區
簡言之,我們通過將正確的區新增到相應的簡介中來建立不同型別的簡介

from abc import ABCMeta, abstractmethod


class Section(metaclass=ABCMeta):
	@abstractmethod
	def describe(self):
		pass


class PersonalSection(Section):
	def describe(self):
		print("個人資訊 Section")


class AlbumSection(Section):
	def describe(self):
		print("相簿 Section")


class PatentSection(Section):
	def describe(self):
		print("專利區 Section")


class PublicationSection(Section):
	def describe(self):
		print("出版作品 Section")

我們建立一個Profile抽象類,提供一個工廠方法createProfile(),建立相應的簡介
工廠方法類:只提供了介面,並沒有實際建立類
子類決定使用哪些區

class Profile(metaclass=ABCMeta):
	def __init__(self):
		self.sections = []
		self.createProfile()

	@abstractmethod
	def createProfile(self):
		pass

	def getSections(self):
		return self.sections

	def addSections(self, section):
		self.sections.append(section)	
	

class linkedin(Profile):
	def createProfile(self):
		self.addSections(PersonalSection())
		self.addSections(PatentSection())
		self.addSections(PublicationSection())


class facebook(Profile):
	def createProfile(self):
		self.addSections(PersonalSection())
		self.addSections(AlbumSection())


if __name__ == "__main__":
	profile_type = input("Which Profile you'd like to create? \
					[LinkedIn or FaceBook]")
	profile = eval(profile_type.lower())()
	print("Creating Profile..", type(profile).__name__)
	print("Profile has sections --", profile.getSections())

執行結果:
Which Profile you’d like to create? [LinkedIn or FaceBook]facebook
Creating Profile… facebook
Profile has sections – [<main.PersonalSection object at 0x7fb4c03bff28>,
<main.AlbumSection object at 0x7fb4c03bf780>]

可以發現:工廠方法裡面的工廠不建立類,只提供介面
facebook選定後,facebook類就被例項化,Profile中的__init__方法被呼叫,
–> __init__中的createProfile()方法被呼叫 --> addSections被呼叫,
–> Section列表的append()方法被呼叫

4. 工廠方法模式的優點

  • ta具有更大的靈活性,程式碼更加通用,因為它不是單純地例項化某個類。這樣實現哪些類取決於介面
  • 鬆耦合,因為建立物件的程式碼與使用它的程式碼是分開的。客戶端無需關心傳遞哪些引數以及需要例項化哪些類。由於新增新類更加容易,所以降低了維護成本

5. 抽象工廠模式

抽象工廠模式的主要目的是提供一個介面來建立一系列相關物件,而無需指定具體的類
抽象工廠的目標是建立一系列相關物件,而工廠方法模式將建立例項的任務交給了子類
實際上,抽象工廠模式不僅確保客戶端與物件的建立相互隔離,同時還確保客戶端能夠使用建立的物件
但是客戶端只能通過介面訪問物件

from abc import ABCMeta, abstractmethod

# 抽象工廠基類:pizza factory
class PizzaFactory(metaclass=ABCMeta):
	@abstractmethod
	def createVegPizza(self):
		pass
	
	@abstractmethod
	def createNonVegPizza(self):
		pass

# Indian pizza factory
class IndianPizzaFactory(PizzaFactory):
	def createVegPizza(self):
		return DeluxVeggiePizza()

	def createNonVegPizza(self):
		return ChickenPizza()

# USA pizza factory
class USPizzaFactory(PizzaFactory):
	def createVegPizza(self):
		return MexicanVegPizza()

	def createNonVegPizza(self):
		return HamPizza()


class VegPizza(metaclass=ABCMeta):
	@abstractmethod
	def prepare(self, VegPizza):
		pass


class NonVegPizza(metaclass=ABCMeta):
	@abstractmethod
	def serve(self, VegPizza):
		pass


class DeluxVeggiePizza(VegPizza):
	def prepare(self):
		print("Prepare ", type(self).__name__)


class ChickenPizza(NonVegPizza):
	def serve(self, VegPizza):
		print(type(self).__name__, "is served with Chicken on ", \
			type(VegPizza).__name__)


class MexicanVegPizza(VegPizza):
	def prepare(self):
		print("Prepare ", type(self).__name__)


class HamPizza(NonVegPizza):
	def serve(self, VegPizza):
		print(type(self).__name__, "is served with Ham on ", \
			type(VegPizza).__name__)


class PizzaStore:
	def __init__(self):
		pass
	
	def makePizza(self):
		for factory in [IndianPizzaFactory(), USPizzaFactory()]:
			self.factory = factory
			self.NonVegPizza = self.factory.createNonVegPizza()
			self.VegPizza = self.factory.createVegPizza()
			self.VegPizza.prepare()
			self.NonVegPizza.serve(self.VegPizza)


pizza = PizzaStore()
pizza.makePizza()

執行結果:
Prepare DeluxVeggiePizza
ChickenPizza is served with Chicken on DeluxVeggiePizza
Prepare MexicanVegPizza
HamPizza is served with Ham on MexicanVegPizza

解析:

  • 與工廠方法的主要區別是: 將工廠多了一層抽象,它建立的不是一個物件而是一類物件
  • 本例中,當建立工廠後,會同步建立蔬菜披薩和非蔬菜披薩兩個類

6. 工廠方法與抽象工廠方法的區別

工廠方法 抽象工廠方法
它向客戶端開放了一個建立物件的方法 抽象工廠包含了一個或多個工廠方法來建立一系列的相關物件
它使用繼承和子類來決定要建立哪個物件 它使用組合將建立物件的任務委託給其他類
工廠方法用於建立一個產品 抽象工廠用於建立相關產品系列

7. 小結

1、介紹了工廠設計模式及其使用的上下文; 如何在軟體架構有效使用工廠模式
2、簡單工廠模式:可以執行時根據客戶端傳入的引數型別來建立相應的例項
3、工廠方法模式:定義一個介面建立物件,但是物件的例項化交給子類
4、抽象工廠模式:提供介面,無需指定具體的類就能建立一系列的相關物件
5、Python實現3種工廠模式,並比較了工廠方法與抽象工廠方法的區別

相關文章