亲宝软件园·资讯

展开

Python访问者模式

范桂飓 人气:0

一、访问者模式(Visitor Pattern)

数据结构中保存着许多元素,当我们希望改变一种对元素的处理方式时,要避免重复的修改数据结构。那么就要求我们在实现代码时,将数据的处理进行分离,即:数据类只提供一个数据处理的接口,而该数据处理接口就被称之为访问者。那么,相同结构的数据面临不同的处理结果时,我们只需要创建不同的访问者。

访问者模式,指作用于一个对象结构体上的元素的操作。访问者可以使用户在不改变该结构体中的类的基础上定义一个新的操作。

优点:

缺点:

二、应用场景

当一个对象的结构中,包含有多种类型的具有不同接口的对象,且用户要在这些对象上进行依赖于具体的类的运算时,需要用到访问者模式。这就是为什么访问者模式要针对每个被访问的子类都设计一个不同的接口的原因。事实上,如果每个被访问的子类都有相同的接口,包括构造方法、其他方法、参数都一致,则访问者类只需要设计一个访问方法,在该方法中含有一个用于区别不同的被访问的子类的参数即可。例如:可以使用被访问者基类作为参数类型。在对象的结构中包含有多种类型的有不同接口的对象时,各个不同的访问方法可能为访问所对应的类提供不同的参数类型。

当有多个不同的并且互不相关的运算将作用到这些对象上,而用户不希望这些运算混淆这些类时,可以使用访问者模式将相关的操作放到独立的类中,例如:为了实现每个结点类中的计算价格方法,可以将所有的计算价格方法放到一个 VisitPrice 类中。

在对象的数据类型很少改变,但是需要经常改变操作或者增加新的操作的情况下,可以使用访问者模式。反之,如果 Element 的子类经常改变结构,例如:需要增加一个新的税种,这就需要在访问者类中增加新的访问方法,因此,在这种情况下使用访问者模式代价较高,尽量不要使用访问者模式。

三、代码示例

该类图包含两个系列的类:“Element 类” 和 “访问者类”,访问者类定义了施加于 Element 类上的操作,为 Element 类提供一些功能。可以有多种具体的访问者类,各自完成特定的目的,如一个访问者类是计算价格,另一个访问者类则是计算存货数量。因此需要定义一个抽象的访问者父类 Visitor 以及用于各种特殊目的具体的子类。Visitor 类必须给每个结点类提供一个操作,即访问方法,例如获得各结点所代表的商品对象的价格等。

实体角色组成:

示例:上市公司的原始财务数据:

class Finance:
    """财务数据结构类"""
    
    def __init__(self):
        self.salesvolume = None          # 销售额
        self.cost = None                 # 成本
        self.history_salesvolume = None  # 历史销售额
        self.history_cost = None         # 历史成本

    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 年财务数据类"""

    def __init__(self, year):
        Finance.__init__(self)
        self.work = []    # 安排工作人员列表
        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:
    """会计类"""

    def __init__(self):
        self.ID = "会计"
        self.Duty = "计算报表"

    def visit(self, table):
        print('会计年度: {}'.format(table.year))
        print("我的身份是: {} 职责: {}".format(self.ID, self.Duty))
        print('本年度纯利润: {}'.format(table.salesvolume - table.cost))
        print('------------------')


class Audit:
    """财务总监类"""

    def __init__(self):
        self.ID = "财务总监"
        self.Duty = "分析业绩"

    def visit(self, table):
        print('会计总监年度: {}'.format(table.year))
        print("我的身份是: {} 职责: {}".format(self.ID, self.Duty))
        if table.salesvolume - table.cost > table.history_salesvolume - table.history_cost:
            msg = "较同期上涨"
        else:
            msg = "较同期下跌"
        print('本年度公司业绩: {}'.format(msg))
        print('------------------')


class Adviser:
    """战略顾问"""
    def __init__(self):
        self.ID = "战略顾问"
        self.Duty = "制定明年战略"

    def visit(self, table):
        print('战略顾问年度: {}'.format(table.year))
        print("我的身份是: {} 职责: {}".format(self.ID, self.Duty))
        if table.salesvolume > table.history_salesvolume:
            msg = "行业上行,扩大生产规模"
        else:
            msg = "行业下行,减小生产规模"
        print('本年度公司业绩: {}'.format(msg))
        print('------------------')


class Work:
    """工作类"""

    def __init__(self):
        self.works = []  # 需要处理的年度数据列表

    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()  # 计划安排财务、总监、顾问对2018年数据处理
    # 实例化2018年数据结构
    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()   # 实例化会计
    audit = Audit()  # 实例化总监
    adviser = Adviser()     # 实例化顾问
    finance_2018.add_work(accounting)   # 会计安排到2018分析日程中
    finance_2018.add_work(audit)    # 总监安排到2018分析日程中
    finance_2018.add_work(adviser)  # 顾问安排到2018分析日程中
    work.add_work(finance_2018) # 添加2018年财务工作安排
    work.visit()

加载全部内容

相关教程
猜你喜欢
用户评论