方法数组:适配器模式?

时间:2015-05-04 05:08:54

标签: java design-patterns adapter

问题描述: 我希望能够将方法列表传递给其他类,其中只在一个类中定义了方法。如果方法(其中一些具有输入参数和非void返回类型)在一个类中定义,我希望能够将其中一些的列表(可能有重复项)作为参数传递给其他类&#39 ; s构造函数。

代码说明: 下面的代码是一个粗略的例子,如果它有损于主要目标,可以忽略。另一个例子,除了下面的一个例子,将是一个方法,其中方法是int Add(int n1,int n2),int Subtract(int n1,int n2),Multiply等..并且接口有一个方法叫做int MathOperation(int n1,int n2)。

尝试解决问题: 适配器模式似乎具有我正在寻找的功能,但我只看到了接口中的方法没有输入或输出参数的示例。我为此问题编写的示例实现发布在下面。

问题类比: 你有一个随机的图片生成器Web服务。有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;
    }
}

3 个答案:

答案 0 :(得分:8)

虽然@alainlompo有一般的想法,但Java 8通过使用BiConsumer(对于双精度)甚至只是Consumer等类对象来大大简化了这一点。事实上,你可以真的疯狂,并有一个方法接受varargs lambdas:

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);

虽然看起来有点尴尬(因为它是一个适配器模式),你可以很容易地看到它如何适用于类体:

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);

当然,你不需要varargs,它也可以简单地传递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!

Java 8中的Function API提供了更多的灵活性,使得编写这样的复杂问题非常简单。

答案 1 :(得分:4)

@John这里是我如何找到解决问题的方法。

我使用MathOperations的情况使其更简单。我认为首先我会更好地使用SomeClass之外的接口:

public interface MathOperable {

    public int mathOperation(int n1, int n2);

}

我创建了两个实现此接口的类的示例和一个SomeClass中的匿名实现(我做了一个Add,Multiply和一个匿名&#34; Substract&#34;)

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类,它包含一个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(可能对应于你的Worker类)

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);

            }
        }
    }

}

我有一个简单的测试类,其中包含一个连接两个类的main

public class SomeTestClass {

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

}

现在举几个例子:

example1

另一个例子!

example 2

我希望这可能有所帮助!

答案 2 :(得分:2)

好吧,我将成为那个&#34; ...那个理解这个问题的人,但无论如何要求重述这个问题因为我认为你走错了路。所以,忍受我:如果你喜欢你所看到的,那就太好了;如果没有,我理解。

基本上,你有不同的意图/动机/目的而不是&#34;适配器&#34;很适合。命令模式更适合。

但首先,更一般地说,设计可重用软件的元素的目标之一&#34; (来自原始GOF设计模式书的标题)是,当您添加功能时,您不想修改代码;相反,您希望添加代码而不触及现有功能。所以,当你有:

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

并且您想在工具箱中添加螺丝刀,这很糟糕:

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

相反,理想情况下,所有现有代码都将保持不变,您只需添加一个新的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)
}

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

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

希望应该清楚如何将此扩展到您的示例中。工人变成了直接的工具列表(或者,为了清楚起见,而不是&#34;工具&#34;,只需将其命名为&#34;命令&#34;)。

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

此模式还允许轻松&#34;撤消&#34;功能和&#34;重试&#34;,以及memoization(缓存结果,因此他们不必重新运行)。