Реализация метода toString в Java с шаблоном Visitor?

#java #visitor-pattern

#java #посетитель-шаблон

Вопрос:

Вот часть моего Java-кода

 public List<OBJ> a = new ArrayList<OBJ>();
public String A;
public String B;
public String C;
for (OBJ o : a) {
    // .... TODO
}
 

Итак, у меня есть интерфейс OBJ, и есть три объекта, которые реализуют OBJ, скажем, X, Y, Z. Поэтому я сохраняю объекты X / Y / Z в списке a. Теперь скажем, что я хочу пройти цикл, и если o имеет экземпляр X, сохраните значение X.в A, если Y, сохраните значение Y. в B, а если Z, сохраните значение Z. в C. Итак, проблема в том, как на самом деле определить, какой тип объекта o (X, Y, Z) для сохранения их значений в правильной строке.

ПРИМЕЧАНИЕ: я хочу использовать шаблон Visitor или что-то в этом роде, но я на самом деле не очень хорошо разбираюсь в этом, поэтому прошу вашей помощи.

Это означает, что НЕТ Instanceof (ов) или приведения типов и НЕТ выделенных методов, таких как

 interface OBJ {
    void blah();
}

class X implements OBJ {
    public void blah();
} // etc
 

Спасибо! Я действительно хочу избавиться от этого аспекта импорта программного обеспечения!

Эй, вау, спасибо за подробные и быстрые ответы, но моя ситуация немного сложнее, и извините, что я не добавил это раньше.

Таким образом, строки A, B, C фактически размещены в другом классе, например

 class ARGH {
    public List<OBJ> a = new ArrayList<OBJ>();
    public String A;
    public String B;
    public String C;
    //invisible constructor here
    public String toString () {
        for (OBJ o : a) {
            // .... TODO
        }
        return "some string"
    }
}
public void main (String[] args) {
    ARGH argh = new ARGH();
    // Setup some X, Y, Z objects and their values here
    String D = argh.toString();
    // Do something to D
}
 

Таким образом, строки и список на самом деле не являются глобальными переменными, поэтому я не думаю, что это сработает:

 ObjVisitor v = new ObjVisitor() {
    @Override
    public void visit(X x) {
      A = x.value();
    }
    // And so on.
}
 

Я предполагаю, что мне нужно каким-то образом передать строку A, B, C в метод visit, но я не знаю, как это сделать, и при этом оставаться с шаблоном Visitor.

Комментарии:

1. Я не уверен, что вы можете это сделать, передача A, B и C в методы не будет работать из-за их неизменности. Вы могли бы создать A, B и C StringBuilders и передать их в качестве параметров конструктору ObjVisitor, тогда у него была бы ссылка на эти StringBuilders?

Ответ №1:

В ореховой скорлупе вы бы поступили так:

  1. Создайте интерфейс Visitor ObjVisitor с одним visit методом для каждого типа.
  2. Добавьте аннотацию accept(ObjVisitor v) к OBJ .
  3. Добавьте an accept(ObjVisitor v) { v.visit(this); } к каждой OBJ реализации.
  4. Вызов o.accept(yourVisitorImpl) в цикле.

Вы действительно получили некоторый код от Bringer128. Я немного доработал и добавил String -stuff .

 import java.util.*;

interface OBJ {
    String accept(ObjVisitor v);
}

interface ObjVisitor {
    String visit(X x);
    String visit(Y y);
    String visit(Z z);
}

class X implements OBJ {
    public String accept(ObjVisitor v){ return v.visit(this); }
}
class Y implements OBJ {
    public String accept(ObjVisitor v) { return v.visit(this); }
}
class Z implements OBJ {
    public String accept(ObjVisitor v) { return v.visit(this); }
}
 

Использование:

 class Test {
    public static void main(String[] args) {

        List<OBJ> objs = Arrays.asList(new Z(), new X());

        ObjVisitor toStringVisitor = new ObjVisitor() {
            public String visit(X x) { return "X object"; }
            public String visit(Y y) { return "Y object"; }
            public String visit(Z z) { return "Z object"; }
        };

        String result = "";
        for (OBJ o : objs)
            result  = o.accept(toStringVisitor)   "n";

        System.out.println(result);

        // Prints
        // Z object
        // X object
    }
}
 

Альтернативой (возможно, лучшим подходом) было бы позволить реализации visitor поддерживать StringBuilder, позволить методам visit возвращать void, а после цикла просто вызвать stringVisitor.getCurrentString() или что-то в этом роде.

Ответ №2:

aioobe разъяснил это для вас, но вот как будет выглядеть реализация.

 interface OBJ {
  void blah();
  // New method!
  void accept(ObjVisitor v);
}

class X implements OBJ {
  public void blah() {}
  @Override
  public void accept(ObjVisitor v) {
    v.visit(this);
  }
}

interface ObjVisitor {
  public void visit(X x);
  public void visit(Y y);
}
 

Теперь используйте его:

 public List<OBJ> a = new ArrayList<OBJ>();
public String A;
public String B;
public String C;

public void myMethod() {

  ObjVisitor v = new ObjVisitor() {
    @Override
    public void visit(X x) {
      A = x.value();
    }
    // And so on.
  }
  for (OBJ o : a) {
    o.accept(v);
  }
}