访问器

描述

访问器封装了一种在对象的异质集合上操作的算法。 它允许在同一数据上写入多种不同的算法,而不必修改数据(或其主要行为)。

此外,访问器模式允许将对象集合的遍历与对每个对象进行的操作分开。

例子

// The data we will visit
mod ast {
    pub enum Stmt {
        Expr(Expr),
        Let(Name, Expr),
    }

    pub struct Name {
        value: String,
    }

    pub enum Expr {
        IntLit(i64),
        Add(Box<Expr>, Box<Expr>),
        Sub(Box<Expr>, Box<Expr>),
    }
}

// The abstract visitor
mod visit {
    use ast::*;

    pub trait Visitor<T> {
        fn visit_name(&mut self, n: &Name) -> T;
        fn visit_stmt(&mut self, s: &Stmt) -> T;
        fn visit_expr(&mut self, e: &Expr) -> T;
    }
}

use visit::*;
use ast::*;

// An example concrete implementation - walks the AST interpreting it as code.
struct Interpreter;
impl Visitor<i64> for Interpreter {
    fn visit_name(&mut self, n: &Name) -> i64 { panic!() }
    fn visit_stmt(&mut self, s: &Stmt) -> i64 {
        match *s {
            Stmt::Expr(ref e) => self.visit_expr(e),
            Stmt::Let(..) => unimplemented!(),
        }
    }

    fn visit_expr(&mut self, e: &Expr) -> i64 {
        match *e {
            Expr::IntLit(n) => n,
            Expr::Add(ref lhs, ref rhs) => self.visit_expr(lhs) + self.visit_expr(rhs),
            Expr::Sub(ref lhs, ref rhs) => self.visit_expr(lhs) - self.visit_expr(rhs),
        }
    }
}

人们可以实现更多的访问器,例如类型检查器,而不需要修改AST数据。

动机

访问器模式在任何你想将算法应用于异质数据的地方都很有用。 如果数据是同质的,你可以使用一个类似迭代器的模式。 使用访问器对象(而不是功能化的方法)允许访问器是有状态的,从而在节点之间交流信息。

讨论

visit_*方法通常会返回void(与例子中不同)。 在这种情况下,有可能将遍历代码抽取出来,并在算法之间共享(也可以提供noop默认方法)。 在Rust中,常见的方法是为每个数据点提供walk_*函数。 例如,

pub fn walk_expr(visitor: &mut Visitor, e: &Expr) {
    match *e {
        Expr::IntLit(_) => {},
        Expr::Add(ref lhs, ref rhs) => {
            visitor.visit_expr(lhs);
            visitor.visit_expr(rhs);
        }
        Expr::Sub(ref lhs, ref rhs) => {
            visitor.visit_expr(lhs);
            visitor.visit_expr(rhs);
        }
    }
}

在其他语言中(例如Java),数据通常有一个accept方法,担任同样的职责。

参见

访问器模式是大多数OO语言中的一种常见模式。

访问器模式

fold模式与visitor类似,但产生一个新版本的被访数据结构。

Latest commit b809265 on 22 Apr 2021