Reputation: 11
I'm not sure if there's a pattern that covers this question, but here goes. In writing a method to call an analytical algorithm (such as a parser or calculation implementation), there is quite a large amount of code to read a source, apply the algorithm, then convert the results to something useful. Basically theres 20-30 lines of code and one algorithm/parser/tokenizer that changes quite often.
So.... my options that I see so far are:
Is there a neat trick or general method construction programming pattern to solve this?
Thanks in advance.
--Edit--
To remove some of the abstraction of this question, here is a prototype of what I am talking about. So really only the implementation of the tokenizer changes.
pubic void tokenizeData(Filename datablob){
// convert filename
// open file
// handle file errors
// other code
// assign desired tokenizer
tokenizer = new TokenizerImplementation (or some other variation);
tokenizedData = tokenizer( cleansedData );
// parsing and collection code
// more error processing code
// cleanup code
}
Upvotes: 0
Views: 300
Reputation: 1285
I'd also go personnally for some combination of Interface
and Abstract
+ Template Method
as suggested by @Lucas Oliveira but for your very problem of selecting the appropriate Tokenizer implementation you may also need a Factory
(pattern) to dynamically load another Tokenizer impl. based on the factory context or parameters without changing the content of your template method tokenizeData()
.
Example.1 a classic parametered Factory:
public class TokenizerFactory
{
private static final Logger logger = LoggerFactory.getLogger(TokenizerFactory.class);
private final int version;
public TokenizerFactory(int version) {
this.version = version;
}
public ITokenizer getInstance() {
switch(version) {
case 1: return new TokenizerV1();
case 2: return new TokenizerV2();
default: return new DefaultTokenizer();
}
}
}
Example.2 a dynamic class-loading static Factory (forgive me for the name):
public class TokenizerFactory
{
private static final Logger logger = LoggerFactory.getLogger(TokenizerFactory.class);
private TokenizerFactory() {}
// Here eg. ETokenizerType is a enum storing class associated to the type.
public static ITokenizer getInstance(ETokenizerType dtype) {
try {
return (ITokenizer)dtype.getClassVar().newInstance();
}
catch(Throwable ex) {
logger.error("Factory could not create an adequate instance of Tokenizer for dtype:{} !",dtype.name());
}
return new DefaultTokenizer();
}
}
You can define an interface for your Tokenizer(s)
as:
public interface ITokenizer {
public void tokenizeData(Filename datablob);
}
... to be implemented by your abstract class AbstractTokenizer
for which all subclasses (such as TokenizerV1
and TokenizerV2
) will redefine only customized abstract method(s). Just like in the following example (based on @Lucas Oliveira proposal):
public abstract class AbstractTokenizer implements ITokenizer {
@Override
public void tokenizeData(Filename datablob) {
// convert filename
// open file
// handle file errors
// other code
tokenizedData = tokenizer( data );
// parsing and collection code
// more error processing code
// cleanup code
}
abstract TokenizedData tokenizer( Data cleansedData ); // << redef. by subclasses.
}
But it will transparent for you to use.
You can finally make use of your TokenizerFactory simply by providing a pre-configured one as argument to your main business methods or use them on-the-fly provided you own the parameter needed to parameterize it. So that the getInstance()
call will return the exact Tokenizer
you need able to ´tokenizeData()´.
Note: for highly parameterized factories, combining them with a Builder (pattern) is generally a life-saver.
Upvotes: 1
Reputation: 3477
Template Method seems what you are looking for.
It alows you to:
Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
Base class declares algorithm 'placeholders', and derived classes implement the placeholders.
This is done with an Abstract class. You should decide which steps of the algorithm are invariant (or standard), and which are variant (or customizable). The invariant steps are implemented in the abstract base class, while the variant steps can be supplied by oncrete derived classes.
In your case things will go like that:
abstract class AbstractTokenizer {
public void tokenizeData(final Object datablob) {
// convert filename
// open file
// handle file errors
// other code
tokenizedData = tokenizer( cleansedData );
// parsing and collection code
// more error processing code
// cleanup code
}
abstract TokenizedData tokenizer( Data cleansedData );
}
Upvotes: 0