gpt4 book ai didi

java - 如何在保持干净的编程实践的同时设计一个通用的 Action 类?

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:54:25 25 4
gpt4 key购买 nike

手头的任务是创建我的 Java Web 应用程序的一部分,这将使我能够以组合方式轻松执行小块代码。手头的任务是允许用户以任何顺序编写“ Action ”。我遇到的困难是将参数传递给我的操作。

一切都从 Action 界面开始:

public interface Action {
void resolve(Context context);
}

当 Action 被解析时,它的代码被执行。代码可以是任何东西:调用 Java 中的方法,执行一些 Javascript...

在这里,“上下文”对我来说是个问题。每个 Action 都在特定上下文中执行。这个想法是创建 Action 的用户可以指定从概念中检索哪个对象,例如正在解析当前 Action 的用户,或在 Action 的特定界面中指定的其他对象。

例如,让我们看一下这个 Action :

public final class ActionScript implements Action {
private final Parameters parameters;
private final String methodName;
private final ScriptsLibrary library;

public ActionScript(ScriptsLibrary library, String methodName, Parameters parameters) {
this.parameters = parameters;
this.library = library;
this.methodName = methodName;
}

@Override
public void resolve(Context context) {
try {
((Invocable) library.getEngine()).invokeFunction(methodName, context);
} catch (ScriptException | NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
}

这是一个使用 Nashorn 在 Javascript 中调用 Action 的简单包装器。参数可以是任何东西:数据库中具有特定 id 的对象,用户配置的 int/String/boolean 值...

一个 Action 的代码可以是这样的:

public class DummyAction implements Action {
@Override
public void resolve(Context context) {
User userExecutingTheAction = context.get("ExecutingUser");
System.out.println("User " + userExecutingTheAction);
}
}

所以 Action 可以检索运行时参数(例如:执行 Action 的用户...)和静态参数(在加载 Action 时创建 - 例如从配置文件),以及所有来自概念的参数。此外,用户可以在运行时注入(inject)的参数中指定对对象的引用。

Action 也可以嵌套/修饰以实现完整的组合性。例如:

public class DummyWrapperAction implements Action {
private final Action wrappedAction;
public DummyWrapperAction(Action wrappedAction) {
this.wrappedAction = wrappedAction;
}

@Override
public void resolve(Context context) {
System.out.println("Before");
wrappedAction.resolve(context);
System.out.println("After");
}
}

这样可以轻松创建操作:

// executes specific action 1 or specific action 2 based on a condition
Action myAction = new LoggingAction(new ConditionalAction(new Condition(3), new SpecificAction1(), new SpecificAction2()));

知道了这一切,设计上下文类最干净的是什么?它应该分成几个元素吗?难点在于在运行时将所有需要的东西注入(inject)到类中,并确保不与潜在的包装操作发生冲突。

Context 基本实现负责:

  • 在运行时从数据库中检索任何对象
  • 为持有交易的 Action 开发者提供静态参数并给予
  • 它访问用户合并运行时和静态概念,而这即使在包装器中(例如:如果父操作调用子操作,执行操作的用户必须仍然是已知的,因此上下文应该合并静态子参数和运行时参数,例如用户)

我觉得它做得太多了。设计受到拥有很多责任的概念类的影响,它们应该是碎片化的(现在应用程序的每个部分都与概念相关联)。但是怎么办?这是我在干净编码中尝试实现的目标:

  • 操作界面应该保持简单(最多 1 个方法,合理数量的参数)
  • 不使用静态机制
  • 最大不变性

以真正的面向对象和面向方法的方式,如何解决这个特定的设计问题?

编辑:删除接口(interface)方法中的公共(public)声明符

编辑 2:我有很多有趣的解决方案,但对我来说更有意义的是每个 Action 都使用特定类型的上下文进行参数化的解决方案。我这样重组了事情:

  • Action 接口(interface)仍然只有一个方法,用上下文解析 Action
  • Action 的静态参数,可能是数据库或其他,在创建 Action 时加载(用户 session )
  • 上下文只是各种操作(事务...)+专门操作的外观,例如操作的用户

最佳答案

我已将 Action 接口(interface)的 Context 设为通用:

public interface Action<C extends Context> {

void resolve(C context); // no need to use 'public' modifier here
// interface methods are always public
}

然后,Context 可以是标记接口(interface)、强制执行契约的接口(interface)或具有默认方法实现的抽象类:

public interface Context {

User get(String user);

// other default methods here
}

然后,你可以这样做:

public class LogUserContext implements Context { 

@Override
public User get(String user) {
// materialize user here
}
}

让用户登录的Action可以是:

public class LogUserAction implements Action<LogUserContext> {

@Override
public void resolve(LogUserContext context) {
User user = context.get("theUser");
// log the user in
}
}

对于包装的Action,我会使用WrapperContext 上下文:

public interface WrapperContext extends Context {

Context getWrappedContext();
}

实现:

public class DummyWrappedContext implements WrapperContext {

private final Context wrappedContext;

public DummyWrapperContext(Context wrappedContext) {
this.wrappedContext = wrappedContext;
}

@Override
public Context getWrappedContext() {
return this.wrappedContext;
}

// TODO other methods from Context, etc.
}

现在你的 DummyWrapperAction 可以如下所示:

public class DummyWrapperAction implements Action<WrapperContext> {

private final Action wrappedAction;

public DummyWrapperAction(Action wrappedAction) {
this.wrappedAction = wrappedAction;
}

@Override
public void resolve(WrapperContext context) {
System.out.println("Before");
Context wrappedContext = context.getWrappedContext();
wrappedAction.resolve(wrappedContext);
System.out.println("After");
}
}

这个想法也是包装上下文。您可以通过允许链接或修饰上下文以及使用负责所有上下文通用任务的抽象类来增强此设计。

关于java - 如何在保持干净的编程实践的同时设计一个通用的 Action 类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32948198/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com