动态铸造类并调用适当的方法



我正在编写一个实用程序,它使用了我不控制的第三方库中定义的一些类。

我想知道什么是处理以下情况的好方法:第三方图书馆有一个基础抽象类"食品",由"开胃菜"、"主菜"、"饮料"one_answers"甜点"扩展而来

我正在写一个"服务员实用程序",里面有提供每种食物的方法。我想避免instanceof检查的无休止链。


`

Class WaiterUtility{
   public serveItems(Food[] items)
   {
        for(Food aFood : items){
            //how do i call the sub-class specific methods i wrote below?
        }
    }
    private void serve(Appetizer aFood){//somecode}
    private void serve(Entree aFood){//somecode}
    private void serve(Beverage aFood){//somecode}
    private void serve(Dessert aFood){//somecode}
}

`

如果可能的话,我恳请你不要像TBotV63在他的回答中那样使用反思(他甚至说要避免)。来自Oracle文档:

如果可以在不使用反射的情况下执行操作,则最好避免使用它

所以,很明显,我们倾向于说所有的Food都可以服务,任何的Waiter都可以服务任何类型的Food。理想情况下,一个好的API将因此暴露出足以让serve(Food)方法在不知道它是什么食物的情况下完成这项工作的方法。你的问题似乎意味着情况并非如此,因此需要做更多的事情
如果第三方库接受社区输入,那么您应该尝试打开问题或拉取请求来添加功能
显然,这并不总是可能的,所以下一个最好的方法是创建一个接口(类似于Serveable),定义您需要的方法,然后在实现该接口时对不同类型的食物进行子类化。那么你就有Waiter.serve(Serveable)了。

这比反射或instanceof的许多使用要多,但它是更好的OO设计。

为什么反思不好

反射文档指出了反射的3个缺点

  1. 内部构件暴露
  2. 性能
  3. 安全

虽然你可能不在乎2或3,但1尤其糟糕。

使用反射可以。。。使代码功能失调,并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。

为什么instanceof坏(在这种情况下)

serveItems(Food[])向调用者暗示,如果您向它传递几个Food项,它将为每个项提供服务。然而事实并非如此。我们只能为Food的某些子类提供服务,如果我们尝试其他操作,就会出现运行时错误。Java是一种很好的类型安全语言,我们更喜欢编译时错误而不是运行时错误
另一个缺点是,每次添加或更改Food的新子类时,都需要向Waiter添加额外的代码。这成为一个跨领域的问题,并使代码从开发的角度不可扩展
这些并不是唯一的缺点/问题,只是几个例子。

您可以尝试以下代码:

Class WaiterUtility{
   private Map<Class<? extends Food>, Waiter> waiters = new HashMap<>(); 
   WaiterUtility() {
     waiters.put(Appetizer.class, new AppetizerWaiter());
     waiters.put(Entree.class, new EntreeWaiter());
     waiters.put(Beverage.class, new BeverageWaiter());
     waiters.put(Dessert.class, new DessertWaiter());
   }
   public serveItems(Food[] items)
   {
        for(Food aFood : items){
            waiter.get(aFood.getClass()).serve(aFood);
        }
    }
    private static abstract interface Waiter  {
       private void serve(Food aFood);
    }
    private static class AppetizerWaiter implements Waiter  {
       private void serve(Food aFood){
         Appetizer appetizer = (Appetizer) aFood;
         //somecode
      }
    }
    private static class EntreeWaiter implements Waiter  {
       private void serve(Food aFood){//somecode}
    }
    private static class BeverageWaiter implements Waiter  {
       private void serve(Food aFood){//somecode}
    }
    private static class DessertWaiter implements Waiter {
       private void serve(Food aFood){//somecode}
    }
}

尝试类似以下内容:

public serveItems(Food[] items)
{
    for(Food aFood : items){
        Class<?> foodClass = aFood.getClass(); // Get the food's class
        Method serve = WaiterUtility.class.getMethod("serve", foodClass); // Get the method by name and argument types
        try {
          serve.invoke(this, aFood);
        } catch (IllegalArgumentException e) { // Should never occur, we're matching it up.
        } catch (IllegalAccessException e) { // Shouldn't occur, we're in the same class.
        } catch (InvocationTargetException e) {
            // Handle errors possibly thrown by the serve method.
        }
}

还没有测试过这个tho。

请注意,您应该避免这种情况,这是一种糟糕的设计。

相关内容

  • 没有找到相关文章

最新更新