Type erasure is giving me nuts recently. I'm designing a class that performs symbolic differentiation on a math expression represented as a binary expression tree. The question is more on the design part than on the actual code part so I'm only giving out the method that looks awful to me.
public Node derive(final Node currentNode, Node parentNode) {
Node dxNode = null;
final Object cDataContext = currentNode.getData();
if (Number.class.isAssignableFrom(cDataContext.getClass()))
dxNode = new TreeNode<Double>(0.0);
else if (AddOperator.class.isAssignableFrom(cDataContext.getClass()))
dxNode = deriveAddContext((Node<AddOperator>) currentNode);
else if (MulOperator.class.isAssignableFrom(cDataContext.getClass()))
dxNode = deriveMulContext((Node<MulOperator>) currentNode);
else if (SineFunction.class.isAssignableFrom(cDataContext.getClass()))
dxNode = deriveSineContext((Node<SineFunction>) currentNode);
if (dxNode != null && parentNode != null)
dxNode.setParent(parentNode);
return dxNode;
}
I think it already speaks for itself. I'm having methods with different names which is fine. The awful part at least for me is this huge if statement that I truncated for simplicity. Is there a better way of doing this? I mean I would love to live with dynamic dispatch having the whole derive method consisting of a simple:
Node dxNode = deriveNode(currentNode);
dxNode.setParent(parentNode);
return dxNode;
I guess Java won't give me this luxury so perhaps there is some design pattern that I can utilize here? Just to give you a better understanding of the algorithm I'll show a sample method:
private Node<AddOperator> deriveAddContext(final Node<AddOperator> additionContext) {
// d/dx [f(x) + g(x)] = d/dx [f(x)] + d/dx [g(x)] => d/dx [f(x)] d/dx [g(x)] +
// ROOT: ADD
Node<AddOperator> dRoot = new TreeNode<AddOperator>(new AddOperator());
// ROOT.LEFT: d/dx [f(x)]
dRoot.setLeft(derive(additionContext.getLeft(), dRoot));
// ROOT.RIGHT: d/dx [g(x)]
dRoot.setRight(derive(additionContext.getRight(), dRoot));
// RET: d/dx
return dRoot;
}
So the whole algorithm is recursive on the expression traversing the original expr in an inorder fashion.
A Node has the following structure:
dataField: <DataType>
leftChild: Node
rightChild: Node
parent: Node