关于工厂模式
在上一篇文章中,我们讲到的是单例模式,单例一种创建型设计模式。这篇我们讲解另一种创建型模式,工厂模式。
工厂模式具有以下优点:
- 松耦合,即对象的创建可以独立于类的实现。
- 客户端无需了解创建对象的类,它只需要知道需要传递的接口、方法和函数,就能够创建所需类型的对象了,这简化了客户端的实现。
- 可以轻松在工厂中添加其他类来创建其他类型的对象,这无需更改客户端代码。最简单的情况下,客户端只需要传递另一个参数即可。
- 工厂还可以重用现有对象,但是,如果客户端直接创建对象的话,总是创建一个新对象。
**举例说:**假如一家公司是生成手机的,公司里的一台机器目前正在生产某为的手机CPU,现在公司CEO认为现在需要根据市场生产具有5g功能的cpu,这时工厂模式就派上用场了。在这种情况下,机器成为了接口,CEO是客户端。CEO只需要关心制造的对象(5gcpu)和创建对象的接口(机器)。
Factory(工厂)模式有三种变体。如下:
- 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑 。
- 工厂方法模式:允许接口创建对象,但是使用哪个类来创建对象,这是交由子类来决定。
- 抽象工厂模式:抽象工厂是一个能够创建一系列相关的对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。
细说工厂模式:
简单工厂模式:
简单工厂模式:简单工厂本身不是一种模式。在开发人员进一步了解前,需要详细了解一下工厂方法和抽象工厂方法。工厂可以帮助开发人员创建不同类型的对象,而不是直接将对象实例化。
UML图解:这里客户端类使用的是Factory类,该类具有create_type()方法。当客户端使用类型参数调用create_type()方法,Factory会根据传入的参数,返回Product1或Product2.
下面代码有助于我们进一步理解简单工厂模式。我们先创建一个名为Animal的抽象产品。Animal是一个抽象的基类(ABCMeta是Python的特殊元类,用来生成类Abstract),它带有方法do_say()。我们利用Animal接口创建了两种产品(Cat和Dog),并实现了do_say()方法来提供这些动物的相应叫声。ForestFactory是一个带有make_sound()方法的工厂。根据客户端传递的参数类型,它就可以在运行时创建适当的
Animal实例,并输出正确的声音。
例:
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
@abstractmethod
def do_say(self):
pass
class Dog(Animal):
def do_say(self):
print('旺旺!!!')
class Cat(Animal):
def do_say(self):
print('喵喵!!!')
# forest Factory defined
class ForestFactory(object):
def make_sound(self, obj_type):
return eval(obj_type)().do_say()
# Client code
if __name__ == '__main__':
a = ForestFactory()
animal = input('请输入Cat或Dog:')
a.make_sound(animal)
输出:
工厂方法模式
1. 了解工厂方法模式:
- 我们定义了一个接口来创建对象,但是工厂本身不负责创建对象,而是将这一任务交由子类来完成,即子类决定了要实例化哪些类。
- Factory方法的创建是通过继承而不是通过实例化来完成的。
- 工厂方法使设计更具加具有可制定性。它可以返回相同的实例或子类,而不是某种类型的对象(就像在简单工厂方法中的那样。)。
在UML图中, 有一个包含factoryMethod()方法的抽象类 Creator.FactoryMethod()方法负责创建指定类型的对象ConcreteCreator 类提供了一个实现Creator抽象类的factoryMethod()方法,这种方法可以在运行时修改已创建的对象。ConcreteCreator创建ConcreteProduct,并确保其创建的对象实现了Product类,同时为Product接口中的所有方法提供相应的实现。
简而言之,Creator接口的factoryMethod()方法和ConcreteCreator类共同决定了要创建Product的哪个子类。因此,工厂方法模式定义了一个接口来创建对象,但具体实例化哪个类则是由它的子类决定的。
2. 实现工厂方法
现在让我们来看看具体是怎么实现的,在下面的代码中,首先定义接口Product。我们将创建一个Section抽象类来定义一个区是哪个方面内容的,让它尽量保持简单,同时还提供一个抽象方法describle().
然后我们会创建多个ConcreteProduct、PersonalSection、AlbumSection、PatentSection、PublicactionSection类。这些类实现describe()抽象方法并打印它们各自的区名称。
我们创建一个名为Profile的抽象类Creator。Proflie[Creator]抽象类提供了一个工厂方法,即createProfile()。createProfile()方法应该由ConcreteClass实现,来实际创建带有适当区的简介。Profile抽象类不知道每个简介应具有哪些区。例如,Facebook的简介应该提供个人信息区和相册区。所以我们将让子类来决定这些。
我们创建了两个ConcreteCreator类,即linkedin和facebook。每个类都实现createProfile()抽象方法,由该方法在运行时实际创建(实例化)多个区(ConcreteProducts):
例:
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 Section')
class PatentSection(Section):
def describe(self):
print('Patent Section')
class PublicactionSection(Section):
def describe(self):
print('Publicaction Section')
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(PublicactionSection())
class facebook(Profile):
def createProfile(self):
self.addSections(PersonalSection())
self.addSections(AlbumSection)
"""
最后,开始编写决定实例化哪个Creator类的客户端代码,以便让它根据指定的选项创建所需的简介。
"""
if __name__ == '__main__':
profile_type = input('你想创建哪个简介:【LinkedIn or Facebook】')
profile = eval(profile_type.lower())()
print("创建配置文件.....", type(profile).__name__)
print('简介部分有---', profile.getSections())
输出结果:
运行完整的代码,它会首先要求输入要去创建的简介名称,我们以输入FaceBook为例,他实例化facebook[ConcreateCreator]类。它会在内部创建ConcreteProduct,也就是说,将实例化PersonalSection和AlbumSection。
工厂模式优点:
- 它具有更大的灵活性,使代码更加通用,因为它不是单纯地实例化某个类。这样,实现哪些类取决于接口(Product),而不是ConcreteProduct类。
- 它们是松耦合的,因为创建对象的代码与使用它的代码是分开的。客户端完全不需要关心传递哪些参数以及需要实例化哪些类。由于添加新类更加容易,所以降低了维护成本。
细说抽象工厂模式:
抽象工厂模式主要目的是提供一个接口来创建一系列相关对象。而无需指定具体的类。工厂方法将创建实例的任务委托给子类,而抽象工厂方法的目标是创建一系列相关目标对象。
如图:ConcreteFactory1和ConcreteFactory2是通过AbstractFactory接口创建的此接口具有创建多种产品的相应方法。
ConcreteFactory1和ConcreteFactory2实现了AbstractFactory,并创建实例ConcreteProduct1、ConcreteProduct2、AnotherConcreteProduct1和AnotherConcreteProduct2。
在这里,ConcreteProduct1和ConcreteProduct2是通过AbstractProduct接口创建的,而AnotherConcreteProduct1和AnotherConcreteProduct2则是通过AnotherAbstractProduct接口创建的。
实际上,抽象工厂模式不仅确保客户端与对象的创建相互隔离,同时还确保客户端能够使用创建的对象。但是,客户端只能通过接口访问对象。如果要使用一个系列中的多个产品,那么抽象工厂模式能够帮助客户端一次使用来自一个产品/系列的多个对象。例如,如果正在开发的应用应该是平台无关的,则它需要对各种依赖项进行抽象处理,这些依赖项包括操作系统、文件系统调用,等等。抽象工厂模式负责为整个平台创建所需的服务,这样的话,客户端就不必直接创建平台对象。
我们开办了一家披萨店,供应美味的印式和美式披萨饼。为此,我们首先创建一个抽象基类——PizzaFactory(AbstractFactory见前面的UML图)。PizzaFactory类有两个抽象方法即createVegPizza()和createNonVegPizza(),它们需要通过ConcreteFactory实现。在这个例子中,我们创造了两个具体的工厂,分别名为IndianPizzaFactory和USPizzaFactory。下面让我们看看这两个具体工厂的实现代码:
class PizzaFactory(metaclass=ABCMeta):
@abstractmethod
def createVegPizza(self):
pass
@abstractmethod
def createNonVegPizza(self):
pass
class IndianPizzaFactory(PizzaFactory):
def createVegPizza(self):
return DeluxVeggiePizza()
def createNonVegPizza(self):
return ChickenPizza()
class USPizzaFactory(PizzaFactory):
def createVegPizza(self):
return MexicanVegPizza()
def createNonVegPizza(self):
return HamPizza()
现在,让我们进一步定义AbstractProducts。在下面的代码中,我们将创建两个抽象类:VegPizza和NonVegPizza(AbstractProduct和AnotherAbstract Product见前面的UML图)。它们都定义了自己的方法,分别是prepare()和serve()。
然后我们为每个AbstractProducts定义ConcreteProducts。现在,就这里而言,我们将创建DeluxVeggiePizza和MexicanVegPizza,并实现prepare()方法。ConcreteProducts1和ConcreteProducts2将代表UML图中的这些类。
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__)
当用户点餐时,要了一份美式非素食披萨的时候,USPizzaFactory负责准备素食,然后再加上火腿,即可完成。
class PizzaStore:
def __init__(self):
pass
def makePizzas(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.makePizzas()
输出如图:
比较工厂方法和抽象工厂方法
工厂方法 | 抽象工厂方法 |
---|---|
它向客户端开放了一个创建对象的方法 | 抽象工厂方法包含一个或多个工厂方法来创建一个系列的相关对象 |
它使用继承和子类决定要创建哪个对象 | 它使用组合将创建对象的任务委托给其他类 |
工厂方法用于创建一个产品 | 抽象方法用于创建相关产品的系列 |
在本文,我们介绍了工厂设计模式及其使用的上下文。
我们还考察了简单工厂,它可以在运行时根据客户端的传入参数类型来创建相应的实例。
另外我们还学习了工厂方法模式,它是简单工厂的一个变体。在这种模式中,我们定义了一个接口来创建对象,但是对象的创建却是交由子类来完成。
最后我们学习了抽象工厂方法,它提供了一个接口,无需指定具体的类就能创建一系列的相关对象。