The challenge is to create part of my Java web application that will allow me to easily execute small pieces of code in composite form. The challenge is to allow the user to compose the "actions" in any order. I am trying to pass parameters to my actions.
It all starts with the action interface:
public interface Action { void resolve(Context context); }
When an action is allowed, its code is executed. The code can be anything: calling a method in Java, executing some Javascript ...
Here, "context" is a problem for me. Each action is performed in a specific context. The idea is that the user creating the action can specify which object to get from the concept, for example, the user who permits the current action, or other objects specified in the specific interface of the action.
Example, look at this 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); } } }
This is a simple wrapper that triggers an action in Javascript using Nashorn. Parameters can be any: objects with specific identifiers in the database, int / String / boolean values configured by the user ...
The code from the action may look like this:
public class DummyAction implements Action { @Override public void resolve(Context context) { User userExecutingTheAction = context.get("ExecutingUser"); System.out.println("User " + userExecutingTheAction); } }
Thus, an action can extract runtime parameters (for example: the user performing the action ...) and static parameters (created when loading the action - for example, from a configuration file) and all this from the concept. In addition, the user can, for example, specify references to the object in the parameters that will be entered at run time.
Actions can be nested / decorated to achieve a complete composition. For example:
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"); } }
This makes it easy to create actions:
// 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()));
Knowing all this, what is the cleanest well for developing a context class? Should it be divided into several elements? The struggle is to introduce everything that is necessary for the class at runtime, and be sure not to conflict with potential wrapped up actions.
The main implementation of the context is responsible for:
- getting any object from the database at runtime
- providing static parameters to the developers of the actions that perform the transaction, and providing
- this is access to the merge of the user and the static concept of the user, and even in the wrapper (for example: if the parent action calls the child action, the user performing the action must still be known, so the context must merge the static child parameters and the run-time parameters, such as user)
I feel that is too much. Design is influenced by Concept classes, which have great responsibility, and should be fragmented (right now, every part of the application is associated with a concept). But how? Here is what I'm trying to achieve in pure coding:
- the action interface should be simple (1 max method, with a reasonable amount of parameters)
- lack of use of static mechanisms
- maximum immutability
In a true object-oriented and method-oriented, how to solve this specific design problem?
edit: remote public declarator in a method in an interface
edit 2: I had many interesting solutions, but the one that gave me more meaning was one in which each action is parameterized by a specific Context. I reorganized things as such:
- The action interface has only one method to allow an action with context.
- Static parameters of actions, whether they can be DB or otherwise, are loaded when the action is created (user session)
- A context is simply a facade for various operations (transaction ...) + specialized operations such as user actions