2
\$\begingroup\$

I have implemented Visitor and Decorator Pattern in python. I am much used to java design pattern style and hence thought would try in python as well. Could anyone tell me if this is the correct way to do in Python.

Is there any better way to do this?

I have made coffee class and decorating it with Sugar and Milk. I have a visitor that will calculate the tax or the cost. The values are random in TaxVisitor and CostVisitor

from abc import ABC, abstractmethod


class Visitable(ABC):

    @abstractmethod
    def accept(self, visitor):
        pass

class CostVisitor(ABC):

    @abstractmethod
    def cost(self, node):
        pass

class TaxVisitor(CostVisitor):

    def cost(self,node):
        if isinstance(node, Milk):
            return 150
        if isinstance(node, Sugar):
            return 100
        if isinstance(node, Plain_Coffee):
            return 10

class CostVisitor(CostVisitor):

    def cost(self,node):
        if isinstance(node, Milk):
            return 15
        if isinstance(node, Sugar):
            return 10
        if isinstance(node, Plain_Coffee):
            return 1

class Coffee(Visitable):

    @abstractmethod
    def cost(self):
        raise ValueError('Implement')

    @abstractmethod
    def display(self):
        raise ValueError('Implement')


class Plain_Coffee(Coffee):

    def cost(self):
        return 2

    def display(self):
        return 'Coffee'

    def accept(self, visitor):
        return visitor.cost(self)


class CoffeeDecorator(Coffee):

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

    def accept(self, visitor):
        return visitor.cost(self) + self.m_base.accept(visitor)


class Milk(CoffeeDecorator):

    def __init__(self, m_base):
        CoffeeDecorator.__init__(self, m_base)

    def cost(self):
        return self.m_base.cost() + 1

    def display(self):
        return self.m_base.display() + ' milk'

class Sugar(CoffeeDecorator):

    def __init__(self, m_base):
        CoffeeDecorator.__init__(self, m_base)

    def cost(self):
        return self.m_base.cost() + .5

    def display(self):
        return self.m_base.display() + ' sugar'



coffee = Plain_Coffee()
coffee = Milk(coffee)
coffee = Sugar(coffee)

print(coffee.accept(CostVisitor()))

```
\$\endgroup\$
3
  • 1
    \$\begingroup\$ You've got some mixed-up code. coffee.accept(CostVisitor()) returns 26, and coffee.cost() returns 3.5. Why the difference? \$\endgroup\$
    – AJNeufeld
    Commented Feb 18, 2020 at 4:17
  • \$\begingroup\$ @AJNeufeld Yes I get your point. I will fix it. \$\endgroup\$
    – device
    Commented Feb 18, 2020 at 17:59
  • \$\begingroup\$ @AJNeufeld I fixed the code by removing cost() since cost is being calculated by CostVisitor so we do not need cost() \$\endgroup\$
    – device
    Commented Feb 18, 2020 at 18:17

1 Answer 1

1
\$\begingroup\$

In general visitor patterns in other languages are manage with tables, maps or any other struct/object type, here is the improvement that you can make on your code

class TaxVisitor(CostVisitor):

    def cost(self,node):
        if isinstance(node, Milk):
            return 150
        if isinstance(node, Sugar):
            return 100
        if isinstance(node, Plain_Coffee):
            return 10

Can be changed to

class TaxVisitor(CostVisitor):
    self.__table = { "Milk" : 150, "Sugar" : 100, ...}

    def cost(self,node):
        if node.__class__.__name__ in self.__table:
            return self.__table[node.__class__.__name__]

This will make it easy to extender the number of instances by just update the table variable, hope is clear.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ why not just the classes as dict keys? Then you don't need the .__class__.__name__ \$\endgroup\$ Commented Feb 18, 2020 at 11:27
  • \$\begingroup\$ Yes that's another valid approach, the idea basically is to use a dict() for handling the visitor lookups instead of if statements. \$\endgroup\$
    – camp0
    Commented Feb 18, 2020 at 12:11
  • \$\begingroup\$ @camp0 I am new to python what is self.__table suppose to mean? Is that instance variable or class variable? How would python recognize self if is not mentioned inside method or __init__() \$\endgroup\$
    – device
    Commented Feb 18, 2020 at 18:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.