Java 关于类型的悲哀逻辑

Java 关于类型的悲哀逻辑,java,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

代码库中到处都是这样的代码:

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
方法。这种逻辑到处重复,我认为太多了,无法为每个类添加方法并在类中重写。如何改进这一点?

访客模式和策略模式都可以在这里使用。第一个解决方案:好的多态性

只需向BaseRecord类添加一个抽象的
process()
方法,并在每个子类中重写它。因此,该守则将成为:

BaseRecord record = ...;
record.process();
如果无法将
process()
方法添加到BaseRecord类(及其子类)中,则实现。它将把process方法保留在BaseRecord类之外,但是每次添加新的子类时,都将被迫修改Visitor接口及其所有实现。因此,编译器将为您检查是否忘记了开关中的一个case

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);
    }
}
公共接口记录访问者{
T visitFoo(foo记录foo);
T visitBar(BarRecord foo);
...
}
公共抽象类基记录{
不接受公开摘要(记录访问者);
}
公共类FooRecord扩展了BaseRecord{
@凌驾
公众不接受(记录访客){
return visitor.visitFoo(这个);
}
}
公共类BarRecord扩展BaseRecord{
@凌驾
公众不接受(记录访客){
回访访客。访客栏(本);
}
}
现在,您只需为问题中描述的每个逻辑块实现RecordVisitor:

RecordVisitor<Void> visitor = new ProcessRecordVisitor();
record.accept(visitor);
RecordVisitor=newprocessrecordvisitor();
记录。接受(访客);

我认为这很有启发性:

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

我猜你要找的是泛型。事实上:泛型加接口事实上,多态性,或者访问者模式正如我在我的问题中解释的(很糟糕),有很多这样的逻辑块的实例(例如,
switch(record.source()){case-FOO:return X((FooRecord)FOO);case-BAR:return X(BarRecord)record);}
X(FooRecord记录){}X(BarRecord记录){}
),但有许多不同的
X
方法)。仅仅将每个
X
添加到基类并重写似乎不是一个好的解决方案。我正在寻找更好的解决方案。这就是为什么我的答案是:如果不能在BaseRecord类中添加process()方法,那么就实现访问者模式。每个逻辑块将转换为一个不同的访问者实现。BaseRecord类只需要一个附加方法,供每个访问者使用:
accept(RecordVisitor)
非常感谢。我终于明白了。