3

我在 Java 中开发了一个 Web 服务,其中包含大约 30 个操作。除了 2-3 行之外,每个操作都有完全相同的代码。以下是代码草图

public Response operation1(String arg1, String arg2)
{
   initialze(); // this line will be different for each operation with different arguments
   authorizeSession();
   Response res;

   if (authorized)
   {
       try 
       {
          Object result = callMethod1(arg1, arg2); // This line is different for each operation
          res = Response.ok(result).build();
       }
       catch( MultipleExceptions ex)
       {
          res = handleExceptions();
       }
       finally
       {
           logInDatabase();
       }

   }
   return res;
}

我应该遵循什么方法,这样我就不必在每个操作中编写相同的代码?

  1. 我应该使用反射吗?
  2. 我听说过面向方面的编程……这里可以使用 AOP 吗?
  3. 我应该使用普通的旧 switch case 语句和一种方法来决定根据操作调用哪个方法吗?
4

3 回答 3

11

这看起来很适合模板方法模式。定义一个包含主要方法(final)的抽象类,它将特定部分委托给受保护的抽象方法。

在您的 Web 服务的每个方法中,实例化这个抽象类的一个子类,它只覆盖两个特定的抽象方法,并调用这个子类实例的主方法。

public abstract class Operation {
    public final Response answer(String arg1, String arg2) {
        authorizeSession();
        Response res;

        if (authorized) {
            try {
                Object result = executeSpecificPart(arg1, arg2);
                res = Response.ok(result).build();
            }
            catch (MultipleExceptions ex) {
                res = handleExceptions();
            }
            finally {
                logInDatabase();
            }
        }

        return res;
    }

    protected abstract Object executeSpecificPart(String arg1, String arg2);
}

...

    public Response operation1(final String arg1, final String arg2) {
        initialize();
        Operation op1 = new Operation() {
            protected Object executeSpecificPart(String arg1, String arg2) {
                ...
            }
        };
        return op1.answer();
    }
于 2012-09-29T14:55:52.757 回答
5

模板方法模式和/或策略模式

每个方法都创建自己的Callable实例并将其传递给一个通用run()方法:

public Response operation1(String arg1, String arg2)
{
    initialze(); // this line will be different for each operation with different arguments
    return run(new Callable<Object> {
        public Object call() {
            return callMethod1(arg1, arg2); // This line is different for each operation
        }
    });
}

private Response run(Callable<Object> method) {
   authorizeSession();
   if (authorized)
   {
       try 
       {
          Object result = method.call();
          return Response.ok(result).build();
       }
       catch( MultipleExceptions ex)
       {
          return handleExceptions();
       }
       finally
       {
           logInDatabase();
       }
   }
   return null;
}

如果 Java 有 lambda,它会更容易读写。

AOP

面向方面的编程在这里不是最好的工具,因为您必须在方法内部截取一些东西。AOP 最适合您需要在方法之前或之后调用某些东西(或当它抛出某些东西时)。这同样适用于反射。

但是 AOP 将显着改进错误处理。然后您的代码可以简化为:

public Response operation1(String arg1, String arg2)
    {
    initialze(); // this line will be different for each operation with different arguments
    authorizeSession();

    if (authorized)
    {
        Object result = callMethod1(arg1, arg2); // This line is different for each operation
        return Response.ok(result).build();
    }
    return null;
    }

和catch ofMultipleExceptionsfinallyblock 可以放在AfterThrowingandAfter通知中。

于 2012-09-29T14:56:23.140 回答
1

在这种情况下,您可以只提取私有方法中的代码。

1.) 反思会使理解和维护变得更慢、更难。

2.) 当一个关注点横切另一个关注点时,应该使用 AO 技术。实际情况并非如此,因为initializecallMethod1是整个算法的一部分,不能单独存在。

3.) switch 语句是正确的思维方式,但却是“多态恐惧症”的标志。

你可以这样做:

private static interface CallMethodHelper {
    Object callMethod() throws MultipleExceptions;
}

public Response operation1(final String arg1, final String arg2) {
    initialze(); 
    return operationHelper(new CallMethodHelper() {
        @Override
        public Object callMethod() throws MultipleExceptions {
            return callMethod1(arg1, arg2);
        }
    });
}

private Response operationHelper(CallMethodHelper helper) {
    authorizeSession();
    Response res = null;

    if (authorized) {
        try {
            Object result = helper.callMethod();
            res = Response.ok(result).build();
        } catch (MultipleExceptions ex) {
            res = handleExceptions();
        } finally {
            logInDatabase();
        }

    }
    return res;
}

请注意,您必须使参数成为最终的,并且您当然可以使用泛型来更改结果和异常类型。

于 2012-09-29T14:52:48.763 回答