Reputation: 6828
I have this kind of table strucutre:
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);}
}
A Parser will create the object of this classes, so I'm kinda hand-tied about extending classes or something like that (but I could reconsider that in front of a good solution).
Now, as you can see the accept()
method which I implemented visit the whole table, but now I want to define a visitor which visit only the first 2 lines of the table. How can I do that without adding an another accept()
?
UPDATE: I thought about this solution, and I'd like to have your opinion. Without rewrite the whole code above, just imagine that each class doesn't implement the IVisitable interface (and so no accept() for each class). What about creating two classes which extend Table? Something like this:
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);
}
}
And this other one:
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);
}
}
The only REALLY UGLY thing of this solution is the constructor part, where it creates a copy (shallow copy)
Upvotes: 2
Views: 437
Reputation: 101072
You can simply add a state to your visitor and keep track of the number of rows you already visited.
Given these types (I omitted Cell for brevity):
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);
}
}
your visitor could look like:
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);
}
}
Example:
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);
will output
Visited Row #0
Visited Row #1
Table has 4 Rows total
(This is of course just a simple example and each instance will only ever be able to process two rows; but you can alter it depending on your needs)
In response to your comment:
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);
}
}
Upvotes: 2
Reputation: 65421
You could move the
foreach (Row row in rows)
row.accept(visitor);
into the visitor.Visit(table)
method
Then provide different implementations of this in different visitors, e.g. change it in your new visitor to
foreach (Row row in rows.Take(2))
row.accept(visitor);
(You would also have to make rows public on your Table class.)
Upvotes: 1