Reputation: 21
Basically I need to have a flexible design to process. Im thinking of something like this:
DataProcessor
and then have concrete classes of different types like CSVDataProcessor
,FTPDataProcessor
,CloudDataProcessor
.process()
defined on DataProcessor
InterfaceMy question is:
What should be the best design to do it, if each concrete class needs different parameters to execute the process()
method. For example, FTPDataProcessor
will need parameters like the file, the host, the port - CSVDataProcessor
will need the file location, and schema - CloudDataProcessor
will need the endpoint service, user, password??
My first thoughts are, to use the constructor to pass the specific requirements to each concrete class, but what if there are a lot of parameters that are needed?
In the future, more DataProcessor
concrete implementations will be added, and that is why I would like to have a very flexible design to implement it since the beginning.
Upvotes: 2
Views: 119
Reputation: 21
First, I would like to thank all the people who has taken time to answer me so far. I came up with a possible option that I would like to share and ask for comments to see if can be a good option or not.
What if I have the main interface like this:
public interface DataProcessor {
public void process();
}
Then what if I have another interface for the specific types of DataProcessors, like this:
public interface FTPDataProcessor extends DataProcessor {
//add all parameters getter and setters but also 'with' methods
public String getHost();
public void setHost(String host);
public FTPDataProcessor withHost(String host);
public String getUsername();
public void setUsername(String username);
public FTPDataProcessor withUsername(String username);
}
Then I can initialize all get'ters, set'ters and with'ters methods in an Abstract class, like this:
public abstract class AbstractFTPDataProcessor implements FTPDataProcessor {
private String host;
private String username;
public String getHost() {
return this.host;
}
public void setHost(String host) {
this.host = host;
}
public FTPDataProcessor withHost(String host) {
setHost(host);
return this;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public FTPDataProcessor withUsername(String username) {
setUsername(username);
return this;
}
}
Then my concrete class can implement just the method process()
, like this:
public class SimpleFTPDataProcessor extends AbstractFTPDataProcessor {
@Override
public void process() {
//do some stuff
}
}
Finally I can use it in my app, like this:
public class Application {
public static void main(String[] args) {
DataProcessor processor = new SimpleFTPDataProcessor()
.withHost('host')
.withUsername('username');
processor.process();
}
}
What others think about this possible option? Is this a good practice?, what can be the prons and/or cons?
Thanks all for your feedback.
Upvotes: -1
Reputation: 49714
One possible solution is:
interface DataProcessorParameters {}
class FtpParams implements DataProcessorParameters {/* connection params come here*/}
interface DataProcessor<T extends DataProcessorParameters> {
void process(T params);
}
class FtpProcessor implements DataProcessor<FtpParams> {
void process(FtpParams params) {
//...do stuff
}
}
While this works, you've got to ask why you wanted an interface in the first place, as the caller must know the actual type to pass the correct parameters.
FtpProcessor proc = new FtpProcessor();
proc.process(new FtpParams()); // this works
DataProcessor proc = ... //let's say this came from somewhere else, and all we know about it is that it's a data processor
proc.process(???); //this doesn't
You're also mentioning passing the parameters in your constructor, which you can do, but that is semantically different: in that case a processor instance will have to use the same parameters for its whole lifecycle, essentially they'll be "one shot". If you pass the parameters in the process()
method, your processors will be "reusable".
One isn't necessarily better than the other, so make your choice depending on what fits your design more, just be aware of the differences.
Upvotes: 1
Reputation: 1321
is the answer of your question.
DataProcessor p = ProcessorsFactory.getProcessor(yourParametersHere);
You should overload your factory getProcessor() with the heavy work. In the end your object will know only about the DataProcessor Interface and the Factory, not about the implementations. That is how one of the leader technologies work:
Spring Bean Factory Example
http://javajadhav.blogspot.bg/2013/07/03-understanding-spring-bean-factory.html
So you can create something like this. In the ProcessorsFactory
you can load some configuration file from your app and depending on the properties writen to instantiate the appropriate processor implementation. At the end you will be workin only with your interface.process + the configuration file.
Upvotes: 2