2015-05-04 77 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(); 
     } 
    } 
} 

这个模式也可以很容易地“撤销”功能和“重试”,以及记忆化(高速缓存的结果,使他们不必重新运行)。