Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在保持干净的编程实践的同时设计通用操作类?_Java_Oop - Fatal编程技术网

Java 如何在保持干净的编程实践的同时设计通用操作类?

Java 如何在保持干净的编程实践的同时设计通用操作类?,java,oop,Java,Oop,手头的任务是创建我的Java web应用程序的一部分,它将允许我以合成的方式轻松地执行小块代码。手头的任务是允许用户以任何顺序编写“操作”。我正在努力解决的是将参数传递给我的操作 所有操作都从操作界面开始: public interface Action { void resolve(Context context); } 解决该操作后,将执行其代码。代码可以是任何东西:在Java中调用方法,执行一些Javascript 在这里,“背景”是我的问题。每个操作都在特定的上下文中执行。其思

手头的任务是创建我的Java web应用程序的一部分,它将允许我以合成的方式轻松地执行小块代码。手头的任务是允许用户以任何顺序编写“操作”。我正在努力解决的是将参数传递给我的操作

所有操作都从操作界面开始:

public interface Action {
    void resolve(Context context);
}
解决该操作后,将执行其代码。代码可以是任何东西:在Java中调用方法,执行一些Javascript

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

例如,让我们看看这个动作:

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中调用操作的简单包装器。参数可以是任何内容:数据库中具有特定ID的对象、用户配置的int/String/boolean值

操作中的代码可以如下所示:

public class DummyAction implements Action {
    @Override
    public void resolve(Context context) {
        User userExecutingTheAction = context.get("ExecutingUser");
        System.out.println("User " + userExecutingTheAction);
    }
}
因此,该操作可以检索运行时参数(例如:执行该操作的用户…)和静态参数(在加载该操作时创建,例如从配置文件),以及概念中的所有参数。此外,例如,用户可以在运行时注入的参数中指定对对象的引用

动作也可以嵌套/修饰,以实现完整的合成。例如:

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()));
了解所有这些,设计上下文类最干净的方法是什么?它应该被分成几个元素吗?困难在于在运行时向类注入所需的所有内容,并确保不与潜在的包装操作冲突

