Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing toString method in Java with The Visitor Pattern?

Here is some of my Java code

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

So I have an interface OBJ and there are three objects that implements OBJ say X, Y, Z. So I store X/Y/Z objects in List a. Now say that I want to go through the loop and if o is of instance X store X.value in A, if Y store Y.value in B, and if Z store Z.value in C. So the problem is really how do you figure out what object type o is (X,Y,Z) to store their values in the right string.

NOTE: I want to use the Visitor pattern or something like it, but I don't really have a firm grasp of it so I'm asking for your help.

This means NO Instanceof(s) or Type Casts and NO Dedicated Methods like

interface OBJ {
    void blah();
}

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

Thanks! I really want to get this import aspect of software engineering down!

Hey wow thanks for the detailed and fast responses, but my situation is a bit more complicated and sorry I didn't add this before.

So String A, B, C are actually housed in another class like

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
}

So the Strings and List are actually not global variables so I don't think this would work:

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

I am assuming I have to somehow pass in the String A, B, C into the visit method but I don't know how to do that and still stay with The Visitor Pattern.

like image 944
Derek Avatar asked Jul 20 '26 03:07

Derek


1 Answers

In a nut-shell you'd do like this:

  1. Create a Visitor interface ObjVisitor with one visit-method for each type.
  2. Add an abstract accept(ObjVisitor v) to OBJ.
  3. Add an accept(ObjVisitor v) { v.visit(this); } to each OBJ implementation.
  4. Call o.accept(yourVisitorImpl) in the loop.

You did indeed get some code from Bringer128. I elaborated a bit and added the 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); }
}

Usage:

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
    }
}

An alternative (perhaps better approach) would be to let the visitor implementation maintain a StringBuilder, let the visit-methods return void, and after the loop just call stringVisitor.getCurrentString() or something like that.

like image 105
aioobe Avatar answered Jul 22 '26 01:07

aioobe