Reputation: 3555
In our current codebase, we have the following data structure to communicate between our service layer and our frontend layer:
public class BaseView {
private Integer id;
// Other common attributes and getters/setters;
}
public class BarView extends BaseView {
private String name;
private String Description;
// Other attributes for this object and getters/setters;
}
We also have a number of double extended classes:
public class BazView extends BaseView {
// name, description, other common attributes;
}
public class BazTypeAView extends BazView {
// attributes specific to Type A Baz;
}
public class BazTypeBView extends BazView {
// attributes specific to Type B Baz;
}
In our service layer, we have service methods implementing a BaseService: a BarService
, a BazService
, a BazTypeAService
, a BazTypeBService
and other with a similar naming scheme which all have methods returning either the class they're responsible for, or a List<class they are responsible for>
. In turn, each of these methods calls a method from the BaseService with signature GetObjects(Class Viewtype, Class BeanType, Criteria)
, with the criteria based on parameters passed into the specific method method. the Beans are between the Service Layer and the Data Access Layer and not really relevant to this.
An example of the scheme:
public interface BaseService {
public BaseView getObject(Class viewType, class beanType, Integer id);
public List<BaseView> getObjects(Class viewType, class beanType, Criteria criteria);
}
public class BaseServiceImpl implements BaseService {
protected BaseView getObject(Class viewType, class beanType, Integer id){
BaseBean bean = (BaseBean) DataService.getobject (beanType, id);
BaseView view = ViewGenerator.createEmptyView(viewType);
copyAttributes(bean, view);
return view;
}
protected List<BaseView> getObject(Class viewType, class beanType, Criteria criteria){
List<BaseBean> bean = (List<BaseBean>) DataService.getobject (beanType, Criteria);
List<BaseView> view = new ArrayList<BaseView>();
copyAttributes(bean, view);
return view;
}
}
public interface BarService extends BaseService {
public BarView getBar(Integer id);
public List<BarView> getBars(BarView barView);
}
public class BarServiceImpl extends BaseServiceImpl implements BarService {
public BarView getBar(Integer id){
BarView bar = getObject(BarView.class, BarBean.class, id);
return bar;
}
public List<BarView> getBars(BarView barView){
Criteria criteria = getCriteriaFromBarView(barView);
List<BarView> bars = getObjects(BarView.class, BarBean.class, criteria);
return bars;
}
}
The above is roughly how the system works. The methods to create a Bar, get a list of Bars, update a Bar and delete a Bar all are effectively the same as the above.
the problem is that getObject returns a raw List, leading to a lot of Eclipse warnings and code that's harder to read. over 2/3 of our warnings for this project are about raw type usage, and though our code still compiles and works as it should, it's A LOT of messages.
If we didn't have the second kind of View which extends an extended class, we could just do List<? extends Fooview>
, but that gives errors for the classes extending BazView. Is there a way to make generic types work here when you have both ? extends Baseview and ? extends X extends Baseview?
Upvotes: 0
Views: 264
Reputation: 304
My answer does not differ much from the others, except that I guess your BaseService is an interface:
In our service layer, we have service methods implementing a BaseService
I would define the interface like that(EDIT):
public interface BaseService<T extends FooView>{
T getObject(Class<T> viewType, Class<? extends FooBean> beanType, Integer id);
List<T> getAllObjects(Class<T> viewType, Class<? extends FooBean> beanType);
}
(ADDED) Implementing that interface in an abstract class requires that all depending services use generics as well:
public abstract class BaseServiceImpl<T extends FooView> implements BaseService<T>{
@Override
public T getObject(Class<T> viewType, Class<? extends FooBean> beanType, Integer id){
FooBean bean = DataService.getObject(beanType, id);
T view = ViewGenerator.createEmptyView(viewType);
copyAttributes(bean, view);
return view;
}
@Override
public List<T> getAllObjects(Class<T> viewType, Class<? extends FooBean> beanType){
List<? extends FooBean> beanList = DataService.getAllObjects(beanType);
return beanList.stream().map(bean -> {
T view = ViewGenerator.createEmptyView(viewType);
copyAttributes(bean, view);
return view;
}).collect(Collectors.toList());
}
private void copyAttributes(FooBean bean, T view){}
}
(EDIT) The implementation of all your services should be straight forward then:
public class BarServiceImpl extends BaseServiceImpl<BarView> implements BarService{
@Override
public BarView getBar(Integer id){
return getObject(BarView.class, BarBean.class, id);
}
@Override
public List<BarView> getAllBars(){
return getAllObjects(BarView.class, BarBean.class);
}
}
public class BazServiceImpl extends BaseServiceImpl<BazView> implements BazService{
@Override
public BazView getBaz(Integer id){
return getObject(BazView.class, BazBean.class, id);
}
@Override
public List<BazView> getAllBazs(){
return getAllObjects(BazView.class, BazBean.class);
}
}
public class BazTypeAServiceImpl extends BaseServiceImpl<BazTypeAView> implements BazTypeAService{
@Override
public BazTypeAView getBazTypeA(Integer id){
return getObject(BazTypeAView.class, BazTypeABean.class, id);
}
@Override
public List<BazTypeAView> getAllBazTypeAs(){
return getAllObjects(BazTypeAView.class, BazTypeABean.class);
}
}
At least in my main method there are no warning because everything is cleanly typed:
public class Main{
public static void main(String[] args){
BarService barService = new BarServiceImpl();
BarView barView = barService.getBar(111);
List<BarView> barViewList = barService.getAllBars();
BazService bazService = new BazServiceImpl();
BazView bazView = bazService.getBaz(222);
List<BazView> bazViewList = bazService.getAllBazs();
BazTypeAService bazTypeAService = new BazTypeAServiceImpl();
BazTypeAView bazTypeAView = bazTypeAService.getBazTypeA(333);
List<BazTypeAView> bazTypeAViewList bazTypeAService.getAllBazTypeAs();
}
}
NOTE:
Replacing the interface with an abstract class(if possible) would reduce the code again because both of the sample methods could be implemented in the abstract class. The main method wouldn't need to change at all.
EDIT
You need to use generics also at that level where you create your objects. Otherwise you need a lot of casting and you get those ugly warnings with your lists. I updated my sample for you.
EDIT2
I already mentioned it before that your DataService and your ViewGenerator need to use generics too. This is how I would implement the method bodies:
public class DataService {
public static <T extends FooBean> T getObject(Class<T> beanType, Integer id){return null;}
public static <T extends FooBean> List<T> getAllObjects(Class<T> beanType){return null;}
}
public class ViewGenerator {
public static <T> T createEmptyView(Class<T> viewType){return null;}
}
Upvotes: 2
Reputation: 14572
A quick answer to use the code styling, I will delete it if I didn't not understand well.
Try to use a generic type like this :
public abstract class BarService<T extends FooView>{
List<T> list;
...
public List<T> getList(){ return list;}
}
public class BazService extends BarService<BazView>{
...
}
public class BazTypeBService extends BarService<BazTypeBView>{
...
}
...
Here,
BazService.getList() will return a List<BazView> instance
BazTypeBService .getList() will return a List<BazTypeBView> instance
Upvotes: 0
Reputation: 12953
if I understand you correctly, what you need is to make your BaseService
get a generic type that will be used in the getObject
method:
BaseService class:
public class <T extends FooView> BaseService<T> {
public T getObject() {
...
}
}
and in the specific services:
public class BazTypeAService extends BaseService<BazTypeAView> {
...
}
Upvotes: 0