访客模式:访问相同结构的不同方式

时间:2015-01-29 10:52:34

标签: c# visitor

我有这种表格结构:

public class Table : IVisitable
{
    public List<Row> rows;
    public void accept(IVisitor visitor)
    {
        foreach(Row row in rows)
            row.accept(visitor);
        visitor.visit(this);
    }
}

public class Row : IVisitable
{
    public List<Cell> columns; 
    public void accept(IVisitor visitor)
    {
        foreach(Cell cell in columns)
            cell.accept(visitor);
        visitor.visit(this);
    }
}
public class Cell : IVisitable
{
    public void accept(IVisitor visitor)
    {visitor.visit(this);}
}

Parser会创建这个类的对象,所以我对扩展类或类似的东西有点牵强(但我可以在良好的解决方案之前重新考虑)。< / p>

现在,您可以看到我实施的accept()方法访问整个表格,但现在我想定义一个只访问表格前两行的访问者。如果不添加另一个accept(),我怎么能这样做?

更新 我想到了这个解决方案,我想得到你的意见。如果不重写上面的整个代码,只需想象每个类都不实现IVisitable接口(因此每个类都没有accept())。那么创建两个扩展Table的类呢?像这样:

public class VisitTwoRowTable : Table,IVisitable
{
    public VisitTwoRowTable(Table table)
    {
        foreach(Row row in table.rows)
              this.rows.add(row);
    }        
    public void accept(IVisitor visitor)
    {
        for(int i=0;i<2;i++)
            row[i].accept(visitor);
        visitor.visit(this);
    }
}

另一个:

public class VisitWholeTable : Table,IVisitable
{
    public VisitWholeTable(Table table)
    {
        foreach(Row row in table.rows)
              this.rows.add(row);
    }
    public void accept(IVisitor visitor)
    {
        foreach(Row row in rows)
            row.accept(visitor);
        visitor.visit(this);
    }
}

此解决方案唯一的真正的UGLY 是构造函数部分,它创建一个副本(浅拷贝)

2 个答案:

答案 0 :(得分:2)

您只需向访问者添加状态,并跟踪已访问过的行数。

鉴于这些类型(为简洁起见,我省略了Cell):

public interface IVisitor
{
    void visit(Row v);
    void visit(Table v);
}

public interface IVisitable
{
    void accept(IVisitor visitor);
}

public class Table : IVisitable
{
    public List<Row> rows;
    public void accept(IVisitor visitor)
    {
        foreach(Row row in rows)
            row.accept(visitor);
        visitor.visit(this);
    }
}

public class Row : IVisitable
{
    public int number;
    public void accept(IVisitor visitor)
    {
        visitor.visit(this);
    }
}

您的访问者可能会像:

public class FirstTwoRowVisitor : IVisitor
{
    int _numOfRows = 0;

    public void visit(Row r)    
    { 
        if (_numOfRows == 2)
            return;
        Console.WriteLine("Visited Row #{0}", r.number);
        _numOfRows++;
    }

    public void visit(Table t)  
    {
        Console.WriteLine("Table has {0} Rows total", t.rows.Count);
    }
}

示例:

var t = new Table() { rows = new List<Row>() };
int i = 0;
t.rows.Add(new Row() {number = i++});
t.rows.Add(new Row() {number = i++});
t.rows.Add(new Row() {number = i++});
t.rows.Add(new Row() {number = i++});
var v = new FirstTwoRowVisitor();
t.accept(v);

将输出

Visited Row #0
Visited Row #1
Table has 4 Rows total

(这当然只是一个简单的例子,每个实例只能处理两行;但你可以根据需要改变它)


回复您的评论:

public interface IVisitor
{
    void visit(Row v);
    void visit(Table v);
    bool keepgoing { get; }
}

public class Table : IVisitable
{
    public List<Row> rows;
    public void accept(IVisitor visitor)
    {
        foreach(Row row in rows)
            if (visitor.keepgoing) row.accept(visitor);
            else break;
        visitor.visit(this);
    }
}

public class FirstTwoRowVisitor : IVisitor
{
    int _numOfRows = 0;

    public bool keepgoing { get { return _numOfRows < 2; } }

    public void visit(Row r)    
    { 
        if (!keepgoing)
            return;
        Console.WriteLine("Visited Row #{0}", r.number);
        _numOfRows++;
    }

    public void visit(Table t)  
    {
        Console.WriteLine("Table has {0} Rows total", t.rows.Count);
    }
}

答案 1 :(得分:1)

你可以移动

foreach (Row row in rows)
    row.accept(visitor);

进入visitor.Visit(table)方法

然后在不同的访问者中提供不同的实现,例如将您的新访问者更改为

foreach (Row row in rows.Take(2))
    row.accept(visitor);

(您还必须在Table类上公开行。)