I have this abstract class where I have defined some methods that implement database actions (fetch rows, insert, delete, etc.)
Now I want to make method that will return some rows (i.e. the whole table) but instead of the domain classes I want it to return the corresponding model classes (which basically is the same as domain but without the relationship lists and some other stuff I don't need for the presentation layer).
The abstract class is
public abstract class DomainService<T extends Domain> {
protected abstract Logger getLogger();
protected final Validator validator;
protected DomainService() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
this.validator = factory.getValidator();
}
abstract public void insert(T object) throws ValidationException;
abstract public void delete(T object) throws EntityNotFoundException;
abstract public List<T> fetchAll();
}
and I want to add another method that will call fetchAll()
and then iterate each item and create the model equivalent and return that list.
public <K extends Model> List<K> fetchAllModels(Class<K> modelClass) {
List<T> domains = fetchAll();
List<K> models = new ArrayList<K>(domains.size());
for ( T domain : domains) {
K model = modelClass.newInstance();
models.add(model.fillIn(domain));
}
return models;
}
Disregarding that this is the code I though just now writing the question, is it acceptable to add a parameter for a generic that is not defined in the class. IMO a class can have methods returning other data types so it should not be a problem. In my case I pass the class so I can create an instance of the model and then use the domain to fill the members. I was of two opinions,
The one I wrote where I add a method to the model class to create it self from the domain object. I was thinking of a constructor that takes the domain object as an argument, but I think it's a bit of a hassle to call a constructor using generics (It would need reflection utilities at the very least) so I though of a method to fill the details after creating an instance using the default constructor. Also the model is on a higher layer and I think higher layers should use lower ones (Database->Domain classes->Access classes (DAO)->Service classes->Servlet classes----> JSP showing data)
I could add a method to the domain class that transforms the domain to its model and call that without having to pass the class of the model
public <K> List<K> fetchAllModels() {
List<T> domains = fetchAll();
List<K> models = new ArrayList<K>(domains.size());
for ( T domain : domains) {
models.add(domain.createModel());
}
return models;
}
but I feel that the domain class should be as clean a representation of the table in the database with the only methods having to do with the columns.
Would it better to add the parameter on the class. I am only going to use it for this method...
Any thoughts comments always welcome
is it acceptable to add a parameter for a generic that is not defined in the class
Absolutely. It's done all the time.
I prefer your first solution, passing the model to the method.
But, what you really want there is a function that creates K from T. In java8, this can be done very succinctly.
public <K extends Model> List<K> fetchAllModels(Function<T,K> func) {
...
K model = func.apply(domain);
and say you have a Model 'M' for domain 'D'
public M(D domain) // constructor
you can pass the constructor as func
(or at least it seems so)
service.fectchAllModels( M::new )
If you use Stream
, fetchAllModels()
becomes much simpler
abstract public Stream<T> fetchAll();
public <K extends Model> Stream<K> fetchAllModels(Function<T,K> func) {
return fetchAll().map(func)
}
And then, why do we even need this method? Just do
// fetch domains, convert each to M
Stream<M> models = service.fetchAll().map( M::new );
So we can remove fetchAllModels()
, and remove any dependencies on model from domain.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With