1

Code base is littered with code like this:

BaseRecord record = // some BaseRecord
switch(record.source()) {
    case FOO:
        return process((FooRecord)record);
    case BAR:
        return process((BarRecord)record);
    case QUUX:
        return process((QuuxRecord)record);
    .
    . // ~25 more cases
    .
}

and then

private SomeClass process(BarRecord record) { }
private SomeClass process(FooRecord record) { }
private SomeClass process(QuuxRecord record) { }

It makes me terribly sad. Then, every time a new class is derived from BaseRecord, we have to chase all over our code base updating these case statements and adding new process methods. This kind of logic is repeated everywhere, I think too many to add a method for each and override in the classes. How can I improve this?

4

3 回答 3

1

Both Visitor Pattern and Strategy pattern can be put in use here. http://en.wikipedia.org/wiki/Strategy_pattern and http://en.wikipedia.org/wiki/Visitor_pattern

于 2013-11-09T15:48:35.190 回答
1

First solution: good old polymorphism.

Simply add an abstract process() method to the BaseRecord class, and override it in every subclass. The code will thus become:

BaseRecord record = ...;
record.process();

If you can't add the process() method into the BaseRecord class (and its subclasses), then implement the visitor pattern. It will leave the process method outside of the BaseRecord class, but each time you add a new subclass, you'll be forced to modify the Visitor interface, and all its implementations. The compiler will thus check for you that you haven't forgotten a case somwhere in a switch.

public interface RecordVisitor<T> {
    T visitFoo(FooRecord foo);
    T visitBar(BarRecord foo);
    ...
}

public abstract class BaseRecord {
    public abstract <T> T accept(RecordVisitor<T> visitor);
}

public class FooRecord extends BaseRecord {
    @Override
    public <T> T accept(RecordVisitor<T> visitor) {
        return visitor.visitFoo(this);
    }
}

public class BarRecord extends BaseRecord {
    @Override
    public <T> T accept(RecordVisitor<T> visitor) {
        return visitor.visitBar(this);
    }
}

Now you simply have to implement RecordVisitor for each block of logic described in the question:

RecordVisitor<Void> visitor = new ProcessRecordVisitor();
record.accept(visitor);
于 2013-11-09T15:51:52.777 回答
0

I think this is instructive:

package classplay;

public class ClassPlay
{
  public void say(String msg) { System.out.println(msg); }

  public static void main(String[] args)
  {
    ClassPlay cp = new ClassPlay();
    cp.go();
  }

  public void go()
  {
    A someClass = new C();
    say("calling process with double dispatch");
    someClass.dueProcess(this);
    say("now calling process directly");
    process(someClass);
  }

  public void process(A a)
  {
    say("processing A");
    a.id();
  }

  public void process(B b)
  {
    say("processing B");
    b.id();
  }

  public void process(C c)
  {
    say("processing C");
    c.id();
  }

  abstract class A 
  {
    abstract public void id(); // { System.out.println("Class A"); }
    public void dueProcess(ClassPlay cp) { cp.process(this); }
  }

  class B extends A
  {
    public void id() { System.out.println("Class B"); }
    public void dueProcess(ClassPlay cp) { cp.process(this); }
  }

  class C extends A
  {
    public void id() { System.out.println("class C"); }
    public void dueProcess(ClassPlay cp) { cp.process(this); }
  }
}
于 2013-11-09T16:44:08.827 回答