简体   繁体   English

Java:扩展类的if-else实例

[英]Java: If-else instanceof extended classes

I have an abstract class X and some classes who extend this class, call them A , B and C . 我有一个抽象类X和一些对该类进行扩展的类,分别称为ABC

In some other class Y I have a few methodcalls that depend on the type of the class. 在其他一些类Y我有一些依赖于类类型的方法调用。 The if-else statement looks like this: 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 "";
    }
}

Note that all methods have the same name ( doStuff() ) but depending on the class (and sometimes flag) call a different method implementation of that method. 请注意,所有方法都具有相同的名称( doStuff() ),但根据类(有时是标记)的不同,该方法的实现方式也不同。 Of course this looks horrible and gets immensely complicated once the classed that are extended from X increase. 当然,一旦从X扩展的分类增加,这看起来就很可怕,并且变得非常复杂。

Is there any way that I can somehow create an intermediate Interface that (or something else) that takes care of the majority (or all) of the if-else statements? 我有什么办法可以创建一个中间接口(或其他东西)来处理大多数(或全部)if-else语句?

First take these methods out of here, and put them in the A, B and C class respectively, implementing the X interface. 首先,将这些方法从此处取出,并将它们分别放入A,B和C类中,以实现X接口。

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

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

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

Then: 然后:

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

can just be x.doStuff(); 可以只是x.doStuff(); (you don't even have to pass the A, B, C because that will be this inside the method. The flag you'll have to mess around with depending more specifically on your code. for example, the other 2 doStuff methods could accept the flag as well, but just ignore it) (你甚至不必须通过A,B,C,因为这将是this里面的方法,标志,你必须惹根据更具体的您的代码周围。例如,其他2层doStuff的方法可以也接受该标志,但忽略它)

APPROACH 1 方法1

Use the state pattern . 使用状态模式 It takes care of your problem and eliminates the if s and else s. 它可以解决您的问题并消除ifelse

Here is the java example . 这是java示例

The state pattern delegates the methods calls to objects that implement the same interface but with different behaviour. 状态模式将方法调用委托给实现相同接口但行为不同的对象。

State pattern 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!");
    }
}

As you can see, the class Girlfriend has no if s and else s. 如您所见,“ Girlfriend ”类没有ifelse It looks pretty clean. 看起来很干净。

The class Girlfriend corresponds to your abstract class X , the classes Normal and Angry correspond to A , B and C . Girlfriend ”类对应于您的abstract class X ,“ Normal和“ Angry ”类对应于ABC

Your class Y then directly delegates to X without checking any cases. 然后,您的Y类将直接委托给X,而无需检查任何情况。

APPROACH 2 方法2

Use the command pattern . 使用命令模式 You could then hand over a command object to Y s doStuff() method and just execute it. 然后,您可以将命令对象移交给YdoStuff()方法,然后执行它。

What about implementing a Handler interface then map it by supported type: 如何实现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
      }
}

What about some 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 "";
   }
}

This can be a difficult problem. 这可能是一个困难的问题。 I think Cruncher's solution, add doStuff to X and override it in A , B , C , is the simplest and best solution when it's appropriate. 我认为Cruncher的解决方案是在合适的情况下将doStuff添加到X并在ABC覆盖它, A是最简单,最好的解决方案。 However, it isn't always appropriate, because of the Single responsibility principle . 但是,由于单一责任原则 ,它并不总是合适的。 (I think that's the correct term. My apologies if I get some terminology wrong, I'm not entirely up-to-date on all of the terms.) (我认为这是正确的术语。如果我对某些术语有误,我深表歉意,我对所有术语都不完全是最新的。)

The idea is that you shouldn't necessarily doStuff to X if it has nothing to do with the purpose of X . 这个想法是,你并不一定doStuffX ,如果它有无关的目的X If X and Y are part of the same "team", ie they've been both set up to serve the purpose of one particular application, then it's probably OK. 如果XY是同一个“团队”的一部分,即它们都已被设置为满足一个特定应用程序的目的,那么可能就可以了。

