设计模式,用于处理不同类型的指令以进行不同类型的方法调用

时间:2013-11-20 08:41:21

标签: java oop design-patterns

我在互联网上看过很多帖子但是我无法适应我的问题。

我想分析一个可以调用不同类型的调用的指令。例如,

指令类型,例如,可以是type-1,type-2等。被调用的方法可以是method1,method2等。因此存在一种多对多的关系。

instType1 method1
instType2 method1
instType2 method3
instType3 method3 
...

我已经考虑过每种指令类型和每种方法类型的一个处理程序,但无论哪种方式,我都必须在每个处理程序中为其他事情编写检查。例如,在instruction-type1-Handler中,我将检查被调用的类型是method1还是method2等。

有没有简单或更干净的方法来做到这一点?

修改-1

一个具体的例子是

invoke-virtual v7, v4, Ljava/io/FileOutputStream;->write

这里我可以有不同类型的invoke- *调用和不同的Java API调用集。

我想为每个invoke-type和called-API类型编写一个单独的处理程序。并且假设如果我将此指令赋予invoke-virtual-handler,则必须先检查调用类型然后再处理它。每个处理程序中都有相同的逻辑。

有没有办法在不同的处理程序中避免使用相同的代码?

2 个答案:

答案 0 :(得分:1)

我同意Kayaman的Command pattern设计。尽管如此,我认为通过Java的Reflection API,你也知道另一种方式也很好。假设您想要的是能够执行以下操作:

evaluate(instruction, call);

无需在方法中评估(...)一大堆切换如果elses 。我建议使用Java的API反射,这意味着您可以根据需要添加任意数量的指令和调用,而无需更改评估它们的代码(功能)(核心原因)面向对象编程)。

希望我在以下解释中不会失去你。让我们从声明指令调用抽象类开始。

public abstract class Call {
String name;

public Call(String name){
    this.name=name;
}

public String evaluate(){
    return this.name;
}

 }

public abstract class Instruction {
String name;

public Instruction(String name){
    this.name=name;
}

public String evaluate(){
    return this.name;
}   
}

我已经放置了非常基本的功能,只是为了看它是否有效。现在让我们创建几个不同的类,这些类从“指令”和“调用”扩展,以便我们可以使用。

public class Instruction1 extends Instruction {

public Instruction1(String name) {
    super(name);
}

}

public class Call1 extends Call {

public Call1(String name) {
    super(name);
}

}

我不会发布所有课程,因为它的代码与Instruction1和Call1相同。所以我创建的类是: Instruction1,Instruction2,Instruction3,Call1,Call2,Call3。

现在我们已经拥有了,我们将创建一个负责处理组合(指令,调用)的正确调用的类。您的评估员,如果您愿意的话。

