关于类型的悲伤逻辑

时间:2013-11-09 15:44:50

标签: java

代码库充满了这样的代码:

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

然后

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

这让我非常难过。然后,每次从BaseRecord派生一个新类时,我们都必须追逐我们的代码库,更新这些case语句并添加新的process方法。这种逻辑在任何地方都会重复,我想太多了,无法为每个方法添加一个方法并在类中重写。我怎样才能改善这个?

3 个答案:

答案 0 :(得分:1)

访客模式和策略模式都可以在这里使用。 http://en.wikipedia.org/wiki/Strategy_patternhttp://en.wikipedia.org/wiki/Visitor_pattern

答案 1 :(得分:1)

第一种解决方案:良好的旧多态性。

只需向BaseRecord类添加一个抽象的process()方法,并在每个子类中覆盖它。因此代码将成为:

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

如果无法将process()方法添加到BaseRecord类(及其子类)中,请实现visitor pattern。它会将进程方法留在BaseRecord类之外,但每次添加新的子类时,都会被迫修改Visitor接口及其所有实现。因此,编译器将检查您是否忘记了交换机中的案例。

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

现在您只需为问题中描述的每个逻辑块实现RecordVisitor:

RecordVisitor<Void> visitor = new ProcessRecordVisitor();
record.accept(visitor);

答案 2 :(得分:0)

我认为这很有启发性:

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