2015-05-04 71 views
13

問題描述: 我希望能夠將方法列表傳遞給只有一個類定義方法的其他類。如果方法(其中一些具有輸入參數和非無效返回類型)在一個類中定義,我希望能夠將其中一些列表(可能具有重複)作爲其他類的構造函數的參數傳遞給它們。數組方法:適配器模式?

代碼描述: 下面的代碼是一個粗例如,如果它從主目標減損可以忽略。另一個例子,除了下面的例子之外,其中的方法是int Add(int n1,int n2),int Subtract(int n1,int n2),Multiply等等。 int MathOperation(int n1,int n2)。

嘗試解決問題: 適配器模式似乎有我需要的功能,但我只看到示例,其中在接口中的方法沒有輸入或輸出參數。我爲這個問題編寫的一個示例實現在下面發佈。

問題比喻: 你有一個隨機圖片發生器的網絡服務。有30個突變可以應用於圖像。客戶端連接並點擊一個「生成」按鈕,其中一些函數的隨機列表被傳遞給Web服務中的其他類,然後繼續使用它自己的數據運行這些函數,同時收集並可能重新使用返回值來生成一些變異的貓圖像。它不能顯式調用其他類中的方法,因爲該過程需要在運行時隨機完成。這就是爲什麼我傾向於生成隨機列表的方法,當點擊'生成'按鈕時按順序執行。

我希望我已經清楚。

public class SomeClass { 
    ... 
    public double UseWrench(double torque, boolean clockwise) { ... } 
    public double UsePliers(double torque, boolean clockwise) { ... } 
    public double UseScrewDriver(double torque, boolean clockwise) { ... } 
    public boolean UseWireCutters(double torque) { ... } 

    interface IToolActions { 
     double TurnFastener(double torque, boolean clockwise); 
     boolean CutWire(double torque); 
    } 

    private IToolActions[] toolActions = new IToolActions[] { 
     new IToolActions() { public double TurnFastener(double torque, boolean clockwise) { double UseWrench(double torque, boolean clockwise); } }, 
     new IToolActions() { public double TurnFastener(double torque, boolean clockwise) { double UsePliers(double torque, boolean clockwise); } }, 
     new IToolActions() { public double TurnFastener(double torque, boolean clockwise) { double UseScrewDriver(double torque, boolean clockwise); } }, 
     new IToolActions() { public boolean CutWire(double torque) { boolean UseWireCutters(double torque); } }, 
    }; 
} 

public class Worker<T> { 

    public List<? extends IToolActions> toolActions; 

    public Worker(List<? extends IToolActions> initialToolSet){ 
     toolActions = initialToolActions; 
    } 
} 
+0

我想我明白你的意思,但我需要更好的問題描述或更好的例子。保留似乎是方法參數和返回值。您將不得不定義一些方法來處理這些問題,並且您的特定要求將決定如何工作。沒有通用的「X」總能解決這類問題。也許可以解釋你自己選擇參數有什麼問題,這可能是一個開始。 – markspace

+0

如何使用'List '? – 2015-05-04 06:08:25

+0

如果您希望它快速且動態,您可以使用反射並僅列出該類中的所有方法,獲取它們的參數並將它們填充到一個循環中。但是,這不會像下面的好界面那樣乾淨 – Falco

回答

4

@John這裏是我如何接近你的問題的解決方案。

我用MathOperations的例子來簡化它。我覺得首先,我會更好,有接口SomeClass的外面,如:

public interface MathOperable { 

    public int mathOperation(int n1, int n2); 

} 

我創造的實現了這個接口和一個匿名裏面執行SomeClass的(我做了一個加法,乘法和一個匿名的「類兩個例子。減去「)

public class Add implements MathOperable { 

    public int mathOperation(int n1, int n2) { 

     return n1 + n2; 
    } 

    public String toString() { 
     return "<addition>"; 
    } 

} 

的toString的首要()僅僅是爲了讓更多的可讀性,我會告訴我的文章的結尾部分的示例的目的。

public class Multiply implements MathOperable { 

    public int mathOperation(int n1, int n2) { 
     // TODO Auto-generated method stub 
     return n1 * n2; 
    } 

    public String toString() { 
     return "<multiplication>"; 
    } 

} 

