组合工厂函数的编程模式

Programming pattern for combining factory functions

假设你有一堆工厂函数,每个函数做两件事:

  1. 修改或添加参数到 class
  2. 的初始化
  3. 之后对 class 实例做一些事情

例如

class Dog:
  def __init__(self, **very_many_kwargs):
    pass

def create_police_dog(department, **dog_kwargs):
  dog_kwargs['race'] = 'pitbull_terrier'
  dog = Dog(**dog_kwargs)
  police_academy = PoliceAcademy()
  police_academy.train(dog)
  return dog

def create_scary_dog(**dog_kwargs):
  dog_kwargs['teeth_size'] = 'MAX'
  dog_kwargs['eye_color'] = fetch_angry_eye_colors('https://dogs.com')
  dog = Dog(**dog_kwargs)
  dog.experience_unhappy_childhood()
  return dog

如何串联多个这样的函数?

我认为我的解决方案不是最好的,并且对任何其他解决方案都非常感兴趣。但这是我的想法:

class DogFactory:
    def __init__(self):
        self.dog_kwargs = {}
        self.dog_functions = []

    def scary(self):
        self.dog_kwargs['teeth_size'] = 'MAX'
        self.dog_kwargs['eye_color'] = fetch_angry_eye_colors('https://dogs.com')
        self.dog_functions.append(Dog.experience_unhappy_childhood)
        return self

    def police(self):
        self.dog_kwargs['race'] = 'pitbull_terrier'
        police_academy = PoliceAcademy()
        self.dog_functions.append(police_academy.train)
        return self


    def create(self):
        dog = Dog(**self.dog_kwargs)

        for f in self.dog_functions:
            f(dog)

        return dog


dog = DogFactory() \
        .scary() \
        .police() \
        .create()

每个函数都应该接受 Dog 现有 实例并修改它,然后 return 修改后的实例。这样,您就可以简单地组合函数。 (这在某种程度上相当于 Builder 模式的功能,尽管下面的示例有些笨拙和笨拙。)

class Dog:
    def __init__(self, **very_many_kwargs):
        pass

def create_police_dog(dog, department):
    dog.race = 'pitbull_terrier'
    police_academy = PoliceAcademy()
    police_academy.train(dog)
    return dog

def create_scary_dog(dog):
    dog.teeth_size = 'MAX'
    dog.eye_color = fetch_angry_eye_colors('https://dogs.com')
    dog.experience_unhappy_childhood()
    return dog

scary_police_dog = create_scary_dog(
                     create_police_dog(
                       Dog(),
                       'vice'
                     )
                   )
    

我认为这是构建器模式的一个更典型的实现。

class Dog:
    ...


class DogBuilder:
    def __init__(self):
        self.kwargs = {}
        self._train = False
        self._experience_unhappy_childhood = False

    def build(self):
        d = Dog(**self.kwargs)
        if self._train:
            PoliceAcademy().train(d)
        if self._experience_unhappy_childhood:
            d.experience_unhappy_childhood()
        return d

    def train(self):
        self._train = True

    def set_race(self, r):
        self.kwargs['race'] = r

    def experience_unhappy_childhood(self):
        self._experience_unhappy_childhood = True

    def make_police_dog(self, department):
        self.kwargs['department'] = department
        return self.set_race('pitbull_terrier').train()

    def make_scary_dog(self):
        return self.set_eye_color(fetch_angry_eye_colors('https://dogs.com')).
                    set_teeth_size('MAX').
                    experience_unhappy_childhood()


scary_police_dog = (DogBuilder()
                     .make_police_dog('vice')
                     .make_scary_dog()
                     .build()

这是一种使用多重继承的方法(更具体地说,合作多重继承)。

class Dog:
    def __init__(self, *, race, teeth_size='Min', eye_color=None, **kwargs):
        super().__init__(**kwargs)
        self.race = race
        self.eye_color = eye_color
        self.teeth_size = teeth_size


class PoliceDog(Dog):
    def __init__(self, *, department, **kwargs):
        kwargs['race'] = 'pitbull_terrier'
        super().__init__(**kwargs)
        PoliceAcademy().train(self)


class ScaryDog(Dog):
    def __init__(self, **kwargs):
        kwargs['teeth_size'] = 'MAX'
        kwargs['eye_color'] = fetch_angry_eye_colors('https://dogs.com')
        super().__init__(**kwargs)
        self.experience_unhappy_childhood()


class ScaryPoliceDog(PoliceDog, ScaryDog):
    pass


d = ScaryPoliceDog(department="vice")

除非有一些关于恐怖警犬的特定内容不适用于普通警犬或恐怖犬,否则在ScaryPoliceDog中不需要做任何特别的事情。这一切都是通过使用 super.

的委托来处理的

特别是:

  1. ScaryPoliceDog 的 MRO 是 PoliceDogScaryDogDogobject。每个的 __init__ 方法将依次调用。
  2. PoliceDog 中的
  3. super() 不是 指的是 Dog;它指的是 ScaryDog,因为那是 MRO 中 PoliceDog 之后的 class。
  4. ScaryPoliceDog 不需要指定种族。尽管 Dog 需要该字段,但 PoliceDog 会提供该字段,因为 PoliceDog.__init__ 将首先被调用。

装饰器几乎 工作但是由于您希望您的修改在实例化之前和之后发生,它们将无法正确链接。而是定义一个可以在创建时链接在一起的通用修饰符的自定义系统:

from abc import ABC, abstractmethod                                                                  
                                                                                                     
class DogModifier(ABC):                                                                              
    @abstractmethod                                                                                  
    def mod_kwargs(self, **kwargs):                                                                  
        pass                                                                                         
                                                                                                     
    @abstractmethod                                                                                  
    def post_init(self, dog):                                                                        
        pass                                                                                         
                                                                                                     
class PoliceDog(DogModifier):                                                                        
    def __init__(self, department):                                                                  
        self._dept = department                                                                      
                                                                                                     
    def mod_kwargs(self, **kwargs):                                                                 
        kwargs['race'] = 'pitbull_terrier'                                                          
                                                                                                    
    def post_init(self, dog):                                                                       
        PoliceAcademy(self._dept).train(dog)                                                        
                                                                                                        
class ScaryDog(DogModifier):                                                                        
    def mod_kwargs(self, **kwargs):                                                                 
        kwargs['teeth_size'] = 'MAX'                                                                
        kwargs['eye_color'] = fetch_angry_eye_color('https://dogs.com')                             
                                                                                                    
    def post_init(self, dog):                                                                       
        dog.experience_unhappy_childhood()                                                          
                                                                                                    
def create_dog(*modifiers, **dog_kwargs):                                                               
    for m in modifiers:                                                                             
        m.mod_kwargs(**dog_kwargs)                                                                      
                                                                                                    
    dog = Dog(**dog_kwargs)                                                                         
                                                                                                    
    for m in modifiers:                                                                             
        m.post_init(dog)                                                                            
                                                                                                    
    return dog                                                                                      
                                                                                                    
# ...                                                                                               
                                                                                                    
police_dog = create_dog(PoliceDog('bomb squad'), kw1='a', kw2='b')                                  
scary_dog = create_dog(ScaryDog(), kw1='x', kw2='y')                                                
scary_police_dog = create_dog(PoliceDog('bomb squad'), ScaryDog(), kw1='z')

*代码仅作为示例显示 - 错误修正留作 reader

的练习