Many elements are stored in the data structure , When we want to change a way of dealing with elements , Avoid modifying the data structure repeatedly . Then we are required to implement the code , Separate the processing of data , namely : The data class only provides an interface for data processing , The data processing interface is called visitor . that , When data with the same structure is faced with different processing results , We just need to create different visitors .
Visitor mode , Refers to the operation of elements acting on an object structure . Visitors can enable users to define a new operation without changing the classes in the structure .
advantage :
It makes it easier to add new methods to a class in a complex class structure in the visitor class , namely : Simply add a new visitor method . If you don't use visitor mode , This requires adding a new method to each class .
The visitor concentrates the relevant methods in a specific visitor class , Other related methods are concentrated in another specific visitor class . in other words , Visitor subclasses are classified according to the type of method .
shortcoming :
When the structure of an object , There are many types of objects with different interfaces , And when users want to perform operations that depend on specific classes on these objects , You need to use visitor mode . This is why the visitor pattern designs a different interface for each subclass being visited . in fact , If each accessed subclass has the same interface , Including construction method 、 Other methods 、 The parameters are consistent , Then the visitor class only needs to design an access method , This method contains a parameter used to distinguish different subclasses being accessed . for example : You can use the accessee base class as the parameter type . When the structure of an object contains many types of objects with different interfaces , Different access methods may provide different parameter types for the corresponding classes .
When there are multiple different and unrelated operations that will act on these objects , When users don't want these operations to confuse these classes , You can use the visitor pattern to put related operations into separate classes , for example : In order to realize the calculation price method in each node class , You can put all the price calculation methods into one VisitPrice Class .
The data type of the object rarely changes , But when operations need to be changed frequently or new operations need to be added , You can use visitor mode . conversely , If Element Subclasses of often change structure , for example : Need to add a new tax , This requires adding new access methods to the visitor class , therefore , Using visitor mode in this case is more expensive , Try not to use visitor mode .
The class diagram contains two series of classes :“Element class ” and “ Visitor class ”, The visitor class defines what is imposed on Element Operations on classes , by Element Class provides some functions . There can be many specific visitor classes , Each accomplishes a specific purpose , For example, a visitor class is to calculate the price , Another visitor class is to calculate the inventory quantity . Therefore, you need to define an abstract visitor parent Visitor And specific subclasses for various special purposes .Visitor Class must provide an operation for each node class , Access method , For example, get the price of the commodity object represented by each node .
Entity role composition :
Example : The original financial data of listed companies :
class Finance:
""" Financial data structure """
def __init__(self):
self.salesvolume = None # sales
self.cost = None # cost
self.history_salesvolume = None # Historical sales
self.history_cost = None # Historical cost
def set_salesvolume(self, value):
self.salesvolume = value
def set_cost(self, value):
self.cost = value
def set_history_salesvolume(self, value):
self.history_salesvolume = value
def set_history_cost(self, value):
self.history_cost = value
def accept(self, visitor):
pass
class Finance_year(Finance):
"""2018 Annual financial data """
def __init__(self, year):
Finance.__init__(self)
self.work = [] # Arrange the list of staff
self.year = year
def add_work(self, work):
self.work.append(work)
def accept(self):
for obj in self.work:
obj.visit(self)
class Accounting:
""" Accounting """
def __init__(self):
self.ID = " accounting "
self.Duty = " Calculation report "
def visit(self, table):
print(' Fiscal year : {}'.format(table.year))
print(" My identity is : {} duty : {}".format(self.ID, self.Duty))
print(' Net profit for the year : {}'.format(table.salesvolume - table.cost))
print('------------------')
class Audit:
""" Chief financial officer """
def __init__(self):
self.ID = " Chief financial officer "
self.Duty = " Analyze performance "
def visit(self, table):
print(' Accounting Director year : {}'.format(table.year))
print(" My identity is : {} duty : {}".format(self.ID, self.Duty))
if table.salesvolume - table.cost > table.history_salesvolume - table.history_cost:
msg = " Up from the same period "
else:
msg = " Compared with the same period "
print(' The company's performance this year : {}'.format(msg))
print('------------------')
class Adviser:
""" Strategic adviser """
def __init__(self):
self.ID = " Strategic adviser "
self.Duty = " Formulate next year's strategy "
def visit(self, table):
print(' Strategic Advisor year : {}'.format(table.year))
print(" My identity is : {} duty : {}".format(self.ID, self.Duty))
if table.salesvolume > table.history_salesvolume:
msg = " The industry goes up , Expand production scale "
else:
msg = " Industry down , Reduce production scale "
print(' The company's performance this year : {}'.format(msg))
print('------------------')
class Work:
""" Work class """
def __init__(self):
self.works = [] # List of annual data to be processed
def add_work(self, obj):
self.works.append(obj)
def remove_work(self, obj):
self.works.remove(obj)
def visit(self):
for obj in self.works:
obj.accept()
if __name__ == '__main__':
work = Work() # Plan and arrange finance 、 director 、 Consultant right 2018 Annual data processing
# Instantiation 2018 Annual data structure
finance_2018 = Finance_year(2018)
finance_2018.set_salesvolume(200)
finance_2018.set_cost(100)
finance_2018.set_history_salesvolume(180)
finance_2018.set_history_cost(90)
accounting = Accounting() # Instantiate accounting
audit = Audit() # Instantiate the director
adviser = Adviser() # Instantiate consultants
finance_2018.add_work(accounting) # The accountant is arranged to 2018 Analysis schedule
finance_2018.add_work(audit) # The director is arranged to 2018 Analysis schedule
finance_2018.add_work(adviser) # The consultant arranged to 2018 Analysis schedule
work.add_work(finance_2018) # add to 2018 Annual financial work arrangement
work.visit()