這裏是我SomeClass的類,它contans一個getRandomListOfOperations,在這裏我模擬時,按鈕上的點擊完成

public class SomeClass { 

    private static MathOperable addition = new Add(); 
    private static MathOperable multiplication = new Multiply(); 

    // Anonymous substraction 
    private static MathOperable substraction = new MathOperable() { 

     public int mathOperation(int n1, int n2) { 
      // TODO Auto-generated method stub 
      return n1-n2; 
     } 

     public String toString() { 
      return "<substraction>"; 
     } 

    }; 


    public List<MathOperable> getRandomListOfOperations() { 

     // We put the methods in an array so that we can pick them up later  randomly 
     MathOperable[] methods = new MathOperable[] {addition,  multiplication, substraction}; 
     Random r = new Random(); 

     // Since duplication is possible whe randomly generate the number of  methods to send 
     // among three so if numberOfMethods > 3 we are sure there will be  duplicates 
     int numberOfMethods = r.nextInt(10); 
     List<MathOperable> methodsList = new ArrayList<MathOperable>(); 

     // We pick randomly the methods with duplicates 
     for (int i = 0; i < numberOfMethods; i++) { 
      methodsList.add(methods[r.nextInt(3)]); 

     } 

     return methodsList;  
    } 

    public void contactSomeOtherClass() { 
     new SomeOtherClass(getRandomListOfOperations()); 
    } 
} 

現在,這裏是我的SomeOtherClass發生了什麼(這可能符合您的工類)

public class SomeOtherClass<T extends MathOperable> { 

    Random r = new Random(); 

    List<T> operations; 

    public SomeOtherClass(List<T> operations) { 
     this.operations = operations; 

     runIt(); 
    } 

    public void runIt() { 

     if (null == operations) { 
      return; 
     } 

     // Let's imagine for example that the new result is taken as  operand1 for the next operation 
     int result = 0; 

     // Here are examples of the web service own datas 
     int n10 = r.nextInt(100); 
     int n20 = r.nextInt(100); 

     for (int i = 0; i < operations.size(); i++) { 

      if (i == 0) { 
       result = operations.get(i).mathOperation(n10, n20); 
       System.out.println("Result for operation N " + i + " = " +  result); 
      } else { 

       // Now let's imagine another data from the web service  operated with the previous result 
       int n2 = r.nextInt(100); 
       result = operations.get(i).mathOperation(result, n2); 
       System.out.println("Current result for operation N " + i + "  which is " + operations.get(i) +" = " + result); 

      } 
     } 
    } 

}

我有一個簡單的TE它包含一個主ST類兩類

public class SomeTestClass { 

    public static void main(String[] args) { 
     SomeClass classe = new SomeClass(); 
     classe.contactSomeOtherClass(); 
    } 

} 

連接現在執行的幾個例子:

example1

而另一例證!

example 2

我希望這可以幫助!

+0

只是一個建設性的批判:比較我的回答,重新:命令模式;這實際上與'public interface MathOperable'類似。具體來說,請注意,帶'int'的雙參數'mathOperation'可能是一個問題。作爲一個通用的設計變更,你可以通過ctor傳遞參數(如Commands),並使'mathOperation'不需要參數。 「int」結果仍然存在問題;但是,它可能會返回另一個MathOp或某種類型的「結果」對象(但是組合將變得很混亂...... OO數學是一個難題。) – michael

+0

@michael_n,謝謝,這真是一個很棒的評論!起初,我開始在這種模式中進行挖掘,但是我想要採取一種工作方法,然後將其匹配到其中一種模式。 – alainlompo

8

雖然@alainlompo有一般的想法,但Java 8通過使用諸如BiConsumer(用於雙打)或甚至僅僅用於類對象的Consumer來簡化這一點。事實上,你可以去真的瘋了,有一個方法接受可變參數的lambda表達式:

public class SomeClass 

    public double useWrench(double torque, boolean clockwise) { ... } 
    public double usePliers(double torque, boolean clockwise) { ... } 
    public double useScrewDriver(double torque, boolean clockwise) { ... } 
    public boolean useWireCutters(double torque) { ... } 

} 

public class Worker { 

