在面向对象编程中,术语“工厂”表示一个负责创建其他类型对象的类。通常情况下,作为一个工厂的类有一个对象以及与它关联的多个方法。客户端使用某些参数调用此方法,之后工厂会据此创建所需类型的对象,然后将它们返回给客户端。
- 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑。
- 工厂方法模式:允许接口创建对象,但使用哪个类来创建对象,则是交由子类决定的。
- 抽象工厂模式:抽象工厂是一个能够创建一系列相关的对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。
简单工厂模式
简单工厂类似个体户,生产产品的种类和能够满足的需求相对简单。产品通常只有一个抽象基类,这个基类至少有一个抽象方法,通过继承这个抽象类得到具体的产品。此时工厂要做的就是根据客户需求,实例化具体产品来满足需求。
从下面的UML图可以看出客户端使用Factory
类,该类具有create_type()方法。当客户端使用类型参数调用create_type()方法时,Factory会根据传入的参数,返回Product1或Product2。接下来通过一个简单的例子来理解。
实现简单工厂方法
我们将创建一个名为Animal
的抽象产品。Animal
是一个抽象的基类,它带有方法do_say()
。我们利用Animal接口创建了两种产品(Cat和Dog),并实现了do_say()
方法来提供这些动物的相应的叫声。ForestFactory
是一个带有make_sound()
方法的工厂。根据客户端传递的参数类型,它就可以在运行时创建适当的Animal实例,并输出正确的声音
class Animal(metaclass=ABCMeta):
@abstractmethod
def do_say(self):
pass
class Dog(Animal):
def do_say(self):
print("Wow Wow!")
class Cat(Animal):
def do_say(self):
print("Meow Meow")
class ForestFactory(object):
def make_sound(self, an):
return eval(an).do_say()
if __name__ == '__main__':
ff = ForestFactory()
animal = input("Which animal should make_sound Dog or Cat?")
ff.make_sound(animal)
说了半天,这个简单工厂类不就是面向对象的继承和多态嘛。确实就是这么回事,从不同的角度来看待相同的概念而已。这里可以看到客户端不直接实例化具体产品。如果新增需求,只需要重新生成具象的产品就可以了,也就起到了解耦合的作用。
工厂方法模式
上面简单工厂模式确实太简单了,工厂模式在此基础上丰富了“工厂的生产能力”,与简单工厂模式相比有以下几点不同:
- 定义工厂基类,创建具体产品的任务放在子类完成,也就是工厂类是通过继承的方式而不是实例化来完成
- 工厂方法使设计更加具有可定制性。它可以返回相同的实例或子类,而不是某种类型的对象
实现工厂方法
让我们拿一个现实世界的场景来理解工厂方法的实现。假设我们想在不同类型的社交网络(例如LinkedIn、Facebook等)上为个人或公司建立简介。那么,每个简介都有某些特定的组成章节。在LinkedIn的简介中,有一个章节是关于个人申请的专利或出版作品的。在Facebook上,你将在相册中看到最近度假地点的照片区。此外,在这两个简介中,都有一个个人信息的区。因此,简而言之,我们要通过将正确的区添加到相应的简介中来创建不同类型的简介。
下面让我们来看看具体如何实现。在下面的代码示例中,首先定义接口Product。我们将创建一个Section抽象类来定义一个区是关于哪方面内容的,让它尽量保持简单,同时还提供一个抽象方法describe()
from abc import abstractmethod
class Section:
@abstractmethod
def describe(self):
pass
class PersonalSection(Section):
def describe(self):
print("Personal Section")
class AlbumSection(Section):
def describe(self):
print("Album Section")
class PatentSection(Section):
def describe(self):
print("Patent Section")
class PublicSection(Section):
def describe(self):
print("Public Section")
我们创建了一个名为Profile
的抽象类Creator。Profile [Creator]抽象类提供了一个工厂方法,即createProfile()
。该方法应该由ConcreteClass
实现,来实际创建带有适当区的简介。Profile抽象类不知道每个简介应具有哪些区。例如,Facebook的简介应该提供个人信息区和相册区。所以我们将让子类来决定这些事情。我们创建了两个ConcreteCreator类,即Linkedin和Facebook。每个类都实现createProfile()抽象方法,由该方法在运行时实际创建(实例化)多个区(ConcreteProducts):
class Profile:
def __init__(self):
self.sections = []
self.create_profile()
@abstractmethod
def create_profile(self):
pass
def get_sections(self):
return self.sections
def add_section(self, section):
self.sections.append(section)
class Linkedin(Profile):
def create_profile(self):
self.add_section(PersonalSection())
self.add_section(PatentSection())
self.add_section(PublicSection())
class Facebook(Profile):
def create_profile(self):
self.add_section(PersonalSection())
self.add_section(AlbumSection())
if __name__ == '__main__':
profile_type = input("Which Profile you'd like to create?[Linkedin or Facebook]")
profile = eval(profile_type)()
print("creating Profile..", type(profile).__name__)
print("Profile has sections --", profile.get_sections())
# Which Profile you'd like to create?[Linkedin or Facebook]Facebook
# creating Profile.. Facebook
# Profile has sections -- [<__main__.PersonalSection object at # 0x7ff09ac4e4d0>, <__main__.AlbumSection object at 0x7ff09ac4e510>]
抽象工厂模式
抽象工厂模式的主要目的是提供一个接口来创建一系列相关对象,而无需指定具体的类。工厂方法将创建实例的任务委托给了子类,而抽象工厂方法的目标是创建一系列相关对象。
实际上,抽象工厂模式不仅确保客户端与对象的创建相互隔离,同时还确保客户端能够使用创建的对象。但是,客户端只能通过接口访问对象。如果要使用一个系列中的多个产品,那么抽象工厂模式能够帮助客户端一次使用来自一个产品/系列的多个对象。例如,如果正在开发的应用应该是平台无关的,则它需要对各种依赖项进行抽象处理,这些依赖项包括操作系统、文件系统调用,等等。抽象工厂模式负责为整个平台创建所需的服务,这样的话,客户端就不必直接创建平台对象了。
实现抽象工厂方法
设想一下你最喜欢的披萨饼店的情况。它提供多种披萨饼,对吧?等等,我知道你想立即订购一份,现在让我们讨论这个场景吧!
现在想象一下,我们开办了一家披萨店,供应美味的印式和美式披萨饼。为此我们首先创建一个抽象基类——PizzaFactory
。PizzaFactory
类有两个抽象方法即createVegPizza()
和createNonVegPizza()
,它们需要通过ConcreteFactory
实现。在这个例子中,我们创造了两个具体的工厂,分别名为IndianPizzaFactory
和USPizzaFactory
。下面让我们看看这两个具体工厂的实现代码:
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 IndianPizzaFactory(PizzaFactory):
def create_veg_pizza(self):
return DeluxVeggiePizza()
def create_non_veg_pizza(self):
return CheckPizza()
class USPizzaFactory(PizzaFactory):
def create_veg_pizza(self):
return MexicanVegPizza()
def create_non_veg_pizza(self):
return HamPizza()
接下来创建抽象产品VegPizza
和NonVegPizza
。这里的想法是,素食披萨饼配有适当的外皮、蔬菜和调味料,非素食披萨饼在素食披萨饼上面搭配非素食食材。
class NonVegPizza(metaclass=ABCMeta):
@abstractmethod
def serve(self, veg_pizza: VegPizza):
pass
class DeluxVeggiePizza(VegPizza):
def prepare(self, ):
print("Prepare ", type(self).__name__)
class CheckPizza(NonVegPizza):
def serve(self, veg_pizza: VegPizza):
print(type(self).__name__, "is served with Chicken on ", type(veg_pizza).__name__)
class MexicanVegPizza(VegPizza):
def prepare(self):
print("Prepare ", type(self).__name__)
class HamPizza(NonVegPizza):
def serve(self, veg_pizza):
print(type(self).__name__, "is served with Ham on ", type(veg_pizza).__name__)
最终用户来到PizzaStore并要一份美式非素食披萨的时候,USPizzaFactory负责准备素食,然后在上面加上火腿,马上就变成非素食披萨了!
class PizzaStore:
def make_pizzas(self):
for factory in [IndianPizzaFactory(), USPizzaFactory()]:
veg_pizza = factory.create_veg_pizza()
non_veg_pizza = factory.create_non_veg_pizza()
veg_pizza.prepare()
non_veg_pizza.serve(veg_pizza)
if __name__ == '__main__':
pizza = PizzaStore()
pizza.make_pizzas()
# Prepare DeluxVeggiePizza
# CheckPizza is served with Chicken on DeluxVeggiePizza
# Prepare MexicanVegPizza
# HamPizza is served with Ham on MexicanVegPizza
总结
工厂设计模式使用非常多,稍大一点的项目都会涉及到抽象工厂类。设计抽象工厂基类非常重要,需要根据实际业务把产品类和工厂类区分开来,然后设计工厂方法。好的设计可以让代码的开发和维护变得清晰容易。设计模式作为开发的软实力,需要在实际过程中不断思考和总结,最终灵活应用。