链接类装饰器以扩展游戏物品的能力

时间:2013-11-22 18:59:33

标签: python class design-patterns decorator

我正在为我的python课程编写一个小游戏,我想整合一个库存和项目系统。该项目提供的可能性是变量(武器,任务物品,消耗品或非消耗品,等等)。

我已经阅读了这篇关于pattern decoratorgoogle translated in english)的教程(法语),我带来了这个: (我使用的是python3)

class Item(object):

    def __init__(self, caracts=None, inventory=None):
        self.caracts = {}
        if caracts:
            self.caracts = caracts
        self.inventory = inventory


class ItemDecorator(Item):

    def __init__(self, item):
        super().__init__()
        self.item = item
        self.caracts = item.caracts


class Consumable(ItemDecorator):

    def __init__(self, item, amount=1):
        super().__init__(item)
        self._amount = 0
        self.amount = amount

    @property
    def amount(self):
        return self._amount

    @amount.setter
    def amount(self, value):
        self._amount = max(0, value)
        if self._amount == 0 and self.item.inventory:
            self.item.inventory.remove(self)

    @amount.deleter
    def amount(self):
        del self._amount


class Usable(ItemDecorator):

    def __init__(self, item, action=None, consumable=None):
        if not action:
            action = lambda *args, **kwargs: None
        self._use = action
        self.consumable = consumable

    def use(self, *args, **kwargs):
        if self.consumable and self.consumable.amount <= 0:
            raise CantBeUsedException("There is no consumable")
        else:
            if self.consumable:
                self.consumable.amount -= 1
            self._use(*args, **kwargs)

我的想法是能够做到这一点:

potion = Usable(Consumable(Item(), 3), use_potion)
print("isinstance(potion.item, Consumable): {}".format(
    isinstance(potion.item, Consumable)))
potion.consumable = potion.item
for dummy in range(4):
    try:
        potion.use()
    except CantBeUsedException as e:
        print("Expected exception: {}".format(e))

但是我的问题就在第4行。可用的potion使用的耗材应该是potion本身。但potion失去了其消耗品能力,只有potion.item拥有它。这甚至是最糟糕的,因为我调用装饰器的顺序很重要。 potion = Consumable(Usable(Item(), use_potion), 3)引导我做potion.item.use(),这个项目总是令我烦恼。

我该如何简化?知道可用的并不一定会消耗自己,甚至是某种东西。事实上,无论先调用哪个装饰器,我都希望能够做到这一点:

potion = Consumable(Usable(Item(), use_potion), 3)
potion.consumable = potion
potion.use()

我无法为我的问题找到一个干净的解决方案。以下是我想到的所有问题: *这个装饰模式是否适应? (它看起来如此,但我可能是错的)     *如果情况并非如此,那么您认为不是一个接口系统(因此,具有多重遗产)更好的解决方案? *我被困在这里做错了什么?

  • 如何在可扩展的同时使这个系统变得非常简单。为此,我想到了这个解决方案:

    class ItemDecorator(Item):
    
            def __init__(self, item):
            super().__init__()
            self.item = item
            self.caracts = item.caracts
            if hasattr(item, "amount"):
                self.amount = item.amount
            if hasattr(item, "use"):
                self.use = item.use
    

    但是通过这样做,我不会失去Decorator模式的所有可扩展性吗?实际上,每次我想创建一个非常复杂的装饰器时,我都需要更新ItemDecorator。因此,我不会失去装饰者模式的所有优势吗?

非常感谢您的帮助

1 个答案:

答案 0 :(得分:0)

现在你的课程布局相当扭曲,而Usable(Consumable(Item(), 3), use_potion)看起来并不像pythonic。

我稍微重新设计了系统:

class ItemCapability:
    pass


class Consumable(ItemCapability):
    def __init__(self, amount):
        super().__init__()
        self.amount = amount


class Usable(ItemCapability):
    pass


class Item:
    def __init__(self, name):
        self.name = name
        self.capabilities = {}

    def make(self, capability):
        assert isinstance(capability, ItemCapability)
        assert capability.__class__ not in self.capabilities

        self.capabilities[capability.__class__] = capability

    def has(self, capability_cls):
        try:
            return self.capabilities[capability_cls]
        except KeyError:
            return False


potion = Item('potion')
potion.make(Usable())
potion.make(Consumable(amount=10))

print(potion.has(Usable))
print(potion.has(Consumable))

通过这种方式,您可以非常简单地理解类系统,并轻松地查询项目的功能。