简体   繁体   English

python中具有抽象方法的委托设计模式

[英]Delegation design pattern with abstract methods in python

I have the following classes implementing a "Delegation Design Pattern" with an additional DelegatorParent class: 我有以下类,通过附加的DelegatorParent类来实现“委托设计模式”:

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()

Everything looks fine. 一切看起来都很好。

Now I would like to put an abstract method in DelegatorParent, to ensure that "myMethod" is always defined. 现在,我想在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()

Can you help me find an "elegant" way to remove "myMethod" from "Delegator"... Intuition tells me that it is somehow redundant (considering that a custom getattr method is defined). 您能帮我找到一种从“委托人”中删除“ myMethod”的“优雅”方法吗...直觉告诉我这在某种程度上是多余的(考虑到定义了自定义getattr方法)。

And more importantly, notice that with this implementation, if I forget to define myMethod in ConcreteDelegatee the program compiles, but it may crash in runtime if I call Delegator.myMethod(), which is exactly what I wanted to avoid by using abstract methods in DelegatorParent. 更重要的是,请注意,使用此实现,如果我忘记在ConcreteDelegatee中定义myMethod,则程序会编译,但是如果我调用Delegator.myMethod(),它可能会在运行时崩溃,这正是我想通过在其中使用抽象方法来避免的事情DelegatorParent。

Obviously a simple solution would be to move @abstractmethod to the Delegator class, but I want to avoid doing that because in my program DelegatorParent is a very important class (and Delegator is just an auxiliary class). 显然,一个简单的解决方案是将@abstractmethod移至Delegator类,但是我想避免这样做,因为在我的程序中DelegatorParent是一个非常重要的类(而Delegator只是一个辅助类)。

You can decide to automatically implement abstract methods delegared to ConcreteDelegatee . 您可以决定自动实现指定给ConcreteDelegatee抽象方法。

For each abstract method, check if it's name exist in the ConcreteDelegatee class and implement this method as a delegate to this class method. 对于每个抽象方法,请检查其名称是否存在于ConcreteDelegatee类中,并将该方法实现为该类方法的委托。

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'

This solves the main problem (prevent ConcreteDelegatee from forgetting to define myMethod ). 这解决了主要问题(防止ConcreteDelegatee忘记定义myMethod )。 Other abstract methods are still checked if you forgot to implement them. 如果您忘记实现其他抽象方法,则仍将对其进行检查。

The __new__ method is in charge of the delegation, that frees your __init__ to do it. __new__方法负责委派,使您的__init__可以释放它。

Since you use ABCMeta , you must defined the abstract methods. 由于使用ABCMeta ,因此必须定义抽象方法。 One could remove your method from the __abstractmethods__ set, but it is a frozenset . 可以从__abstractmethods__集合中删除您的方法,但这是一个frozenset Anyway, it involves listing all abstract methods. 无论如何,它涉及列出所有抽象方法。

So, instead of playing with __getattr__ , you can use a simple descriptor. 因此,可以使用一个简单的描述符来代替__getattr__

For instance: 例如:

class Delegated(object):
    def __init__(self, attrname=None):
        self.attrname = attrname

    def __get__(self, instance, owner):
        if instance is None:
            return self
        delegatee = instance.delegatee
        return getattr(delegatee, self.attrname)


class Delegator(DelegatorParent):
    def __init__(self):
        self.delegatee = ConcreteDelegatee()
        DelegatorParent.__init__(self)

    myMethod = Delegated('myMethod')

An advantage here: the developer has the explicit information that "myMethod" is delegated. 这是一个优点:开发人员拥有明确的信息,可以委派“ myMethod”。

If you try: 如果你试试:

a = Delegator()
result = a.myMethod()

It works! 有用! But if you forget to implement myMethod in Delegator class, you have the classic error: 但是,如果您忘记在Delegator类中实现myMethod ,则会遇到经典错误:

Traceback (most recent call last):
  File "script.py", line 40, in <module>
    a = Delegator()
TypeError: Can't instantiate abstract class Delegator with abstract methods myMethod

Edit 编辑

This implementation can be generalized as follow: 该实现可以概括如下:

class DelegatorParent():
    __metaclass__ = ABCMeta

    @abstractmethod
    def myMethod1(self):
        pass

    @abstractmethod
    def myMethod2(self):
        pass

    def __init__(self):
        self.a = 'whatever'


class ConcreteDelegatee1():
    def myMethod1(self):
        return 'myMethod1'


class ConcreteDelegatee2():
    def myMethod2(self):
        return 'myMethod2'


class DelegatedTo(object):
    def __init__(self, attrname):
        self.delegatee_name, self.attrname = attrname.split('.')

    def __get__(self, instance, owner):
        if instance is None:
            return self
        delegatee = getattr(instance, self.delegatee_name)
        return getattr(delegatee, self.attrname)


class Delegator(DelegatorParent):
    def __init__(self):
        self.delegatee1 = ConcreteDelegatee1()
        self.delegatee2 = ConcreteDelegatee2()
        DelegatorParent.__init__(self)

    myMethod1 = DelegatedTo('delegatee1.myMethod1')
    myMethod2 = DelegatedTo('delegatee2.myMethod2')


a = Delegator()
result = a.myMethod2()

Here, we can specify the delegatee name and delegatee method. 在这里,我们可以指定委托人名称和委托人方法。

Here is my current solution. 这是我目前的解决方案。 It solves the main problem (prevent ConcreteDelegatee from forgetting to define myMethod), but I'm still not convinced because I still need to define myMethod inside Delegator, which seems redundant 它解决了主要问题(避免了忘记定义myMethod来防止ConcreteDelegatee),但是我仍然不确定,因为我仍然需要在Delegator中定义myMethod,这似乎是多余的

from abc import ABCMeta, abstractmethod

class DelegatorParent(object):
    __metaclass__ = ABCMeta

    def __init__(self):
        self.a = 'whatever'

    @abstractmethod
    def myMethod(self):
        pass


class Delegatee(object):
    def checkExistence(self, attrname):
        if not callable(getattr(self, attrname, None)):
            error_msg = "Can't instantiate " + str(self.__class__.__name__) + " without abstract method " + attrname
            raise NotImplementedError(error_msg)


class ConcreteDelegatee(Delegatee):    
    def myMethod(self):
        return 'myMethod'

    def myMethod2(self):
        return 'myMethod2'


class Delegator(DelegatorParent):
    def __init__(self):
        self.delegatee = ConcreteDelegatee()
        DelegatorParent.__init__(self)
        for method in DelegatorParent.__abstractmethods__:
            self.delegatee.checkExistence(method)

    def myMethod(self, *args, **kw):
        return self.delegatee.myMethod(*args, **kw)

    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)



# if I forget to implement myMethod inside ConcreteDelegatee, 
# the following line will correctly raise an exception saying 
# that 'myMethod' is missing inside 'ConcreteDelegatee'.
a = Delegator() 

print a.myMethod() # correctly prints 'myMethod'

print a.myMethod2() #correctly prints 'myMethod2'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM