正确的方法具有多态性与Python的组成,而不是继承

问题描述 投票:0回答:1

虽然工作的一个项目我被困在我的设计,同时使用继承。现在,我试图摆脱它,而是使用组成,因为这似乎是适当的解决我的问题。不过,我需要polymorfisme,我不知道如果我实现了我的作文以正确的方式。

可能有人来看看下面我的代码?在最后三行我希望所有的动物行走,但前提是他们必须走的能力。它是调用此属性的功能之前,好做法,如果一个对象具有一定的属性(在这种情况下,“腿”)第一次检查?我只是好奇,如果这是正确的方式做到这一点,或者如果有一个更好的办法。

class Animal:

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")


class Wings:

    def flap(self):
        print("Wings are flapping")


class Legs:

    def walk(self):
        print("Legs are walking")


class Bird:

    def __init__(self):
        self.animal = Animal("Bird")
        self.wings = Wings()

    def make_sound(self):
        print(f"{self.animal.name} is Singing!")


class Dog:

    def __init__(self):
        self.animal = Animal("Dog")
        self.legs = Legs()

    def make_sound(self):
        print(f"{self.animal.name} is Barking")


class Cat:

    def __init__(self):
        self.animal = Animal("Cat")
        self.legs = Legs()

    def make_sound(self):
        print(f"{self.animal.name} is Meowing!")


if __name__ == '__main__':

    animals = list()

    animals.append(Bird())
    animals.append(Dog())
    animals.append(Cat())

    for animal in animals:
        animal.make_sound()

    for animal in animals:
        if hasattr(animal, 'legs'):
            animal.legs.walk()
python inheritance polymorphism composition review
1个回答
1
投票

你已经会有点洁癖实际上的xD

继承描述了“是”的关系,组合物描述了一种“具有”的关系。所以你的情况,使用组成像翅膀和腿的属性非常有意义,但鸟,猫与狗是动物 - 它们没有“有”动物(当然,他们都有跳蚤却是另一个话题) - 所以他们应该Animal继承。

此外,大多数鸟类有腿太AFAICT,还有相当一部分实际上是在所有不靠谱(但有些用它们来游泳,非常有效地做到这一点);-)

它是调用此属性的功能之前,好做法,如果一个对象具有一定的属性(在这种情况下,“腿”)第一次检查?

取决于上下文,真的。作为一般规则,不,这不是好的做法(参见“告诉不问”和“得墨忒耳定律”),但也有它的合理的情况。此外,“好”的设计也依赖于解决问题,而我们到达这里的玩具例子是从来没有代表现实生活中的使用情况的限制。

从理论上讲,成分/代表团应该是透明的,客户端代码,所以你应该叫whatever_animal.walk()并用它做。现在你(作为“客户端代码”)可能会想知道动物不能行走,在这种情况下,非行走的动物应该提高时tolds走路异常......这也意味着,Animal必须为所有可能的默认实现“行动”,以及客户端代码必须为“UnsupportedAction”准备(或但是你想给它们命名)例外。

WRT /实现,使得委托透明可以像使用__getattr__(),即作为简单:

class UnsupportedAction(LookupError):
    pass

class Animal(object):
    _attributes = ()

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")

    def __getattr__(self, name):
        for att in self._attributes:
            if hasattr(att, name):
                return getattr(att, name)
        else:
            raise UnsupportedAction("{} doesn't know how to {}".format(type(self), name))



class Dog(Animal):
    _attributes = (Legs(), )


class Bird(Animal):
    _attributes = (Legs(), Wings())

该好听点这个解决方案是,它的死简单,非常有活力。不太好的一点是,它既不检查的也不明确。

另一种解决方案是明确的代表团:

class UnsupportedAction(LookupError):
    pass

class Animal(object):
    _attributes = ()

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")


    def walk(self):
        return self._resolve_action("walk")

    def fly(self):
        return self._resolve_action("walk")

    # etc            

    def _resolve_action(self, name):
        for att in self._attributes:
            if hasattr(att, name):
                return getattr(att, name)
        else:
            raise UnsupportedAction("{} doesn't know how to {}".format(type(self), name))

这是相当冗长,更动感,但更明显的移动,记录,可读和可检查的。

在上面的例子中,你实际上可以分解出具有自定义描述符的冗余代码:

class Action(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, obj, cls):
        if obj is None:
            return self
        return obj._resolve_action(self.name)

    def __set__(self, obj, value):
        raise AttributeError("Attribute is readonly")


class Animal(object):
    _attributes = ()

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")

    walk = Action("walk")
    fly = Action("fly")

    # etc

但是再一次,这一切都不是有道理没有一个真正的要解决的问题,这通常定义了妥善解决。

© www.soinside.com 2019 - 2024. All rights reserved.