Java:if-else instanceof扩展类

时间:2013-09-19 14:42:18

标签: java interface polymorphism abstract-class

我有一个抽象类X和一些扩展此类的类,称它们为ABC

在其他一些类Y中,我有一些依赖于类类型的方法调用。 if-else语句如下所示:

public class Y implements InterfaceY {

    public Y(){
    }

    public String doStuff (X x, Boolean flag) {
    String s = "";
    if (x instanceof A) {
        doStuff((A) x));
    } else if (x instanceof B) {
        doStuff((B) x));
    } else if (x instanceof C) {
        doStuff((C) x, flag); 
    } else {
        throw new Exeption();
    }
    return s;

    private String doStuff(A a) {
        return "";
    }

    private String doStuff(B b) {
        return "";
    }

    private String doStuff(C c, Boolean flag) {
        return "";
    }
}

请注意,所有方法都具有相同的名称(doStuff()),但根据类(有时是标志),调用该方法的不同方法实现。当然,这看起来很可怕,并且一旦从X延伸的分类增加,就会变得非常复杂。

有什么方法可以以某种方式创建一个中间接口(或其他东西)来处理大多数(或所有)if-else语句?

6 个答案:

答案 0 :(得分:1)

首先将这些方法取出来,分别放入A,B和C类,实现X接口。

private String doStuff(A a) {
    return "";
}

private String doStuff(B b) {
    return "";
}

private String doStuff(C c, Boolean flag) {
    return "";
}

然后:

