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()))
```
asked Feb 18, 2020 at 0:12
\$\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\$ Commented Feb 18, 2020 at 4:17
  • \$\begingroup\$ @AJNeufeld Yes I get your point. I will fix it. \$\endgroup\$ 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\$ 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.

answered Feb 18, 2020 at 8:27
\$\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\$ 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\$ Commented Feb 18, 2020 at 18:19

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.