2015-01-29 2 views
2

У меня есть этот вид таблицы 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);} 
} 

синтаксический анализатор будет создавать объект этого класса, так что я своего рода ручной привязал о расширении классов или что-то вроде этого (но я мог бы пересмотреть, что перед хорошим решением).

Теперь, как вы можете увидеть метод accept() который я реализовал визит весь стол, но теперь я хочу, чтобы определить посетителя, который посетить только первые 2 строки таблицы. Как я могу это сделать без добавления другого accept()?

ОБНОВЛЕНИЕ: Я думал об этом решении, и я хотел бы получить ваше мнение. Не переписывая весь код выше, просто представьте, что каждый класс не реализует интерфейс IVisitable (и поэтому no accept() для каждого класса). Как создать два класса, которые расширяют таблицу? Что-то вроде этого:

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

Вы можете просто добавить штат к вашему посетителю и следить за количеством строк вы уже посетили.

Учитывая эти типы (я опущена 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); 
    } 
} 
+0

Это одно из лучших решений, которое у меня было слишком много до сих пор, но это не лучшее решение с точки зрения производительности, но если ничего лучше не выйдет, я думаю, что я пойду с этим. – justHelloWorld

+0

В любом случае, я просто добавил раздел UPDATE, может быть, вы могли бы взглянуть на него;) – justHelloWorld

+1

Подкласс для реализации интерфейса 'IVisitable' кажется мне очень уродливым. Не могли бы вы уточнить, были ли проблемы с производительностью с моим решением? В любом случае, вы уверены, что вам действительно нужен шаблон посетителя здесь? – sloth

1

Вы можете переместить

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

i nto visitor.Visit(table)

Затем предоставляют различные реализации этого в разных посетителях, например. изменить его в новом посетителя

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

(Вы также должны сделать ряды общественности на своем классе таблицы.)

+0

Я думал о решении, как этот вы предложили, но если я не ошибаюсь вы 'не разрешено исследовать посещенную структуру, так как посетитель не должен знать, «какова его структура», чтобы быть более независимым. Я знаю, что вы можете сделать это в некоторых частных случаях, но я не думаю, что это один из них :) – justHelloWorld

+0

В любом случае я просто добавил раздел UPDATE, может быть, вы могли бы посмотреть на него;) – justHelloWorld

+0

Я не слишком увлечен на вашем решении точка шаблона посетителя состоит в том, чтобы отделить данные от посещения, предпочтительнее ли мое решение или решение sloth. imho –

Смежные вопросы