public class InstructionCallsEvaluator {
public String evaluate(Instruction1 instruction, Call1 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction1 instruction, Call2 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction1 instruction, Call3 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction2 instruction, Call1 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction2 instruction, Call2 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction2 instruction, Call3 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction3 instruction, Call1 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction3 instruction, Call2 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

public String evaluate(Instruction3 instruction, Call3 call){
    return this.evaluate( (Instruction) instruction, (Call) call);
}

private String evaluate(Instruction instruction, Call call){
    return instruction.evaluate() + "  " + call.evaluate();
}
}

很长呃?您可以在这里添加所有可能的组合。因此,如果您要添加新的#类,则必须来到此评估程序并添加相应的组合。 如果与命令模式实现相比,您可以将其视为缺点。

我添加private String evaluate(Instruction instruction, Call call)的原因是因为如果输出的格式发生了变化,我只需要更改此方法,而不是所有其他方法。

此外,我在调用此方法之前向上转换方法参数的原因是因为否则您将获得一个永不停止的循环,从而导致stackOverflow异常:

public String evaluate(Instruction3 instruction, Call1 call){
    return this.evaluate(instruction, call);
}

如果我这样离开并执行,当它调用 this.evaluate(...)时,它最终会在永无止境的情况下回忆public String evaluate(Instruction3 instruction, Call1 call)循环。

我之所以展示这种实施方式,是因为我假设您想要确定哪种组合指令,调用。否则,我会使用命令模式。

显然,我在评估(...)** 中所做的事情非常简单,因为我不确切地知道你要做什么。你必须根据自己的情况调整它。

现在设置了处理程序,让我们去负责正确调用它的类,而不用 if elses 切换。你的处理程序,如果你愿意的话。

public class InstructionHandler {

public String evaluate(Instruction instruction, Call call) {
    try {
        return  useEvaluator(InstructionCallsEvaluator.class, instruction, call);
    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | 
             InvocationTargetException | NoSuchMethodException ex) {
        Logger.getLogger(InstructionHandler.class.getName()).log(Level.SEVERE, null, ex);
    }
    return "Something went wrong when calling Evaluator";
}


private String useEvaluator(Class<InstructionCallsEvaluator> instructionCallsEvalutor, Instruction instruction, Call call) 
                    throws InstantiationException, IllegalAccessException, IllegalArgumentException, 
                    InvocationTargetException, NoSuchMethodException {    
    Class[] methodParameterTypes = new Class[]{instruction.getClass(), call.getClass()};
    Object instance = instructionCallsEvalutor.newInstance();
    Method method = instructionCallsEvalutor.getDeclaredMethod("evaluate", methodParameterTypes);
    return  method.invoke(instance, instruction, call).toString();
}

}

让我们一步一步地完成这一过程。所有电话都将来到

`public String evaluate(Instruction instruction, Call call)`

其中抽象类作为参数。进入后,必须调用类 InstructionCallsEvaluator 的相应 evaluate(...)方法。我在两种方法中分离它的原因是将try{} catch{}放在一个中,将实际方法放在另一个中(清洁度和可读性)。

您已经知道要调用其方法的类: InstructionCallsEvaluator ,但您不知道要调用哪种方法。那是

private String useEvaluator(Class<InstructionCallsEvaluator> instructionCallsEvalutor, Instruction instruction, Call call)

进来了。

此方法处理methodParameterTypes,创建InstructionCallsEvaluator类的实例,并确定应用于传递的参数的方法。一旦它获得了该方法,它就会调用它。让我们试试这个主要课程:

public class Main {

public static void main(String[] args) {
    ArrayList<Instruction> instructions = getInstructions();
    ArrayList<Call> calls = getCalls();
    InstructionHandler handler = new InstructionHandler();

    for(Instruction instrct : instructions)
        for(Call call : calls)
            System.out.println(handler.evaluate(instrct, call));
}

private static ArrayList<Instruction> getInstructions(){
    ArrayList<Instruction> instructions = new ArrayList<>();
    instructions.add(new Instruction1("Instruction 1"));
    instructions.add(new Instruction2("Instruction 2"));
    instructions.add(new Instruction3("Instruction 3"));
    return instructions;
}

private static ArrayList<Call> getCalls(){
    ArrayList<Call> calls = new ArrayList<>();
    calls.add(new Call1("Call 1"));
    calls.add(new Call2("Call 2"));
    calls.add(new Call3("Call 3"));
    return calls;
}

这是输出:

Instruction 1  Call 1
Instruction 1  Call 2
Instruction 1  Call 3
Instruction 2  Call 1
Instruction 2  Call 2
Instruction 2  Call 3
Instruction 3  Call 1
Instruction 3  Call 2
Instruction 3  Call 3

您可能想知道为什么我没有完全跳过整个 InstructionHandler 课程而去了:

InstructionCallsEvaluator评估者= new InstructionCallsEvaluator();    evaluator.evalute(....);

那是因为那时我必须用每个正确的Class和Instruction类手动调用evaluator.evalute(....);,这样我的main会看起来像这样:

   evaluator.evalute(new Instruction1(), new Call1());
   evaluator.evalute(new Instruction2(), new Call2());
   ...

而不是能够调用它传递所述类的上传。当然,这取决于你的目标是什么,你可能只想要这个,在这种情况下,你可以完全跳过 InstructionHandler 类。

如果你不做evaluator.evalute(new Instruction1(), new Call1());而只是用upcast调用它,那么你会得到一个编译时错误。您必须从

更改访问修饰符
    private String evaluate(Instruction instruction, Call call){
    return instruction.evaluate() + "  " + call.evaluate();
}

公开。但是它总会经历这种方法,你就失去了知道(指令,召唤)发生了什么组合的可能性。

再一次,这个答案假定了你的目标,因为我有点难以理解你想做什么。希望它对你或任何研究这个问题的人都有帮助。

答案 1 :(得分:0)

Command pattern可能对您有用。你需要编写更多的类,但它可能会产生更清晰的全面体系结构(避免代码中散布的条件)。