方法数组:适配器模式?
Array of methods: Adapter Pattern?
问题描述:
我希望能够将方法列表传递给其他 classes,其中这些方法只在一个 class 中定义。如果方法,其中一些具有输入参数和非空 return 类型,在一个 class 中定义,我希望能够传递其中一些方法的列表,可能有重复项,如其他 class 的构造函数的参数。
代码说明:
下面的代码是一个粗略的示例,如果它有损于主要目标,则可以忽略。另一个例子,除了下面的例子,方法是 int Add(int n1, int n2), int Subtract(int n1, int n2), Multiply 等,接口有一个方法叫做int MathOperation(int n1,int n2)。
尝试解决问题:
适配器模式似乎具有我正在寻找的功能,但我只看到接口中的方法没有输入或输出参数的示例。下面发布了我为这个问题编写的示例实现。
问题类比:
您有一个随机图片生成器 Web 服务。可以对图像应用 30 种突变。客户端连接并单击 "generate" 按钮,其中一些函数的随机列表将传递给 Web 服务中的其他一些 class,然后使用它自己的数据继续 运行 这些函数同时还收集并可能重新使用 return 值来生成一些变异的猫图像。它不能只显式调用另一个 class 中的方法,因为该过程需要在 运行 时间随机完成。这就是为什么我倾向于生成随机方法列表的想法,这些方法在单击 'generate' 按钮时按顺序执行。
希望我已经说清楚了。
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;
}
}
@John 这是我解决您的问题的方法。
我使用了 MathOperations 的案例来简化它。我首先认为我最好在 SomeClass 之外拥有接口,例如:
public interface MathOperable {
public int mathOperation(int n1, int n2);
}
我创建了两个实现此接口的 classes 示例和一个在 SomeClass 中的匿名实现(我做了一个加法、乘法和一个匿名 "Substract")
public class Add implements MathOperable {
public int mathOperation(int n1, int n2) {
return n1 + n2;
}
public String toString() {
return "<addition>";
}
}
重写 toString() 只是为了让我将在 post.
末尾展示的示例更具可读性
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 class,它包含一个 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 class)
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);
}
}
}
}
我有一个简单的测试 class,其中包含连接两个 classes
的主电源
public class SomeTestClass {
public static void main(String[] args) {
SomeClass classe = new SomeClass();
classe.contactSomeOtherClass();
}
}
现在举几个执行的例子:
还有一张图!
希望对您有所帮助!
虽然@alainlompo 有一般的想法,Java 8 通过使用诸如 BiConsumer
(对于双打)甚至只是 Consumer
之类的东西大大简化了 [=39] =] 对象。事实上,你可以真的发疯,并且有一个方法接受可变参数 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);
虽然这样应用它似乎有点尴尬(因为它是一个适配器模式),但您可以很容易地看到它如何应用于 class 主体:
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);
当然,你不需要可变参数,只要传递一个 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 中的函数 API 提供了更多的灵活性,使得像这样的复杂问题的编写变得非常简单。
好的,我将成为 "that guy"...理解问题但仍然要求重述问题的人,因为我认为您走错了路。所以,请耐心等待:如果你喜欢你所看到的,那很好;如果没有,我明白了。
基本上,intent/motivation/purpose 与 "adapter" 适合的不同。命令模式更合适。
但首先,更一般地说,设计 "elements of reusable software" 的目标之一(来自原始 GOF 设计模式书的标题)是您不想 修改 代码,当您 添加 功能时;相反,您想 添加 代码而不触及现有功能。所以,当你有:
public class Toolbox {
public void hammer() { ... }
}
并且您想在工具箱中添加一把螺丝刀,这很糟糕:
public class Toolbox {
public void hammer() { ... }
public void screwdriver() { ... }
}
理想情况下,所有现有代码都将保持不变,您只需添加一个新的 Screwdriver 编译单元(即添加一个新文件)和一个单元测试,然后测试现有代码的回归(应该不太可能,因为 none 现有代码已更改)。例如:
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); ... }
}
希望应该清楚如何将其扩展到您的示例。 Worker 变成了 straight-foward 工具列表(或者,为了清楚起见,而不是 "Tool" ,只称它为 "Command")。
public class Worker {
public List<Command> actionList;
....
public void work() {
for(...) {
action.execute();
}
}
}
此模式还允许简单的 "undo" 功能和 "retry",以及记忆(缓存结果,因此它们不必 re-run)。
问题描述: 我希望能够将方法列表传递给其他 classes,其中这些方法只在一个 class 中定义。如果方法,其中一些具有输入参数和非空 return 类型,在一个 class 中定义,我希望能够传递其中一些方法的列表,可能有重复项,如其他 class 的构造函数的参数。
代码说明: 下面的代码是一个粗略的示例,如果它有损于主要目标,则可以忽略。另一个例子,除了下面的例子,方法是 int Add(int n1, int n2), int Subtract(int n1, int n2), Multiply 等,接口有一个方法叫做int MathOperation(int n1,int n2)。
尝试解决问题: 适配器模式似乎具有我正在寻找的功能,但我只看到接口中的方法没有输入或输出参数的示例。下面发布了我为这个问题编写的示例实现。
问题类比: 您有一个随机图片生成器 Web 服务。可以对图像应用 30 种突变。客户端连接并单击 "generate" 按钮,其中一些函数的随机列表将传递给 Web 服务中的其他一些 class,然后使用它自己的数据继续 运行 这些函数同时还收集并可能重新使用 return 值来生成一些变异的猫图像。它不能只显式调用另一个 class 中的方法,因为该过程需要在 运行 时间随机完成。这就是为什么我倾向于生成随机方法列表的想法,这些方法在单击 'generate' 按钮时按顺序执行。
希望我已经说清楚了。
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;
}
}
@John 这是我解决您的问题的方法。
我使用了 MathOperations 的案例来简化它。我首先认为我最好在 SomeClass 之外拥有接口,例如:
public interface MathOperable {
public int mathOperation(int n1, int n2);
}
我创建了两个实现此接口的 classes 示例和一个在 SomeClass 中的匿名实现(我做了一个加法、乘法和一个匿名 "Substract")
public class Add implements MathOperable {
public int mathOperation(int n1, int n2) {
return n1 + n2;
}
public String toString() {
return "<addition>";
}
}
重写 toString() 只是为了让我将在 post.
末尾展示的示例更具可读性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 class,它包含一个 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 class)
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);
}
}
}
}
我有一个简单的测试 class,其中包含连接两个 classes
的主电源public class SomeTestClass {
public static void main(String[] args) {
SomeClass classe = new SomeClass();
classe.contactSomeOtherClass();
}
}
现在举几个执行的例子:
还有一张图!
希望对您有所帮助!
虽然@alainlompo 有一般的想法,Java 8 通过使用诸如 BiConsumer
(对于双打)甚至只是 Consumer
之类的东西大大简化了 [=39] =] 对象。事实上,你可以真的发疯,并且有一个方法接受可变参数 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);
虽然这样应用它似乎有点尴尬(因为它是一个适配器模式),但您可以很容易地看到它如何应用于 class 主体:
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);
当然,你不需要可变参数,只要传递一个 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 中的函数 API 提供了更多的灵活性,使得像这样的复杂问题的编写变得非常简单。
好的,我将成为 "that guy"...理解问题但仍然要求重述问题的人,因为我认为您走错了路。所以,请耐心等待:如果你喜欢你所看到的,那很好;如果没有,我明白了。
基本上,intent/motivation/purpose 与 "adapter" 适合的不同。命令模式更合适。
但首先,更一般地说,设计 "elements of reusable software" 的目标之一(来自原始 GOF 设计模式书的标题)是您不想 修改 代码,当您 添加 功能时;相反,您想 添加 代码而不触及现有功能。所以,当你有:
public class Toolbox {
public void hammer() { ... }
}
并且您想在工具箱中添加一把螺丝刀,这很糟糕:
public class Toolbox {
public void hammer() { ... }
public void screwdriver() { ... }
}
理想情况下,所有现有代码都将保持不变,您只需添加一个新的 Screwdriver 编译单元(即添加一个新文件)和一个单元测试,然后测试现有代码的回归(应该不太可能,因为 none 现有代码已更改)。例如:
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); ... }
}
希望应该清楚如何将其扩展到您的示例。 Worker 变成了 straight-foward 工具列表(或者,为了清楚起见,而不是 "Tool" ,只称它为 "Command")。
public class Worker {
public List<Command> actionList;
....
public void work() {
for(...) {
action.execute();
}
}
}
此模式还允许简单的 "undo" 功能和 "retry",以及记忆(缓存结果,因此它们不必 re-run)。