【问题标题】:Avoid casting and instanceOf避免强制转换和 instanceOf
【发布时间】:2015-09-17 02:34:30
【问题描述】:

我有一个界面

public interface Details {
   // nothing needed until now
}

在如下类中使用:

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

接口有两种实现

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

}

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

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

在那里我有一些类似以下的操作

    // ...  
    public void setBooleanB(boolean booleanB) {
        if (details instanceof DetailB) {
            ((DetailB) details).setBooleanB(booleanB);
        }
    }
    // ...

我怎样才能避免这种强制转换和 instanceOf 的东西?是否有任何设计模式适用于此?

【问题讨论】:

  • 您是否考虑过将 isBoolean 和 setBoolean 函数添加到接口中,并将它们添加到两个类中,而 A 类的实现什么都不做?
  • 这里可能有抽象问题。如果您的操作需要知道 Details 的实现是什么,它应该直接与支持的实现一起工作。否则,每次添加新实现时,都必须更新操作代码,这与抽象模型背道而驰。或者,您的操作可以只处理所有 Details 实现共有的内容,并将实现特定的处理委托给接口中的专用方法。
  • 没有方法的接口是一种设计味道。接口的全部目的是定义对象必须遵守的交互协议。如果该协议不包含任何方法,那么除了使用Object 之外,它什么也不提供。您的所有Details 类要么有一个共同的交互模式,要么没有。如果他们不这样做,他们就不是真正相关的,他们的类型应该反映这一点。如果他们这样做了,那么接口应该定义这种关系。
  • 我编辑了我最初的帖子,现在你可以看到我为什么使用这个界面了。 “值”类封装了详细信息。我不希望该值具有所有不同的详细信息类型(例如 getDetailsA()、getDetailsB()、...)。
  • "只有当多态行为确实是目标的行为时,多态才有意义。当它是观察者的行为时,你需要运行时类型。 sites.google.com/site/steveyegge2/when-polymorphism-fails

标签: java design-patterns casting instanceof


【解决方案1】:

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

第一个设计味道是您创建了一个实际不存在的继承关系。简而言之,植根于Details 的层次结构违反了Liskov Substitution Principle。当一个类声称(如Model 那样)支持Details 接口时,它声称Details任何 实现都可以。程序的正确性和行为不应该改变,无论是给定DetailsADetailsB,还是一些尚未发明的FooDetails 类。

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

第二种设计气味是“功能嫉妒”。似乎Model 的许多方法只是对其底层details 属性的传递调用。您可以考虑,而不是在Model 上使用setBooleanB,而只是提供一个getDetails 方法,然后让调用者直接在Details 对象上工作。这不会删除 instanceof 检查或强制转换,但会将它们移出此类。

这里的第三件事与前两个有关。 Model 不依赖于 Details,因为它的属性类型会告诉你,而是依赖于(至少)DetailsB。如果是这样,那么它的属性类型应该这样说。现在,有时您可能需要ModelDetailsA 并且有时您需要ModelDetailsB,但它不能两者同时。在这种情况下,您可以使用泛型解决此问题。

首先,将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;
    }
}

然后,创建两个绑定到不同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);
    }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 2019-08-09
    • 1970-01-01
    相关资源
    最近更新 更多