上下文基本实现负责:

  • 在运行时从数据库中检索任何对象
  • 向持有事务的动作开发人员提供静态参数,并提供
  • 它可以访问用户合并运行时和静态概念,以及 即使在包装器中(例如:如果父操作调用子操作 执行操作的用户必须仍然是已知的,以便上下文 合并静态子参数和运行时参数,例如 (用户)
我觉得它做得太多了。设计受到具有许多职责的概念类的影响,它们应该是分散的(现在应用程序的每个部分都与概念相关联)。但是怎么做呢? 以下是我试图在干净编码中实现的目标:

  • 操作界面应保持简单(最多1个方法,带有 合理的参数数量)
  • 不使用静态机制
  • 最大不变性
以真正面向对象的方式和面向方法,如何解决这个特定的设计问题

编辑:在接口的方法中删除了公共声明符

编辑2:我有很多有趣的解决方案,但对我来说更有意义的是,每个动作都用特定的上下文参数化。 我将事情重新组织如下:

  • Action接口仍然只有一个方法,用于使用上下文解析操作
  • 在创建操作(用户会话)时加载操作的静态参数(可能是DB或其他)
  • 上下文只是各种操作(事务…)和专门操作(如操作的用户)的外观

解释器GoF模式,解释器树可以以某种方式外部化。您可以装饰任何节点,并将很好地响应您的请求。 还有一件事:如果表示是外部的(即由用户创建),操作接口至少应该有两种方法:一种用于检查树的有效性(例如,需要两个子节点的节点必须正好有这些子节点),另一种用于执行树

编辑:这里有一个例子

import java.util.*;

//context can be further complicated if you want visibility blocks (i.e.you need a stack of contexts)

class Context {
private Map<String, Object> variables;

Context() {
    variables = new HashMap<java.lang.String, Object>();
}

//this is to retrieve stuff from your context (variables)
Object getVariable(String name) { return variables.get(name); }

// put here the shared structs
void setVariable(String name, Object value) { variables.put(name, value); }
}


interface Action<T> {

void verify(Context ctx);

void execute(Context ctx);

T getData();

void setData(T t);

List<Action<?>> getChildren();

void addChild(Action<?> action);
}

// we offer some default impl, but we keep it abstract
// note on design: you can split (derive) this in Terminal / Non-terminal nodes (careful at the above interface)
// however, it's not in my objective to follow the pattern to the letter, but to explain how the things can be done
// plus, I need to kepp this short, it's long enough
abstract class BaseAction<T> implements Action<T> {
private List<Action<?>> children;
private T data;

public BaseAction() {
    children = new LinkedList<Action<?>>();
}

@Override
public void verify(Context ctx) {
    for(Action<?> a : children) {
        a.verify(ctx);
    }
}

@Override
public void execute(Context ctx) {
    for(Action<?> a : children) {
        a.execute(ctx);
    }
}

@Override
public T getData() {
    return data;
}

@Override
public void setData(T t) {
    this.data = t;
}

@Override
public List<Action<?>> getChildren() {
    return Collections.unmodifiableList(children);
}

@Override
public void addChild(Action<?> action) {
    children.add(action);
}
}

class BlockAction<T> extends BaseAction<T> {} //needs further refinement, including push/pop contexts if necessary

//let's implement your Action Script, some stuff left out.
// we suppose that the action produces some string
// that's a final node
final class ActionScript extends BaseAction<String>{
private final String library;
private final String methodName;
private final String aParameter;

public ActionScript(final String library, final String methodName, final String aParameter) {
    this.library = library;
    this.methodName = methodName;
    this.aParameter = aParameter;
}

@Override
public void verify(Context ctx) {
    if(!getChildren().isEmpty()) {
        throw new RuntimeException("Terminal node with children ?!?");
    }
}

@Override
public void execute(Context ctx) {
    //do whatever here (your code)
    String paramValue = (String) ctx.getVariable(aParameter);
    setData(library + "." + methodName + "(" + paramValue + ")");
}
}

// this can be further complicated, i.e. to have 2 subnodes, but for simplicity:
final class AssignmentAction<T> extends BaseAction<T> {
private String variableName;

public AssignmentAction(String variableName) {
    this.variableName = variableName;
}

@Override
public void verify(Context ctx) {
    if(getChildren().size() != 1) {
        throw new RuntimeException(String.format("= node with %d children ?!?", getChildren().size()));
    }
    super.verify(ctx);
}

@Override
public void execute(Context ctx) {
    @SuppressWarnings("unchecked")
    Action<T> child = (Action<T>) getChildren().get(0);
    child.execute(ctx);
    ctx.setVariable(variableName, child.getData());
}
}

public class IP {
public static void main(String []args) {
    Context ctx = new Context();
    ctx.setVariable("inputVar", "Hello world!");
    Action<String> root = new BlockAction<String>();
    root.addChild(new AssignmentAction<String>("var"));
    root.getChildren().get(0).addChild(new ActionScript("myLib", "foo", "inputVar"));

    root.verify(ctx);

    root.execute(ctx);

    System.out.println(ctx.getVariable("var"));
}
}
import java.util.*;
//如果需要可见性块(即,需要一堆上下文),则上下文可能会更复杂
类上下文{
私有映射变量;
上下文(){
变量=新的HashMap();
}
//这是从上下文(变量)中检索内容
对象getVariable(字符串名称){返回变量。get(名称);}
//把共享结构放在这里
void setVariable(字符串名称,对象值){variables.put(名称,值);}
}
界面作用{
无效验证(上下文ctx);
无效执行(上下文ctx);
T getData();
无效设置数据(T);
列表>儿童;
私有T数据;
公共行动(){
children=newlinkedlist>getChildren(){
返回集合。不可修改列表(子项);
}
@凌驾
公共无效添加子对象(操作){
儿童.添加(行动);
}
}
类BlockAction扩展了BaseAction{}//需要进一步细化,必要时包括push/pop上下文
//让我们来实现你的动作脚本,一些遗漏的东西。
//我们假设这个动作会产生一些字符串
//这是最后一个节点
最后一个类ActionScript扩展了BaseAction{
私有最终字符串库;
私有最终字符串methodName;
专用最终字符串参数;
公共操作脚本(最终字符串库、最终字符串方法名、最终字符串参数){
this.library=图书馆;
this.methodName=methodName;
this.apaparameter=apaparameter;
}
@凌驾
公共无效验证(上下文ctx){
如果(!getChildren().isEmpty()){
抛出新的RuntimeException(“带子节点的终端节点?!?”);
}
}
@凌驾
公共无效执行(上下文ctx){
//在这里做任何事(你的代码)
字符串paramValue=(字符串)ctx.getVariable(APParameter);
setData(库+“+methodName+”(“+paramValue+”));
}
}
//这可能更加复杂,即有2个子节点,但为简单起见:
最后一个类AssignmentAction扩展了BaseAction{
私有字符串变量名;
公共赋值操作(字符串变量名){
public interface Action<C extends Context> {

    void resolve(C context); // no need to use 'public' modifier here
                             // interface methods are always public
}
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
    }
}
public class LogUserAction implements Action<LogUserContext> {

     @Override
     public void resolve(LogUserContext context) {
         User user = context.get("theUser");
         // log the user in
     }
}
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.
}
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");
    }
}