避免强制转换和instanceOf

Chr*_*311 2 java design-patterns casting instanceof

我有一个界面

public interface Details {
   // nothing needed until now
}
Run Code Online (Sandbox Code Playgroud)

它在如下类中使用:

public class Value {
    // many fields
    private Details details;

    public Value(SomeType type) {
        switch (type) {
        case TYPE_1:
        case TYPE_2:
            this.details = new DetailsA();
            break;
        case TYPE_3:
            this.details = new DetailsB();
            break;
        default:
            throw new NotImplementedException("not yet implemented");
        }
    }

    public Details getDetails() {
        return this.details;
    }
}
Run Code Online (Sandbox Code Playgroud)

该接口有两个实现

public class DetailsA implements Details {

    private BigDecimal betragA;

    public DetailsA() {
    }

    public BigDecimal getBetragA() {
      return this.betragA;
    }

    public void setBetragA(BigDecimal betragA) {
      this.betragA = betragA;
   }

}

public class DeailsB implements Details {

    private BigDecimal betragB;
    private boolean booleanB;

    public BetragB() {
    }

    public BigDecimal getBetragB() {
        return this.betragB;
    }

    public void setBetragB(BigDecimal betragB) {
        this.betragB = betragB;
    }

    public boolean isBooleanB() {
        return this.booleanB;
    }

    public void setBooleanB(boolean booleanB) {
        this.booleanB = booleanB;
    }

    // some more fields

}
Run Code Online (Sandbox Code Playgroud)

我有一个模型类,我想在其中使用这些详细信息,具体取决于实例。

 public class Model extends AbstractModel {

    private Details details;

    public void init(StoerungValue stoerung) {
        setDetails(stoerung.getSchaden().getDetails());
    }

    private void setDetails(Details details) {
        this.details = details;
    }
    // ...
Run Code Online (Sandbox Code Playgroud)

在那里我有一些如下操作

    // ...  
    public void setBooleanB(boolean booleanB) {
        if (details instanceof DetailB) {
            ((DetailB) details).setBooleanB(booleanB);
        }
    }
    // ...
Run Code Online (Sandbox Code Playgroud)

我怎样才能避免这种转换和instanceOf的东西?任何设计模式都适用于此吗?

Ian*_*ird 5

我认为你遇到的问题是设计味道的集合。你已经把自己逼到了墙角,可能没有简单的出路。我不知道这个解决方案是否适合您,但您至少可以考虑一下。

第一个设计味道是您创建了一个实际上不存在的继承关系。简而言之,植根于的层次结构Details违反了里氏替换原则。当一个类声明(确实Model如此)支持该Details接口时,它就声明任何的实现都Details可以。无论程序被赋予 a DetailsA、 aDetailsB还是某个FooDetails尚未发明的类,程序的正确性和行为都不应该改变。

现实情况是,DetailsADetailsB并没有实际关系。您可以看到这一点,因为Details它没有方法,因此也可能是Object任何两个类已经继承的方法。

第二种设计味道是“功能羡慕”。看起来 的许多方法Model只是对其底层属性的传递调用details。您可以考虑,而不是仅仅setBooleanB提供Model一个getDetails方法,然后让调用者直接在Details对象上工作。这不会删除instanceof检查或铸造,但会将它们移出此类。

这里的第三件事与前两件事相关。 Model并不取决于Details它的属性类型会告诉您的,而是取决于(至少)DetailsB。如果是这样的话,那么它的属性类型应该这么说。现在,有时您可能需要 aModel和 a DetailsA有时您需要 aModel和 a DetailsB,但不能同时两者。在这种情况下,您可以使用泛型解决该问题。

首先,使Model类成为通用类,并使用一个类型参数来告诉其底层Details实际上必须是什么。

public abstract class Model<T extends Details> {
    private T details;

    public void init(T dets) {
        setDetails(dets);
    }

    public void setDetails(T dets) {
        this.details = dets;
    }

    public T getDetails() {
        return this.details;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,创建两个绑定到不同Details类型的子类,从而可以承诺做正确的事情,而不需要强制转换或instanceof调用。

public class ModelA extends Model<DetailsA> {
    public BigDecimal getBetragA() {
        return this.getDetails().getBetragA();
    }
}

public class ModelB extends Model<DetailsB> {
    public boolean getBooleanB() {
        return this.getDetails().isBooleanB();
    }

    public void setBooleanB(boolean boolB) {
        this.getDetails().setBooleanB(boolB);
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定这是否能解决您的问题,但这是需要考虑的事情。