python优雅的工厂模式(不使用字典和eval),自动注册!!!!
简单说下工厂方法模式和抽象工厂模式
工厂方法模式
定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
工厂方法模式就是简单工厂模式的进一步抽像。由于面向对象多态性,工厂方法模式保持了简单工厂的有点同时克服了他的缺点。工厂方法模式中,核心的工厂被提升为一个抽象类,将具体的创建工作交给他的子类完成。这个抽象的工厂类仅规定具体工厂实现的接口,而不明确指出如何实例化一个产品类,这使得工厂方法模式允许系统在不修改原有产品结构的情况下轻松的引进新产品。
抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式 ,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品 当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率
抽象工厂模式包含以下4个角色: AbstractFactory(抽象工厂) ConcreteFactory(具体工厂) AbstractProduct(抽象产品) ConcreteProduct(具体产品)
举例说明:
首先,我们先看一个简单工厂的例子:
#coding=utf-8
class Mercedes(object):
"""梅赛德斯
"""
def __repr__(self):
return "Mercedes-Benz"
class BMW(object):
"""宝马
"""
def __repr__(self):
return "BMW"
假设我们有两个“产品”分别是Mercedes和BMW的汽车,如果没有“工厂”来生产它们,我们就要在代码中自己进行实例化,如:
mercedes = Mercedes()
bmw = BMW()
但现实中,你可能会面对很多汽车产品,而且每个产品的构造参数还不一样,这样在创建实例时会遇到麻烦。这时就可以构造一个“简单工厂”把所有汽车实例化的过程封装在里面。
class SimpleCarFactory(object):
"""简单工厂
"""
@staticmethod
def product_car(name):
if name == 'mb':
return Mercedes()
elif name == 'bmw':
return BMW()
有了SimpleCarFactory类后,就可以通过向固定的接口传入参数获得想要的对象实例,如下:
c1 = SimpleCarFactory.product_car('mb')
c2 = SimpleCarFactory.product_car('bmw')
虽然有了一个简单的工厂,但在实际使用工厂的过程中,我们会发现新问题:如果我们要新增一个“产品”,例如Audi的汽车,我们除了新增一个Audi类外还要修改SimpleCarFactory内的product_car方法。这样就违背了软件设计中的开闭原则[1],即在扩展新的类时,尽量不要修改原有代码。所以我们在简单工厂的基础上把SimpleCarFactory抽象成不同的工厂,每个工厂对应生成自己的产品,这就是工厂方法。
#coding=utf-8
import abc
class AbstractFactory(object):
"""抽象工厂
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def product_car(self):
pass
class MercedesFactory(AbstractFactory):
"""梅赛德斯工厂
"""
def product_car(self):
return Mercedes()
class BMWFactory(AbstractFactory):
"""宝马工厂
"""
def product_car(self):
return BMW()
我们把工厂抽象出来用abc模块[2]实现了一个抽象的基类AbstractFactory,这样就可以通过特定的工厂来获得特定的产品实例了:
c1 = MercedesFactory().product_car()
c2 = BMWFactory().product_car()
到这里,你们有没发现一个问题,我怎么知道使用哪一个工厂(MercedesFactory还是BMWFactory)呢?
一般的有两种写法:
def create(info):
if(info=="Mercedes"):
factory=MercedesFactory()
elif(info=="BMWFactory"):
factory=BMWFactory()
return factory
上述的写法还可以使用dict方式,属于同一种类型
def create(info):
factory_dict = {"Mercedes": MercedesFactory, "BMWFactory": BMWFactory}
return factory_dict.get(info)()
还有一种是eval的方式,提前是eval的值要和工厂类名称一样
def create(info):
return eval("{}Factory()".format(info))
info只能传BMW和Mercedes。两种写法都不够优雅,写法1,我每增加一个工厂,多需要修改if或者修改dict。eval那个更是邪恶????的,避免使用。
找了好多,都没有优雅的,只能自己来了。当时的思路是这样的,既然抽象类是用来创建类的,那么,我创建的时候是不是可以往元类注册一些,元类记录这些信息呢。我的代码如下:
class EntityDoesNotExistException(Exception):
pass
class EntityAutoRegistryMetaClass(type):
"""
demo
class BaseEntity(metaclass=EntityAutoRegistryMetaClass):
pass
class AEntity(BaseEntity):
name = 'A'
assert BaseEntity.all() == {'A': AEntity}
assert BaseEntity.get('A') is AEntity
try:
BaseEntity.get('A')
except EntityDoesNotExistException:
assert False
else:
assert True
"""
__model_type_attr_name = 'name'
__registered_map = {}
def __new__(mcs, *args, **kwargs):
cls = super(EntityAutoRegistryMetaClass, mcs).__new__(mcs, *args, **kwargs)
mcs.__register_model_entity(cls)
return cls
@classmethod
def __register_model_entity(mcs, cls):
cls_entity_name = getattr(cls, mcs.__model_type_attr_name, None)
if not cls_entity_name:
return
mcs.__registered_map[cls_entity_name] = cls
def all(cls):
return type(cls).__registered_map
def get(cls, name: str):
all_entities = cls.all()
if name not in all_entities:
raise EntityDoesNotExistException()
return all_entities[name]
class BaseEntity(metaclass=EntityAutoRegistryMetaClass):
pass
class AEntity(BaseEntity):
name = 'A'
assert BaseEntity.all() == {'A': AEntity}
assert BaseEntity.get('A') is AEntity
try:
BaseEntity.get('A')
except EntityDoesNotExistException:
assert False
else:
assert True
原理是,注册是时候都需要调用EntityAutoRegistryMetaClass 的 __new__方法, __new__方法中调用__register_model_entity,生成map。再简单实现下get方法就可以了。