工厂模式也是最常用的设计模式之一,可以分为简单工厂、工厂方法、抽象工厂三大类型,简单工厂模式不属于GOF。这里就python中简单工厂的实现做个简单说明,我们从最普通的实现一步一步优化。先看代码:
#!/usr/bin/env python
#coding: utf-8
class DesktopPC:
pass
class AllInOnePC:
pass
class Laptop:
pass
class PcSimpleFactory:
def produce(self, category):
product = None
if category == 'desktop':
product = Desktop()
elif category == 'all_in_one':
product = AllInOnePC()
elif category == 'laptop':
product = Laptop()
else:
pass
return product
if __name__:
p = PcSimpleFactory()
print(p.produce('desktop'))
print(p.produce('all_in_one'))
print(p.produce('laptop'))
输出
<__main__.DesktopPC object at 0x7f2447720390>
<__main__.AllInOnePC object at 0x7f2447720390>
<__main__.Laptop object at 0x7f2447720390>
在其他面向对象的高级语言中,我们经常看到产品会专门抽象出来一个接口,所有具体产品实现该接口。由于python是鸭子类型(ducking type,如果它走路像鸭子,游泳像鸭子,那么我们就认为它是鸭子; ),似乎接口就没有没什么存在的必要了,如果开发人员都能够严格遵守规范,确实没有必要了。但有时候为了避免开发人员由于粗心犯错,我们有必要在程序层面强制当前类必须实现特定方法,python在语言层面没有这种支持,不过在标准库abc里提供了抽象类的功能,继承抽象类的类必须实现抽象类中的方法。代码修改如下:
from abc import ABCMeta, abstractmethod
class PC(metaclass=ABCMeta):
@abstractmethod
def run():
pass
class DesktopPC(PC):
def run():
print('this is run on DesktopPC')
class AllInOnePC(PC):
def run():
print('this is run on AllInOnePC')
class Laptop(PC):
def run():
print('this is run on Laptop')
这样只要是PC,我们都继承PC这个抽象类,就不会漏掉他应该实现的方法了。再看看有没有什么可以优化的地方,前面文章里我们介绍了单例模式,不正是为工厂量身定制的嘛,再修改:
from weakref import WeakValueDictionary
from abc import ABCMeta, abstractmethod
class SingletonBase:
_instance = WeakValueDictionary()
def __new__(cls, *args, **kwargs):
return cls._instance.setdefault(cls, super(SingletonBase, cls).__new__(cls))
class PC(metaclass=ABCMeta):
@abstractmethod
def run():
pass
class DesktopPC(PC):
def run():
print('this is run on DesktopPC')
class AllInOnePC(PC):
def run():
print('this is run on AllInOnePC')
class Laptop(PC):
def run():
print('this is run on Laptop')
class PcSimpleFactory(SingletonBase):
def produce(self, category):
product = None
if category == 'desktop':
product = DesktopPC()
elif category == 'all_in_one':
product = AllInOnePC()
elif category == 'laptop':
product = Laptop()
else:
pass
return product
看着似乎不差,好像还不够完美,我们试着使用python的特性修改下他:
#!/usr/bin/env python
#coding: utf-8
from weakref import WeakValueDictionary
from abc import ABCMeta, abstractmethod
class SingletonBase:
_instance = WeakValueDictionary()
def __new__(cls, *args, **kwargs):
return cls._instance.setdefault(cls, super(SingletonBase, cls).__new__(cls))
class PC(metaclass=ABCMeta):
@abstractmethod
def run():
pass
class DesktopPC(PC):
def run():
print('this is run on DesktopPC')
class AllInOnePC(PC):
def run():
print('this is run on AllInOnePC')
class Laptop(PC):
def run():
print('this is run on Laptop')
class PcSimpleFactory(SingletonBase):
def __init__(self, producesMap=None):
self._produceMap = producesMap
def produce(self, category):
return self._produceMap[category]()
if __name__:
p = PcSimpleFactory({'desktop': DesktopPC, 'all_in_one': AllInOnePC, 'laptop': Laptop})
print(p.produce('desktop'))
print(p.produce('all_in_one'))
print(p.produce('laptop'))
我们使用字典替代了丑陋的if/else,看起来舒服多了,但是好像还有个问题,万一使用的人传进去的producesMap中的产品类型不对呢,我们加个检测:
#!/usr/bin/env python
#coding: utf-8
from weakref import WeakValueDictionary
from abc import ABCMeta, abstractmethod
class SingletonBase:
_instance = WeakValueDictionary()
def __new__(cls, *args, **kwargs):
return cls._instance.setdefault(cls, super(SingletonBase, cls).__new__(cls))
class PC(metaclass=ABCMeta):
@abstractmethod
def run():
pass
class DesktopPC(PC):
def run():
print('this is run on DesktopPC')
class AllInOnePC(PC):
def run():
print('this is run on AllInOnePC')
class Laptop(PC):
def run():
print('this is run on Laptop')
class PcSimpleFactory(SingletonBase):
def __new__(cls, *args, **kwargs):
if any((not issubclass(v, PC) for _, v in args[0].items())):
raise Exception('pc map error')
return super(PcSimpleFactory, cls).__new__(cls, *args, **kwargs)
def __init__(self, producesMap=None):
self._produceMap = producesMap
def produce(self, category):
return self._produceMap[category]()
if __name__:
p = PcSimpleFactory({'desktop': DesktopPC, 'all_in_one': AllInOnePC, 'laptop': Laptop})
print(p.produce('desktop'))
print(p.produce('all_in_one'))
print(p.produce('laptop'))
现在应该差不多了,完美