But suppose you have an abstract Shape class that has subclasses Circle , Square , Undecagon , RandomBlob , etc. There will be some methods that belong in the Shape class that would be useful to any application that uses the Shape class. 但是,假设你有一个抽象的Shape有子类CircleSquareUndecagonRandomBlob等方面将是在属于一些方法Shape类,这将是到使用的任何应用Shape类。 But now say you are writing a game that uses some of those shapes, and you want a polymorphic operation that determines what happens when the shape gets eaten by a flying monkey. 但是现在说您正在编写一个使用其中某些形状的游戏,并且您想要一个多态操作,该操作确定当飞猴吃掉该形状时会发生什么。 You wouldn't want to add an abstract computeEatenByFlyingMonkey method to your Shape class, even if the class were your own creation and not in someone else's library, because that would be just too specific for a class that could be generally used for other purposes than this one game. 即使该类是您自己创建的,也不在其他人的库中,您也不想在Shape类中添加抽象的computeEatenByFlyingMonkey方法,因为这对于通常用于其他用途的类而言太具体了这个游戏。

I can think of a couple ways to approach this. 我可以想到几种解决方法。

If it's not appropriate (or not possible) to add doStuff to X , but if A , B , and C are more closely connected to your application so that adding doStuff to them is appropriate, you can add another class: 如果不适合(或不可能)将doStuff添加到X ,但是如果ABC与您的应用程序联系更紧密,那么将doStuff添加到它们是适当的,则可以添加另一个类:

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) { ... }
}

and so on for every other class. 以此类推。 ( XWithStuff is just an example name; in real life, a name that contains both "X" and some reference to the application or purpose is probably better.) (PS I don't know why you're using Boolean instead of boolean but I'm leaving it that way in case there's a good reason.) XWithStuff只是一个示例名称;在现实生活中,同时包含“ X”和对应用程序或用途的一些引用的名称可能会更好。)(PS我不知道为什么您使用Boolean而不是boolean但是如果有充分的理由,我将以这种方式离开。)

If it's also not appropriate or not possible to add doStuff to A , B , and C , here's a possible solution: 如果也不合适或不可能将doStuff添加到ABC ,则可能的解决方法是:

public interface StuffInterface {
    public void doStuff (Boolean flag);
}

public class AWithStuff extends A implements StuffInterface {
    @Override
    public void doStuff (Boolean flag) { ... }
}

and then in your program create objects of class AWithStuff instead of A , etc. To call doStuff on an X : 然后在程序中创建类AWithStuff对象而不是A ,等等。要在X上调用doStuff

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

If that's not an option and you have to deal directly with A , B , etc., and you can't add doStuff to those classes, then any solution will be a bit hacky. 如果这不是一个选择,并且您必须直接处理AB等,并且不能将doStuff添加到这些类中,那么任何解决方案都将有点笨拙。 If you don't want to use if - then - else , you could look into the visitor pattern , or you could conceivably create a HashMap<Class<?>,Interf> that would map A.class , B.class , etc., to some interface object that calls the correct doStuff . 如果您不希望使用if - then - else ,则可以查看访客模式 ,或者可以想象创建一个HashMap<Class<?>,Interf>来映射A.classB.class等。到调用正确的doStuff某些接口对象。 But I haven't worked out the details. 但是我还没有弄清楚细节。 (Actually, the "visitor pattern" probably wouldn't be appropriate unless you have some sort of complex structure composed of objects of type X.) (实际上,除非您具有某种由X类型的对象组成的复杂结构,否则“访客模式”可能不合适。)

Separate a DoStuffOperation , create the relative factory and use them. 分开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);
  }
}

In this way you don't have to refactor call to Y.doStuff() or X and derived classes. 这样,您不必重构对Y.doStuff()X和派生类的调用。 You can't remove at all some sort of instanceof to decide which implementation of doStuff() use unless X classes implements a DoStuffCreator interface like: 除非X类实现了DoStuffCreator接口,否则您根本无法删除任何instanceof来决定使用哪种doStuff()实现:

interface DoStuffCreator {
  DoStuffOperation getDoStuffOperation(boolean flag);
}

X and A are your classes. XA是您的课程。 You can also construct using reflection or other automatic way (external property file and so on). 您还可以使用反射或其他自动方式(外部属性文件等)进行构造。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM