0

I have a document object and a list of operations that can be applied to that document in sequence. The types of operations are known in advance, but not their parameters, the order of operations or their number. For instance there may be a DB from which we pull a specific list of operations and corresponding parameters for a given document.

The solution I've written in Java looks something like the following:

public interface Operation {

}

public class OperationA implements Operation {
    private String paramA;
    private String paramB;
}


public class OperationB implements Operation {
    private String paramA;
    private String paramB;
    private String paramC;
}

public OperationApplier {
    public Document apply(List<Operation> operations, Document inputDocument) {
        Document transformedDocument = inputDocument;
        for (Operation operation : operations) {
            if (operation instanceof OperationA) {
                OperationA operationA = (OperationA) operation;
                transformedDocument = apply(operationA, document);
            } else if (operation instanceof OperationB) {
                OperationB operationB = (OperationB) operation;
                transformedDocument = apply(operationB, document);
            }
        }
        return transformedDocument;
    }
}

private Document apply(OperationA operation, Document document) {
    ...
}

private Document apply(OperationB operation, Document document) {
    ...
}

Now to apply a list of operations on a document we can do:

OperationApplier operationApplier = new OperationApplier();
List<Operation> operations = Arrays.asList(
    new OperationA("foo", "bar"),
    new OperationB("foo", "bar", "baz"),
    new OperationA("hoge", "piyo")
);
Document inputDocument = ...;
Document outputDocument = operationApplier(operations, inputDocument);

Basically I see two smells in the solution I implemented above:

  1. The empty interface Operation is just there to allow the construction of an operation list, but it otherwise contains no shared contract between operation implementations.
  2. The use of instanceof and downcasting, which are also considered bad practices in Java.

Are there any design patterns I should consider to improve upon the implementation above? Besides the two smells I mentioned, do you see any substantial issues with the solution I've adopted?

4

You could eliminate both the placeholder interface and the downcasting by adding a single apply method to the interface, and requiring each class to implement it.

public interface Operation {
    public Document apply(Document input);
}

That makes your loop a lot simpler too:

public OperationApplier {
    public Document apply(List<Operation> operations, Document inputDocument) {
        Document doc = inputDocument;
        for (Operation operation : operations) 
            doc = operation.apply(doc);
        }
        return doc;
    }
}

The above requires you to move the apply logic out of the OperationApplying and into the OperationX class. If you don't think that is a good idea, you can make apply a simple passthrough.

public class OperationA implements Operation {
    private String paramA;
    private String paramB;

    public Document apply(Document input, OperationApplier applier) {
        return applier.apply(input);

}

And call with this:

for (Operation operation : operations) 
    doc = operation.apply(doc, this);
}
  • I would add that the interface should/could also extend Function<Document,Document> or UnaryOperator<Document> and have a functional interface annotation. – Hangman4358 Sep 6 '18 at 13:31
0

You are describing a technique known as 'orchestration' which is really an Enterprise Design Pattern.

John Wu's approach is sound, the operation is the verb, and logically belongs in an interface. John's example is the classic 'builder' pattern where the Document is passed around.

If you want to take a different approach -- you could follow the approach used by a rules-processing engine. Rules-based processing is good for workflow's, which is what it sounds like you are hoping to accomplish.

https://kissflow.com/workflow/workflow-engine-business-rule-engine-difference/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.