【问题标题】:How to use java interfaces with multiple implementing classes如何使用具有多个实现类的 java 接口
【发布时间】:2009-10-02 21:27:00
【问题描述】:
public interface Foo {
}

public class SpecificFoo implements Foo {
}

public interface SomeInterface {
    void thisMethod(Foo someKindOfFoo);
}

public class SomeClass implements SomeInterface {

    public void thisMethod(Foo someKindOfFoo) {
        // calling code goes into this function
         System.out.println("Dont go here please");
    }

    public void thisMethod(SpecificFoo specificFoo) {
        // not into this function
         System.out.println("Go here please");
    }
}

public class SomeOlderClass {

    public SomeOlderClass( SomeInterface inInterface ) {
        SpecificFoo myFoo = new SpecificFoo();

        inInterface.thisMethod(myFoo);
    }
}

调用代码:

SomeClass myClass = new SomeClass();
SomeOlderClass olderClass = new SomeOlderClass(myClass);

我有一个接口(@98​​7654325@),几个类调用(例如SomeOlderClass)。我有一个实现接口的类,但我想对传递到通用接口的特定实现进行类型安全操作。

如上面的代码所示,我真的很希望能够制作另一个与传入接口的特定类型相匹配的方法。这行不通。我认为这是因为调用代码只知道接口,而不知道具有更具体方法的实现(即使SpecificFoo implements Foo

那么我怎样才能以最优雅的方式做到这一点呢?我可以通过在实现接口的类中添加 if 语句来使代码正常工作 (SomeClass):

public void thisMethod(Foo someKindOfFoo) {
    // calling code goes into this function
    if ( someKindOfFoo.getClass().equals(SpecificFoo.class) )
        thisMethod(SpecificFoo.class.cast(someKindOfFoo));
    else
        System.out.println("Dont go here please");
}

但是,这并不优雅,因为我每次添加一种新的 Foo 时都必须添加 if 语句。我可能会忘记这样做。

另一个选项是将SpecificFoo 添加到SomeInterface,并让编译器整理提醒我需要SomeClass 中的实现。问题是我最终添加了相当多的样板代码。 (如果其他人实现了接口,他们必须实现新方法,以及任何测试)

鉴于FooSpecificFoo 是相关的,我似乎应该缺少另一个选项。想法?

更多信息:

好吧,我实际上工作了一段时间来尝试简化问题。当我添加更多细节时,复杂性会增加很多。但无论如何……我想我可以解释一下。

基本上,我正在使用 Ray Ryan 在他的talk 中解释的命令模式编写 GWT Web 应用程序 RPC servlet

在 google 代码上有几个它的实现,但是它们中的许多都遇到了这个继承问题。我认为这是 GWT-RPC 代码 bugreport 中的一个错误,但是,当我进一步实施时,我注意到纯粹在客户端发生了类似的问题,并且在托管模式下。 (即所有 java,没有 gwt javascript 疯狂)。

所以我将基本思想抽象为一个原始的 java 命令行案例,并看到了同样的问题,如上所述。

如果您按照 Ray Ryan 讨论的内容进行操作,Foo 是一个动作,SpecificFoo 是我想要调用的特定动作。 SomeInterface 是客户端 RPC 服务,SomeClass 是服务器端 RPC 类。 SomeOlderClass 是一种 rpc 服务,它会知道缓存等等。

很明显,对吧?就像我说的,我认为所有 GWT RPC 的废话只会在基本问题上搅浑水,这就是为什么我尽量简化它。

【问题讨论】:

  • 为什么我的耳朵里有血?

标签: java interface oop


【解决方案1】:

如果您需要在运行时找出对象的实际类型,那么设计很可能是错误的。这至少违反了Open Closed Principle and Dependency Inversion Principle

(因为Java没有multiple dispatch,所以thisMethod(Foo)将被调用而不是thisMethod(SpecificFoo)Double dispatch可以用来绕过语言的限制,但可能仍然存在一些设计问题潜伏在那里。 ..)

请提供更多信息,说明您要完成的工作。目前,该问题没有提供足够的信息来提出正确的设计。

一个通用的解决方案是,由于操作取决于 Foo 的运行时类型,因此该方法应该是 Foo 的一部分,以便其实现可以根据 Foo 的类型而有所不同。因此,您的示例将更改为如下所示(可能将SomeInterface 或其他参数添加到thisMethod())。

public interface Foo {
    void thisMethod();
}

public class SpecificFoo implements Foo {
        public void thisMethod() {
                 System.out.println("Go here please");
        }
}

【讨论】:

    【解决方案2】:

    尝试使用双重调度:向Foo 接口添加一个由SomeClass#thisMethod 调用的方法。然后把代码放到这个方法的实现中。

    public interface Foo {
      public void thatMethod(SomeClass a);
      public void thatMethod(SomeOlderClass a);
    }
    
    public class SomeClass implements SomeInterface {
        public void thisMethod(Foo someKindOfFoo) {
            someKindOfFoo.thatMethod(this);
        }
    }
    

    【讨论】:

      【解决方案3】:

      抱歉,我发现问题描述过于抽象,无法提出建议。您显然有一个设计问题,因为您通常不需要检查接口的类型。不过,我会试一试...首先,我需要让您的问题更具体,以便我的小大脑能够理解。除了Foos,还有Birds呢?

      public interface Bird {
      }
      
      public class Ostrich implements Bird {
      }
      
      public interface BirdManager {
          void fly(Bird bird);
      }
      
      public class AdvancedBirdManager implements BirdManager {
      
          public void fly(Bird bird) {
              System.out.println("I am in the air. Yay!");
          }
      
          public void fly(Ostrich ostrich) {
              System.out.println("Sigh... I can't fly.");
          }
      }
      
      public class ZooSimulation {
          public ZooSimulation(BirdManager birdManager) {
              Ostrich ostrich = new Ostrich();
              birdManager.fly(ostrich);
          }
      }
      
      public static void main(String[] args) {
          AdvancedBirdManager advancedBirdManager = new AdvancedBirdManager();
          ZooSimulation zooSimulation = new ZooSimulation(advancedBirdManager);
      }
      

      在这里,鸵鸟会宣布“我在空中。耶!”这不是我们想要的。

      好吧,所以,忽略我在这里没有基本 OO 的事实,问题是 BirdManager 将寻找与传入的类型匹配的最不具体的方法。所以无论我给什么样的鸟它,它将始终匹配fly(Bird)。我们可以在其中放置一些if 检查,但是随着您添加更多类型的鸟类,您的设计将进一步退化。这是困难的部分 - 我不知道这在你的问题的上下文中是否有意义,但考虑一下我将逻辑从管理器移到鸟的重构:

      public interface Bird {
          void fly();
      }
      
      public class BasicBird implements Bird {
          public void fly() {
              System.out.println("I am in the air. Yay!");
          }
      }
      
      public class Ostrich implements Bird {
          public void fly() {
              System.out.println("Sigh... I can't fly.");
          }
      }
      
      public interface BirdManager {
          void fly(Bird bird);
      }
      
      public class AdvancedBirdManager implements BirdManager {
      
          public void fly(Bird bird) {
              bird.fly();
          }
      }
      
      public class ZooSimulation {
          public ZooSimulation(BirdManager birdManager) {
              Ostrich ostrich = new Ostrich();
              birdManager.fly(ostrich);
          }
      }
      
      public static void main(String[] args) {
          AdvancedBirdManager advancedBirdManager = new AdvancedBirdManager();
          ZooSimulation zooSimulation = new ZooSimulation(advancedBirdManager);
      }
      

      我们的鸵鸟现在说对了,鸟类经理仍然把它当作一只鸟来对待。同样,糟糕的 OO(鸵鸟不应该有 fly() 方法)但它说明了我的想法。

      【讨论】:

      • (见我上面的补充)。自我注意:使用鸟类,而不是 foo。 :) 谢谢,这有点帮助。我认为在我的情况下,像你所做的那样将逻辑移动到类中是不可行的。正如我上面解释的,对象是动作,并且涉及服务器端特定的代码。虽然它不会在客户端执行,但我不知道 GWT 是否愿意在其客户端代码中包含这些内容。嗯,虽然不太确定。
      【解决方案4】:

      只要没有太多Foo 的实现,我会在SomeInterface 中为Foo 的每个子类声明一个抽象方法,并让一个抽象类将调用转发到为定义的默认方法最通用的类​​型:

      public interface Foo {
      }
      
      public class SpecificFoo implements Foo {
      }
      
      public interface SomeInterface {
              void thisMethod(Foo someKindOfFoo);
              void thisMethod(SpecificFoo specificFoo);
              void thisMethod(OtherSpecificFoo otherSpecificFoo);
      }
      
      public abstract class AbstractSomeInterface {
              public void thisMethod(Foo wrongFoo) {
                  throw new IllegalArgumentException("Wrong kind of Foo!");
              }
      
              public void thisMethod(SpecificFoo specificFoo) {
                  this.thisMethod((Foo) specificFoo);
              }
      
              public void thisMethod(OtherSpecificFoo otherSpecificFoo) {
                  this.thisMethod((Foo) specificFoo);
              }
      }
      
      public class SomeClass extends AbstractSomeInterface {
              public void thisMethod(SpecificFoo specificFoo) {
                       // calling code goes into this function
                       System.out.println("Go here please");
              }
      }
      
      public class SomeOlderClass {
      
              public SomeOlderClass( SomeInterface inInterface ) {
                      SpecificFoo myFoo = new SpecificFoo();
      
                      inInterface.thisMethod(myFoo);
              }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-11
        • 1970-01-01
        • 1970-01-01
        • 2019-10-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多