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

标签 python inheritance design-patterns delegation abc

我有以下类使用附加的 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__ 有空去做。

关于python - python中具有抽象方法的委托(delegate)设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46677221/

相关文章:

Python:在散点图中查找多条线性趋势线

对象中对象的 Javascript 原型(prototype)继承

c++ - 什么是对象切片?

javascript - 使用模块加载和类继承将 ES6 转换为 ES5

oop - 面向对象的原则应该应用在过程语言中吗?

api - 使用 websockets 设计 API 的最佳方式是什么?

python - 如何使用变形网格扭曲图像

python - Django Heroku Postgres - 编程错误 : relation does not exist

python - 将太大而无法放入内存的 CSV 文件保存到 parquet 文件中

design-patterns - 如何使构造函数返回子类对象