【问题标题】:Downcasting object to interface将对象向下转换为接口
【发布时间】:2016-06-14 12:22:29
【问题描述】:

简而言之,我希望能够通过不实现特定接口的超类对类实例进行分组。但是从实例集中,我想从实现该接口的那些实例上的接口调用方法。

一些可以解释它的示例代码。

class Building{
  String c = "white"; 
  Building(){
  }

  void printColor(){
    println("The building is " + c);
  }

  void paint( String c ){
   this.c = c;  
  }

  void printBuildQuality(){
   println("The build quality is average"); 
  }
}


class SturdyFactoryBuilding extends Building implements Factory{

 SturdyFactoryBuilding(){
  super(); 
 }

 void printBuildQuality(){
  println("The build quality is sturdy"); 
 }

 void printFactoryOutput(){
  println("This factory makes stuff"); 
 }
}

class ShakyFactoryBuilding extends Building implements Factory{

  ShakyFactoryBuilding(){
   super(); 
  }

  void printBuildQuality(){
   println("The build quality is shaky");
  }

  void printFactoryOutput(){
   println("This factory makes slightly different stuff"); 
  }
}


public interface Factory{

  public void printFactoryOutput();

}

 Building building = new SturdyFactoryBuilding();    
 building.printBuildQuality();
 building.printColor();
 building.paint("bright red");
 building.printColor();
 building.printFactoryOutput();  

有没有一种方法可以实现这一点,也许是通过在超类中添加一个“isFactory”标志。

谢谢。

【问题讨论】:

  • 您可以通过简单的if(building instanceof Factory) 检查来实现它。但是,您尝试做的事情不会被认为是好的设计。
  • 我看到了@Kayaman,就像这样:if(building instanceof Factory){ SturdyFactoryBuilding sfb = (SturdyFactoryBuilding) building; sfb.printFactoryOutput(); } 我可以看出这不是一个好的设计,因为我仍然需要向下转换到特定的子类。有没有更好的方法来解决这个问题?
  • 您可以将printFactoryOutput 添加到Building,为其提供一个NOOP 默认实现。工厂会覆盖该默认值。一种适配器方法。

标签: java inheritance subclass superclass implements


【解决方案1】:

我认为你必须做出权衡:要么接受一些反模式,要么打开Building“接口”以充当适配器:

class Building implements Factory{

    // the other building stuff

    @Override
    public void printFactoryOutput(){ /* NO OP */ }
}

然后你可以在所有Buildings 上调用printFactoryOutput,到目前为止没有任何效果。

由于您的 Factory-implementations 扩展了 Building,它们会自动继承 NOOP-Implementation。但是既然你覆盖了它:

class ShakyFactoryBuilding extends Building implements Factory{

  ShakyFactoryBuilding(){
   super(); 
  }

  @Override
  public void printBuildQuality(){
   println("The build quality is shaky");
  }

  @Override
  public void printFactoryOutput(){
   println("This factory makes slightly different stuff"); 
  }
}

...你得到了想要的结果。

当然,缺点是所有建筑物都有printFactoryOutput 可见。但这就是我所说的权衡。如果这不可接受,您将不得不彻底重新考虑您的设计。

为了明确不是工厂的建筑物不应被调用该方法,您可以在 Building 中抛出 UnsupportedOperationException,但这会强制 try/catch 块在您的代码中无处不在.如果实际上是工厂,您也可以返回一个布尔值:default=false 并返回 true ... 有很多可能性。

您也可以更改您的设计以使用composition over inheritance

【讨论】:

  • 感谢您的解释。我将重做超类以实现接口方法。它使我的缺点(至少没有重新设计)保持在最低限度。当在超类中实现 Factory 接口时,也实现 Factory 的子类不需要拥有自己的方法覆盖。有没有办法强制这样做,或者implements Factory 是否可以从“工厂”子类中排除?我会接受你的回答,因为它回答了我的主要问题。
  • 既然 Building 实现了 Factory,您可以将其从 XXXFactory 类中移除,是的。
【解决方案2】:

我认为您得到的信息是,这是一个坏主意。它违反了普遍接受的面向对象设计原则。也就是说,有几种方法可以解决,有些方法比其他方法少。

直接投射

最简单的做法是这样的:

if (building instanceof Factory)
    ((Factory)building).printFactoryOutput();

您正在检查它是否为Factory,然后在转换后调用Factory-specific 方法。这是实现糟糕设计的一种直接(因此很容易理解)的方式。

Building 知道Factory

目前BuildingFactory 之间没有必要的关系,但这样的关系可能会对您有所帮助。

class Building {
    // ...
    Factory adaptToFactory() {
        return null;
    }
}

class SturdyFactoryBuilding ... {
    // ...
    @Override
    Factory adaptToFactory() {
        return this;
    }
}

ShakyFactoryBuilding 也是如此。

那么你可以写

Factory f = building.adaptToFactory();
if (f != null)
    f.printFactoryOutput();

更通用的适配器

如果你做了很多这类事情,你可以把它变成一个你需要的地方应用的模式。 (例如,Eclipse 到处都使用适配器。)

interface Adaptable {
    <T> T adapt(Class<T> clazz);
}

class Building implements Adaptable {
    // ...
    @Override
    <T> T adapt(Class<T> clazz) {
        if (clazz.isInstance(this)) {
            return clazz.cast(this);
        }
        return null;
    }
}

然后你会写

Factory f = building.adapt(Factory.class);
if (f != null)
    f.printFactoryOutput();

还有更多的地方可以解决这个问题,但我认为这对于这个问题来说已经足够了。

【讨论】:

  • 谢谢,很遗憾没有重新设计系统的选项。但由于接口只有少数方法,Fildor 描述的方式或“adaptToFactory”方式似乎都是合理的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 2021-07-24
  • 2019-05-08
  • 2017-10-09
  • 1970-01-01
相关资源
最近更新 更多