I'm creating a toolset in Java consisting of many Natural Language Processing (NLP) tools, such as Tokenizer, POS tagger, Lemmatizer, Sentence parsing, etc.
I want to put all good and publicly available tools into my toolset, all sharing the same interface, so that I can change library easily without change of API.
The project structure is something like this:
tokenizer/ Tokenizer.java (an interface) LibraryFooTokenizer.java (from first library) LibraryBarTokenizer.java (from second library) DefaultTokenizer.java (a default tokenizer) lemmatizer/ Lemmatizer.java LibraryFooLemmatizer.java LibraryBarLemmatizer.java DefaultLemmatizer.java
The Library*Tokenizer.java
are implementing Tokenizer.java
, so I can directly use Tokenizer
in my applications without needing to know the implementation details. So far so good.
My question is regarding the implementation of DefaultTokenizer.java
. It's supposed to be for someone who just want to use a Tokenizer, who perhaps doesn't really know from the list of available Tokenizer which one is the best.
Should it be a subclass of the currently best library, or should it implement the interface and has the best library as a member, or else?
Currently I use the second approach as follows:
package tokenizer;
public class DefaultTokenizer implements Tokenizer{
public Tokenizer tokenizer;
public DefaultTokenizer(){
tokenizer = new LibraryFooTokenizer();
}
@Override
public String[] tokenizer(String text){
return tokenizer.tokenize(text);
}
}
Is it better than this one?
package tokenizer;
public class DefaultTokenizer extends LibraryFooTokenizer{}
The points that I'm considering are the ease of modification, in case later LibraryBarTokenizer
is updated and becomes better, I want to update to that one.
You can also review this in term of ease of debugging, is it nice with common IDE (I'm currently using Eclipse)?
I'm currently using this as internal research toolset, to compare various implementations. There are some use cases why DefaultTokenizer might be required:
Sometimes we might decide that we want to change the library for Tokenizer (an update makes it better, change of license makes it (in)appropriate). Now, on codes that doesn't care about which Tokenizer is used, but only requiring the best one, using Default makes change easier, since we just need to change which library is the default.
It might be the case that there will be someone trying to use a Tokenizer component while developing new component. While it's true that in research one is supposed to know the details of each component, sometimes we want to test something for which the Tokenizer is not that crucial to the result, but is required. In that case, taking the default might be good (and if the result changes if the Tokenizer is changed, then there is something wrong with how the Tokenizer is used).
Those reasons might not be too compelling, but we decided to use such default implementation, and currently I'm looking for ways to improve how we implement the default, hence this question.
It might be the case that using default is that bad for reasons I haven't considered. In that case, that also qualifies as an answer.
But I will be glad if someone can offer more insights on how this can be improved, or whether it doesn't matter much which way I choose.
LibraryFooTokenizer
is my own wrapper of the original library, so I have full control over it. I thought it's clear from the package structure, since you can't include other library's code inside your package just like that.