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()))
```
1 Answer 1
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.
-
1\$\begingroup\$ why not just the classes as dict keys? Then you don't need the
.__class__.__name__
\$\endgroup\$Maarten Fabré– Maarten Fabré2020年02月18日 11:27:19 +00:00Commented 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– camp02020年02月18日 12:11:18 +00:00Commented 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– device2020年02月18日 18:19:41 +00:00Commented Feb 18, 2020 at 18:19
coffee.accept(CostVisitor())
returns26
, andcoffee.cost()
returns3.5
. Why the difference? \$\endgroup\$