if (x instanceof A) {
    doStuff((A) x));
} else if (x instanceof B) {
    doStuff((B) x));
} else if (x instanceof C) {
    doStuff((C) x, flag); 

可以只是x.doStuff();(你甚至不必传递A,B,C,因为在方法中它将是this。你将不得不乱用的旗帜依赖更具体地说,在您的代码上。例如,其他2个doStuff方法也可以接受该标志,但只是忽略它。)

答案 1 :(得分:0)

APPROACH 1

使用state pattern。它会解决您的问题并消除ifelse s。

这是java example

状态模式将方法调用委托给实现相同接口但行为不同的对象。

状态模式示例:

public class StatePatternExample {
    public static void main(String[] args) {
        Girlfriend anna = new Girlfriend();
                            // OUTPUT
        anna.kiss();        // *happy*
        anna.greet();       // Hey, honey!
        anna.provoke();     // :@
        anna.greet();       // Leave me alone!
        anna.kiss();        // ...
        anna.greet();       // Hey, honey!
    }
}

interface GirlfriendInteraction extends GirlfriendMood {
    public void changeMood(GirlfriendMood mood);
}

class Girlfriend implements GirlfriendInteraction {
    private GirlfriendMood mood = new Normal(this);

    public void provoke() {
        mood.provoke();
    }
    public void kiss() {
        mood.kiss();
    }
    public void greet() {
        mood.greet();
    }
    public void changeMood(GirlfriendMood mood) {
        this.mood = mood;
    }
}

interface GirlfriendMood {
    public void provoke();
    public void kiss();
    public void greet();
}

class Angry implements GirlfriendMood {
    private final GirlfriendInteraction context;

    Angry(GirlfriendInteraction context) { // more parameters, flags, etc. possible
        this.context = context;
    }
    public void provoke() {
        System.out.println("I hate you!");
    }
    public void kiss() {
        System.out.println("...");
        context.changeMood(new Normal(context));
    }
    public void greet() {
        System.out.println("Leave me alone!");
    }
}

class Normal implements GirlfriendMood {
    private final GirlfriendInteraction context;

    Normal(GirlfriendInteraction context) {
        this.context = context;
    }

    public void provoke() {
        System.out.println(":@");
        context.changeMood(new Angry(context));
    }

    public void kiss() {
        System.out.println("*happy*");
    }

    public void greet() {
        System.out.println("Hey, honey!");
    }
}

如您所见,课程Girlfriend没有ifelse。它看起来很干净。

班级Girlfriend对应于您的abstract class X,班级NormalAngry对应AB和{{1} }。

您的班级C然后直接委托给X而不检查任何案件。

APPROACH 2

使用command pattern。然后,您可以将命令对象移交给Y的{​​{1}}方法,然后执行它。

答案 2 :(得分:0)

如何实现Handler接口,然后按支持的类型映射它:

public interface Handler<T extends X>{
     Class<T> supportedClass;

     void doStuff(T value, Object...args);
}


public class Y implements InterfaceY {

       private Map<Class<?>, Handler<?>> handlers;

      public Y(List<Handler<?>> handlers){
          // populate map
      }


      public void process(X value){
           handler.get(value.getClass).doStuff(X, ...);
           // you would have to figure out how to determine when other values are needed
      }
}

答案 3 :(得分:0)

某些double dispatch呢?

class X {
    public String letYdoStuff(Y y, Boolean flag) {
        return y.doStuff(this, flag);
    }

    public static void main(String [] args) {
        //X x = new A();
        X x = new B();
        Y y = new Y();

        y.doStuff(x, false);
    }

    public X getThis() {
        return this;
    }
}

class A extends X {
    public String letYdoStuff(Y y, Boolean flag) {
        return y.doStuff(this, flag);
    }
}
class B extends X {
    public String letYdoStuff(Y y, Boolean flag) {
        return y.doStuff(this, flag);
    }
}
class C extends X {
    public String letYdoStuff(Y y, Boolean flag) {
        return y.doStuff(this, flag);
    }
}

class Y {
    public Y(){
    }

    public String doStuff (X x, Boolean flag) {
       String s = "";

       return x.letYdoStuff(this, flag);
   }

   public String doStuff(A a, Boolean flag) {
       System.out.println("in A");
       return "";
   }

   public String doStuff(B b, Boolean flag) {
       System.out.println("in B");
       return "";
   }

   public String doStuff(C c, Boolean flag) {
       System.out.println("in C");
       return "";
   }
}

答案 4 :(得分:0)

这可能是一个难题。我认为克朗彻的解决方案,补充道 doStuff到X并在ABC覆盖它是最简单的 适当时的最佳解决方案。然而,并非总是如此 适当的,因为Single responsibility principle。 (我认为这是正确的用语。如果我得到一些,我道歉 术语错了,我并不完全了解所有条款。)

我们的想法是,如果有doStuff,则不一定XX的目的无关。如果XY是其中的一部分 同样的“团队”,即他们都是为了一个人的目的而设立的 特别的应用,然后它可能没问题。

但假设您有一个具有子类的抽象ShapeCircleSquareUndecagonRandomBlob等等 属于Shape类的一些方法对我们有用 任何使用Shape类的应用程序。但现在说你是 写一个使用其中一些形状的游戏,你想要一个 多态操作,确定形状得到时会发生什么 被飞猴吃掉了。您不希望添加摘要 computeEatenByFlyingMonkey课程的Shape方法即使是doStuff课程 上课是你自己的创作而不是别人的图书馆, 因为那对于一个可能的类来说太具体了 通常用于除了这一场比赛之外的其他目的。

我可以想到几种方法来解决这个问题。

如果将X添加到A是不合适(或不可能),但是 如果BCdoStuff与您的应用程序关联得更紧密 向他们添加public abstract class XWithStuff extends X { // repeat any constructors in X, making them all be just // calls to super(...) public abstract void doStuff (Boolean flag); } public class A extends XWithStuff { @Override public void doStuff (Boolean flag) { ... } } 是合适的,您可以添加另一个 class:

XWithStuff

以及其他所有课程。 (Boolean只是一个例子 名称;在现实生活中,一个包含“X”和一些参考的名称 申请或目的可能更好。)(P.S。我不知道 为什么你使用的是boolean而不是doStuff,但我要离开它 这种方式,以防有充分的理由。)

如果将A添加到B也不合适或不合适, Cpublic interface StuffInterface { public void doStuff (Boolean flag); } public class AWithStuff extends A implements StuffInterface { @Override public void doStuff (Boolean flag) { ... } } ,这是一个可能的解决方案:

AWithStuff

然后在你的程序中创建类A的对象 在doStuff上致电X

void doStuff (X x, Boolean flag) { if (x instanceof StuffInterface) { ((StuffInterface) x).doStuff (flag); } else { throw new IllegalArgumentException (); } }
A

如果这不是一个选项,您必须直接与BdoStuff打交道, 等,你不能将if添加到这些类,然后任何解决方案 会有点hacky。如果您不想使用then - else - HashMap<Class<?>,Interf>,那么 可以查看visitor pattern,或者你可以想象创建 一个A.class,用于映射B.classdoStuff, 等,调用正确的{{1}}的某个接口对象。但 我还没有弄清楚细节。 (实际上,除非你有某种由X型对象组成的复杂结构,否则“访客模式”可能不合适。)

答案 5 :(得分:0)

分隔DoStuffOperation,创建相关工厂并使用它们。

  public interface DoStuffOperation<T> {
    String doStuff(T x);
  }
  public class ADoStuffImpl implements DoStuffOperation<A> {
    public String doStuff(A x) {
      return "doStuff<A>";
    }
  }
  public class ADoStuffWithFlagImpl implements DoStuffOperation<A> {
    public String doStuff(A x) {
      return "doStuffWithFlag<A>";
    }
  }
  public class DoStuffImplFactory {
    public final static <T extends X> DoStuffOperation<X> getDoStuff(Class<T> xClass,boolean flag)  {
      DoStuffOperation<X> impl = null;

      if(xClass.equals(A.class))
      {
        if(flag)
          impl = (DoStuffOperation)new ADoStuffWithFlagImpl();
        else
          impl = (DoStuffOperation)new ADoStuffImpl();
        }
      }
      return impl;
    }
  }

  public class Y implements InterfaceY {
    public String doStuff (X x, Boolean flag) {
      return DoStuffImplFactory.getDoStuff(x.getClass(),flag).doStuff(x);
  }
}

通过这种方式,您无需重构对Y.doStuff()X及派生类的调用。 除非instanceof类实现DoStuffCreator接口,否则您无法删除某些doStuff()来确定X的哪个实现使用:

interface DoStuffCreator {
  DoStuffOperation getDoStuffOperation(boolean flag);
}

XA是您的课程。您还可以使用反射或其他自动方式(外部属性文件等)构建。