Bober02
Bober02

Reputation: 15351

Output object based on the underlying implementation

I have the following base interface:

public interface Value{

double getValue();
}

I would like to have a few different Outputter interfaces that would take a Collection and output them, based on the CONCRETE type of the Value, so I could have a Value class that would have 10 other important fields, which should also be output. I know this falls nicely into a Visitor pattern, but my concern is this: Each outputter would always get a list of ONE specific type of Value, SO I could, in theory, supply a typed list of MeanValue, StdDevValue etc. I am not sure how to design this nicely, as Values are generated by another interface, so in fact, I am only holding a reference to Collection, and I do not want to downcast it and then call specific method on the Outputter...

Upvotes: 1

Views: 150

Answers (2)

Tom Anderson
Tom Anderson

Reputation: 47243

The Visitor pattern doesn't involve any casting. That's the point of it.

Here's the core of a Visitorised family of values:

interface Value {
    double getValue();
    void accept(ValueVisitor visitor);
}

interface ValueVisitor {
    public void visit(MeanValue value);
    public void visit(StdDevValue value);
}

class MeanValue implements Value {
    @Override
    public double getValue() {
        // whatever
    }

    @Override
    public void accept(ValueVisitor visitor) {
        visitor.visit(this);
    }
}

class StdDevValue implements Value {
    @Override
    public double getValue() {
        // whatever
    }

    public int getDegreesOfFreedom() {
        // here's a subclass-specific method
    }

    @Override
    public void accept(ValueVisitor visitor) {
        visitor.visit(this);
    }
}

Here's the output bit:

abstract class Outputter implements ValueVisitor {
    public void output(Collection<? extends Value> values) {
        for (Value value : values) {
            value.accept(this);
        }
    }
}

class PrintingOutputter extends Outputter {
    @Override
    public void visit(MeanValue value) {
        System.out.println("Mean: " + value.getValue());
    }

    @Override
    public void visit(StdDevValue value) {
        System.out.println("Std Dev: " + value.getValue() + " (" + value.getDegreesOfFreedom() + ")");
    }
}

You can use an Outputter with either a collection of mixed values, or a collection of a specific kind:

    List<Value> mixedValues = /* whatever */;
    outputter.accept(mixedValues);

    List<MeanValue> meanValues = /* whatever */ ;
    outputter.accept(meanValues);

Upvotes: 1

Brian Agnew
Brian Agnew

Reputation: 272367

Isn't this just simple polymorphism ? i.e. you want each concrete class to implement Outputter appropriately.

public interface Outputter {
   String output(); // implement this...
}

A visitor pattern is designed for mediation between the calling class and the called class. So the calling class calls a method and passes itself, and the called (visited) class calls back.e.g.

public interface Outputter {
   String output(Caller c); // implement this...
}

and an implementation may look like:

public String output(Caller c) {
   c.doWhatever(getValue());
   c.doAnotherThing(getAnotherValue());
}

Note how the implementation differs for each implementation (type) of Outputter

Upvotes: 0

Related Questions