抽象工厂模式和 Borg 模式 2/30 | Python 主题月
写在前面
本文正在参加「Python主题月」,详情查看活动链接
这个月是 Python 活动月,我决定尝试用 Python 来刷这 30 天的每日一题和随机一题。然后如果周末有精力,我想捣鼓捣鼓这个python-patterns
设计模式对我来说更多的是学习而不是我的个人经验总结,所以我很可能理解偏,如果有大佬见到了请及时指出,我之所以选择在掘金来写一些个人的东西是因为这里的文章质量更高,我不希望后来者看到了这些文章被误导。
抽象工厂模式
顾名思义这是抽象的工厂模式,简单一句话,提供了一种方法来封装一组单独的工厂。
不想继续看文字的可以直接去看下面的代码了。
在 Java 和其他语言中,抽象工厂模式的作用是为创建相关/依赖的对象提供一个接口,而不需要指定其实际类别。这个想法是为了抽象化对象的创建,这取决于业务逻辑、平台选择等等。
在 Python 中,我们使用的接口只是一个可调用的接口,它是 Python 中的“内置”接口,在通常情况下,我们可以简单地使用类本身作为可调用的接口,因为类这个概念在 Python 中是一类对象,然后对象的概念,也就是类的实例,和各位之前的理解没什么不一样。
接下来看代码
import random
from typing import Type
class Pet:
def __init__(self, name: str) -> None:
self.name = name
def speak(self) -> None:
raise NotImplementedError
def __str__(self) -> str:
raise NotImplementedError
class Dog(Pet):
def speak(self) -> None:
print("woof")
def __str__(self) -> str:
return f"Dog<{self.name}>"
class Cat(Pet):
def speak(self) -> None:
print("meow")
def __str__(self) -> str:
return f"Cat<{self.name}>"
class PetShop:
"""A pet shop"""
def __init__(self, animal_factory: Type[Pet]) -> None:
"""pet_factory is our abstract factory. We can set it at will."""
self.pet_factory = animal_factory
def buy_pet(self, name: str) -> Pet:
"""Creates and shows a pet using the abstract factory"""
pet = self.pet_factory(name)
print(f"Here is your lovely {pet}")
return pet
# Additional factories:
# Create a random animal
def random_animal(name: str) -> Pet:
"""Let's be dynamic!"""
return random.choice([Dog, Cat])(name)
# Show pets with various factories
def main() -> None:
"""
# A Shop that sells only cats
>>> cat_shop = PetShop(Cat)
>>> pet = cat_shop.buy_pet("Lucy")
Here is your lovely Cat<Lucy>
>>> pet.speak()
meow
# A shop that sells random animals
>>> shop = PetShop(random_animal)
>>> for name in ["Max", "Jack", "Buddy"]:
... pet = shop.buy_pet(name)
... pet.speak()
... print("=" * 20)
Here is your lovely Cat<Max>
meow
====================
Here is your lovely Dog<Jack>
woof
====================
Here is your lovely Dog<Buddy>
woof
====================
"""
if __name__ == "__main__":
random.seed(1234) # for deterministic doctest outputs
import doctest
doctest.testmod()
复制代码
这个例子抽象了宠物的创建,并根据我们选择的工厂(狗,猫,随机动物)来进行。这样做是因为 dog
,cat
和 random_animal
都尊重一个共同的接口(可被创建和 .speak()
的可调用性)。
实际使用中比如涉及到表处理的时候每一条数据都有创建时间和修改时间,需要把时间显示这个功能独立出来就可以用抽象工厂模式。或者是街边小面馆里面给过水面配菜码一样,虽然菜码不一样,但每碗面都必须要有面,有豆芽,有两片绿叶菜,这个盛面的方法要独立出来可以用抽象工厂模式。
Borg 模式
这个模式是需要和单例模式区分的一个概念。
Borg 模式(也被称为 Monostate 模式)是一种实现单子行为的方法,但不是只有一个类的实例,而是有多个共享相同状态的实例。换句话说,重点是共享状态而不是共享实例。
来看代码:
class Borg:
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
class YourBorg(Borg):
def __init__(self, state=None):
super().__init__()
if state:
self.state = state
else:
# initiate the first instance with default state
if not hasattr(self, "state"):
self.state = "Init"
def __str__(self):
return self.state
def main():
"""
>>> rm1 = YourBorg()
>>> rm2 = YourBorg()
>>> rm1.state = 'Idle'
>>> rm2.state = 'Running'
>>> print('rm1: {0}'.format(rm1))
rm1: Running
>>> print('rm2: {0}'.format(rm2))
rm2: Running
# When the `state` attribute is modified from instance `rm2`,
# the value of `state` in instance `rm1` also changes
>>> rm2.state = 'Zombie'
>>> print('rm1: {0}'.format(rm1))
rm1: Zombie
>>> print('rm2: {0}'.format(rm2))
rm2: Zombie
# Even though `rm1` and `rm2` share attributes, the instances are not the same
>>> rm1 is rm2
False
# New instances also get the same shared state
>>> rm3 = YourBorg()
>>> print('rm1: {0}'.format(rm1))
rm1: Zombie
>>> print('rm2: {0}'.format(rm2))
rm2: Zombie
>>> print('rm3: {0}'.format(rm3))
rm3: Zombie
# A new instance can explicitly change the state during creation
>>> rm4 = YourBorg('Running')
>>> print('rm4: {0}'.format(rm4))
rm4: Running
# Existing instances reflect that change as well
>>> print('rm3: {0}'.format(rm3))
rm3: Running
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
在 Python 中,实例属性被存储在一个叫做 __dict__
的属性字典中。通常,每个实例都有自己的字典,但是 Borg 模式修改了这一点,使所有的实例都有相同的字典。
在这个例子中,__shared_state
属性是所有实例之间共享的字典,当初始化一个新的实例时(即在 __init__
方法中),通过将 __shared_state
分配给 __dict__
变量来确保这一点。其他的属性通常被添加到实例的属性字典中,但是由于属性字典本身是共享的(也就是 __shared_state
),所有其他的属性也将被共享。
共享状态在管理数据库连接等应用中很有用。来个具体的例子:
小结
我会把抽象工厂模式用在事后的共性提取上,然后 Borg 模式用在强调共享状态弱化共享实例的代码里。