    @SafeVarargs 
    public Worker(SomeClass example, Consumer<? extends SomeClass>... operations) { 
     for (Consumer bc : operations) { 
      bc.accept(example); 
     } 
    } 
} 

然後,這是很容易簡化爲:

SomeClass c = new SomeClass(); 
new Worker(c, SomeClass::useWrench, SomeClass:usePliers, SomeClass::useScrewDriver, SomeClass::useWireCutters); 

雖然看起來有點彆扭應用它像(由於它是一個適配器模式),你可以很容易地看到這是如何應用到A級機體:

public class SomeClass 

    public double useWrench(double torque, boolean clockwise) { ... } 
    public double usePliers(double torque, boolean clockwise) { ... } 
    public double useScrewDriver(double torque, boolean clockwise) { ... } 
    public boolean useWireCutters(double torque) { ... } 

    @SafeVarargs 
    public void operate(Consumer<? extends SomeClass>... operations) { 
     for (Consumer<? extends SomeClass> bc : operations) { 
      bc.accept(example); 
     } 
    } 

} 

//Elsewheres 
SomeClass c = new SomeClass(); 
c.operate(SomeClass::useWrench, SomeClass:usePliers, SomeClass::useScrewDriver, SomeClass::useWireCutters); 

當然,你不需要可變參數,它會工作菊st以及簡單傳遞一個Collection

但是等待還有更多!

如果你想要一個結果,你甚至可以通過Function使用自迴歸方法,如:

public class SomeClass { 

    public double chanceOfSuccess(Function<? super SomeClass, ? extends Double> modifier) { 
     double back = /* some pre-determined result */; 
     return modifier.apply(back); //apply our external modifier 
    } 

} 

//With our old 'c' 
double odds = c.chanceOfSuccess(d -> d * 2); //twice as likely! 

有從功能API在Java 8中提供這麼多的靈活性,使得複雜的問題像這樣簡單寫得非常簡單。

+1

這可能是['ToDoubleFunction'](https://docs.oracle.com/javase/8/docs/api/java/util/function/ToDoubleFunction.html)。 –

+0

這是一個很好的例子,爲什麼更多的人可能會開始使用Scala(Clojure等)的語言。 Java作爲一種語言只能發展很多(...自從jdk 1.0開始,我一直在使用它;它發展很多)。 – michael

2

好的,我將成爲「那個人」......理解這個問題的人,但仍要求重申問題,因爲我認爲你走錯了路。所以,忍耐着我:如果你喜歡你所看到的,那麼很棒;如果不是,我明白。

基本上,你有與「適配器」適合的不同的意圖/動機/目的。命令模式更合適。

但首先,更普遍的設計「的可複用軟件元素」(從原來的GOF設計模式書的標題)的目標之一是,你不想修改代碼時,你添加功能;相反,你想添加代碼而不觸及現有的功能。所以,當你有:

public class Toolbox { 
    public void hammer() { ... } 
} 

,你想螺絲刀添加到您的工具箱中,這是不好的:

public class Toolbox { 
    public void hammer() { ... } 
    public void screwdriver() { ... } 
} 

相反,理想情況下,所有現有的代碼保持不變,你會只添加一個新的螺絲刀編譯單元(即添加一個新文件)和一個單元測試,然後測試現有的迴歸代碼(這應該不太可能,因爲現有的代碼沒有改變)。例如:

public class Toolbox { 
    public void useTool(Tool t) { t.execute(); ...etc... } 
} 

public interface Tool { // this is the Command interface 
    public void execute() // no args (see ctors) 
} 

pubic Hammer implements Tool { 
    public Hammer(Nail nail, Thing t) // args! 
    public void execute() { nail.into(t); ... } 
} 

pubic Screwdriver implements Tool { 
    public Screwdriver(Screw s, Thing t) 
    public void execute() { screw.into(t); ... } 
} 

希望它應該變得清晰如何擴展到您的示例。工作者變得直截了當的工具列表(或者爲了清楚起見,而不是「工具」,只是將其稱爲「命令」)。

public class Worker { 
    public List<Command> actionList; 
    .... 
    public void work() { 
     for(...) { 
     action.execute(); 
     } 
    } 
} 

這個模式也可以很容易地「撤銷」功能和「重試」,以及記憶化(高速緩存的結果,使他們不必重新運行)。