Skip to main content
 首页 » 编程设计

python之python中具有抽象方法的委托(delegate)设计模式

2024年08月19日20powertoolsteam

我有以下类使用附加的 DelegatorParent 类实现“委托(delegate)设计模式”:

class DelegatorParent(): 
 
    def __init__(self): 
        self.a = 'whatever'     
 
class ConcreteDelegatee(): 
 
    def myMethod(self): 
        return 'myMethod' 
 
 
class Delegator(DelegatorParent): 
 
    def __init__(self): 
        self.delegatee = ConcreteDelegatee() 
        DelegatorParent.__init__(self) 
 
    def __getattr__(self, attrname): 
        return getattr(self.delegatee, attrname) 
 
a = Delegator() 
result = a.myMethod() 

一切看起来都很好。

现在我想在 DelegatorParent 中放置一个抽象方法,以确保始终定义“myMethod”。

from abc import ABCMeta, abstractmethod 
 
class DelegatorParent(): 
    __metaclass__ = ABCMeta 
 
    @abstractmethod 
    def myMethod(self): 
        pass 
 
    def __init__(self): 
        self.a = 'whatever' 
 
 
class ConcreteDelegatee(): 
 
    def myMethod(self): 
        return 'myMethod' 
 
 
class Delegator(DelegatorParent): 
 
    def __init__(self): 
        self.delegatee = ConcreteDelegatee() 
        DelegatorParent.__init__(self) 
 
    def __getattr__(self, attrname): 
        return getattr(self.delegatee, attrname) 
 
    # This method seems unnecessary, but if I erase it an exception is 
    # raised because the abstract method's restriction is violated 
    def myMethod(self):  
        return self.delegatee.myMethod() 
 
 
a = Delegator() 
result = a.myMethod() 

你能帮我找到一种“优雅”的方法来从“Delegator”中删除“myMethod”吗...直觉告诉我它在某种程度上是多余的(考虑到定义了自定义 getattr 方法)。

更重要的是,请注意,在这个实现中,如果我忘记在 ConcreteDelegatee 中定义 myMethod,程序会编译,但如果我调用 Delegator.myMethod(),它可能会在运行时崩溃,这正是我想通过使用来避免的DelegatorParent 中的抽象方法。

显然,一个简单的解决方案是将@abstractmethod 移动到Delegator 类,但我想避免这样做,因为在我的程序中DelegatorParent 是一个非常重要的类(而Delegator 只是一个辅助类)。

请您参考如下方法:

您可以决定自动实现委托(delegate)给 ConcreteDelegatee 的抽象方法。

对于每个抽象方法,检查它的名称是否存在于 ConcreteDelegatee 类中,并将此方法作为此类方法的委托(delegate)来实现。

from abc import ABCMeta, abstractmethod 
 
class DelegatorParent(object): 
    __metaclass__ = ABCMeta 
 
    def __init__(self): 
        self.a = 'whatever' 
 
    @abstractmethod 
    def myMethod(self): 
        pass 
 
 
class Delegatee(object): 
    pass 
 
 
class ConcreteDelegatee(Delegatee):     
    def myMethod(self): 
        return 'myMethod' 
 
    def myMethod2(self): 
        return 'myMethod2' 
 
 
class Delegator(DelegatorParent): 
 
    def __new__(cls, *args, **kwargs): 
        implemented = set() 
        for name in cls.__abstractmethods__: 
            if hasattr(ConcreteDelegatee, name): 
                def delegated(this, *a, **kw): 
                    meth = getattr(this.delegatee, name) 
                    return meth(*a, **kw) 
                setattr(cls, name, delegated) 
                implemented.add(name) 
        cls.__abstractmethods__ = frozenset(cls.__abstractmethods__ - implemented) 
        obj = super(Delegator, cls).__new__(cls, *args, **kwargs) 
        obj.delegatee = ConcreteDelegatee() 
        return obj 
 
    def __getattr__(self, attrname): 
        # Called only for attributes not defined by this class (or its bases). 
        # Retrieve attribute from current behavior delegate class instance. 
        return getattr(self.delegatee, attrname) 
 
# All abstract methods are delegared to ConcreteDelegatee 
a = Delegator()  
 
print(a.myMethod()) # correctly prints 'myMethod' 
 
print(a.myMethod2()) #correctly prints 'myMethod2' 

这解决了主要问题(防止 ConcreteDelegatee 忘记定义 myMethod)。如果您忘记实现其他抽象方法,仍会检查它们。

__new__ 方法负责委托(delegate),让您的 __init__ 有空去做。