Reputation: 6384
I am coding a Binary Tree as part of a learning exercise. I am trying to accommodate the two ways of deleting a node in a binary tree: DeleteByMerge and DeleteByCopy.
What is the best way to offer choice for a user to choose between these methods?
I am leaning towards a composition approach(like a strategy) :
public class BinaryTree{
BtreeDelete del;
public BinaryTree(){
this.del = new DeleteByCopy();
}
public BinaryTree(BtreeDelete del){
this.del = del;
}
public boolean delete(Node node){
// Common code
del.delete()
}
}
Classes DeleteByMerge and DeleteByCopy implement the interface BtreeDelete, so I could wire up during instantiation like this:
BinaryTree btree = new BinaryTree(new DeleteByMerge());
OR
BinaryTree btree = new BinaryTree(new DeleteByCopy());
.
Inheritance Based approach:
public class BinaryTree{
public BinaryTree(){
}
public boolean delete(Node node){
// Common code
deleteNode();
}
// An overriddable hook with a default implementation
protected boolean deleteNode(Node node){
//By default implementation for DeleteByCopy is provided
}
}
Different implementations for delete will demand a separate subclass(leading to a possible explosion of classes):
public class BtreeDelByMerge extends BinaryTree{
protected boolean deleteNode(Node node){
// Code for deleting a node by Merging
}
}
My qualm with the Inheritance approach is BtreeDelByMerge is not a type of Btree, it's behaviour does not change by much and having a to create a separate subclass for just one of its methods seems unnatural. Also it does not tend as well as the composition approach if I wanted a tree with a particular implementation of insert along with delete etc.
Are there any particular advantages going the inheritance way in this case? Also, is it a good idea to offer the choice at all? For ex: The Collections framework does not offer too many choices, so the implementations are well encapsulated and consistent but rigid.
Upvotes: 0
Views: 145
Reputation: 21760
I think that depends on the implementation of deleteNode
. If all it does is working on the node itself then your first approach (the strategy approach) looks clean and elegant.
If there's a case (or a case may exist in the future) in which the deletion could be made more efficient by using protected members/method of the class, I would choose the inheritance approach.
For example: let's say you have an inner hash map that helps you traverse through the tree faster then just be walking left/right/parent, and you make this map protected. Now, if you choose the strategy approach, you won't be able to use that map and you might need it for faster deletions. If you choose the inheritance approach and make the map protected you might be able to improve the naive algorithm for deletion.
Also, this is only an exercise, but if you were to face such dilemma in a "real-life" scenario you need to remember that deciding over an API is a commitment. You should take the in consideration the points I mentioned.
Upvotes: 1