簡體   English   中英

Java:擴展類的if-else實例

[英]Java: If-else instanceof extended classes

我有一個抽象類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語句?

首先,將這些方法從此處取出,並將它們分別放入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

使用狀態模式 它可以解決您的問題並消除ifelse

這是java示例

狀態模式將方法調用委托給實現相同接口但行為不同的對象。

狀態模式示例:

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 ,“ Normal和“ Angry ”類對應於ABC

然后,您的Y類將直接委托給X,而無需檢查任何情況。

方法2

使用命令模式 然后,您可以將命令對象移交給YdoStuff()方法,然后執行它。

如何實現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
      }
}

雙重派遣怎么辦?

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 "";
   }
}

這可能是一個困難的問題。 我認為Cruncher的解決方案是在合適的情況下將doStuff添加到X並在ABC覆蓋它, A是最簡單,最好的解決方案。 但是,由於單一責任原則 ,它並不總是合適的。 (我認為這是正確的術語。如果我對某些術語有誤,我深表歉意,我對所有術語都不完全是最新的。)

這個想法是,你並不一定doStuffX ,如果它有無關的目的X 如果XY是同一個“團隊”的一部分,即它們都已被設置為滿足一個特定應用程序的目的,那么可能就可以了。

但是,假設你有一個抽象的Shape有子類CircleSquareUndecagonRandomBlob等方面將是在屬於一些方法Shape類,這將是到使用的任何應用Shape類。 但是現在說您正在編寫一個使用其中某些形狀的游戲,並且您想要一個多態操作,該操作確定當飛猴吃掉該形狀時會發生什么。 即使該類是您自己創建的,也不在其他人的庫中,您也不想在Shape類中添加抽象的computeEatenByFlyingMonkey方法,因為這對於通常用於其他用途的類而言太具體了這個游戲。

我可以想到幾種解決方法。

如果不適合(或不可能)將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) { ... }
}

以此類推。 XWithStuff只是一個示例名稱;在現實生活中,同時包含“ X”和對應用程序或用途的一些引用的名稱可能會更好。)(PS我不知道為什么您使用Boolean而不是boolean但是如果有充分的理由,我將以這種方式離開。)

如果也不合適或不可能將doStuff添加到ABC ,則可能的解決方法是:

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

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

然后在程序中創建類AWithStuff對象而不是A ,等等。要在X上調用doStuff

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

如果這不是一個選擇,並且您必須直接處理AB等,並且不能將doStuff添加到這些類中,那么任何解決方案都將有點笨拙。 如果您不希望使用if - then - else ,則可以查看訪客模式 ,或者可以想象創建一個HashMap<Class<?>,Interf>來映射A.classB.class等。到調用正確的doStuff某些接口對象。 但是我還沒有弄清楚細節。 (實際上,除非您具有某種由X類型的對象組成的復雜結構,否則“訪客模式”可能不合適。)

分開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和派生類的調用。 除非X類實現了DoStuffCreator接口,否則您根本無法刪除任何instanceof來決定使用哪種doStuff()實現:

interface DoStuffCreator {
  DoStuffOperation getDoStuffOperation(boolean flag);
}

XA是您的課程。 您還可以使用反射或其他自動方式(外部屬性文件等)進行構造。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM