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:
- 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. - 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?