How to avoid duplicate code in the translation objects? - java

I have a super class named Transformer. and I have two derived classes that converts class A to B and vice versa. There is the classes :
class A{
private Long id;
private String name;
... getter and setters
}
class B{
private Long id;
private String name;
... getter and setters
}
interface Transformer<S,D> {
D convert(S source);
}
class ATransformer implements Transformer<A,B>{
public B convert(A a){
B b=new B();
b.setId(a.getId());
.....
return b;
}
}
class BTransformer implements Transformer<B,A>{
public A convert(B b){
A a=new A();
a.setId(b.getId());
.....
return a;
}
}
as you see, derived transformer classes have duplicate codes. Is there any design or solution to avoid this duplication without using reflection?

You can use a bean mapper framework like Dozer or orika to do the work for you (and spare you the duplicate code).
However as already mentioned in the other comments, it is (in most cases) preferable to avoid having classes that are nearly the same without them being in the same class hirarchy and therefore castable.

Related

How can I properly compose my POJOs using inheritance or another approaches?

I have two POJO-classes with almost same fields, e.g.:
public class B {
private String string1;
private String string2;
// getters, setters, equals, toString
}
public class C {
private String string1;
private String string2;
private String string3;
// getters, setters, equals, toString
}
I want to compose them using inheritance and one remark, that class C can not extend class B from a business point of view, therefore I made one abstract class that contains same fields and is inherited by these classes:
public abstract class A {
private String string1;
private String string2;
// getters, setters, equals, toString
}
public class B extends A {
}
public class C extends A {
private String string3;
// getter, setter, equals, toString
}
And question is it ok, that I have a class B and it's full the same with abstract class or there is better approach to implement that, maybe design pattern?
If you need any clarification please let me now, thank you in advance!
The way you did is totally fine, it just takes advantage of inheritance, and that's all you need for.
In addition, I would just change the access modifier of abstract class's field members from "private" to "protected", so that all concrete POJOs will have direct access to those fields, like this:
public abstract class A {
protected String string1;
protected String string2;
// getters, setters, equals, toString
}
public class B extends A {
}
public class C extends A {
private String string3;
// getter, setter, equals, toString
}

Best way to write pojos that can have common fields

I am currently making a service in which there are lots of public API's. And the response and request objects overlap a lot. So, I was thinking that is there a way by which we can generalise the pojo creation for the request/response objects.
Sometimes the response object is identical to the request object with one or two extra fields.
Let me give you an example.
#Data
public class Request {
private A objA;
private B objB;
}
#Data
public class Response {
private A objA;
private B objB;
private C objC;
}
#Data
public class A {
private D objD;
}
#Data
public class B {
private String sB;
private E obje;
}
#Data
public class C {
private String sC;
}
Similary, D and E are pojos as well. The thing is that there is a lot of similarity(overlapping fields) in request/response objects.
Your solution is probably inheritance: Create a parent abstract object type with the overlapping fields and have the request and response objects extend it and specify any extra (unique) fields they need.
Inheritence
public abstract class Common {
private String overlapfield1;
private String overlapfield2
}
public class Request extends Common {
private String requestField1;
private String requestField2;
}
public class Response extends Common {
private String responseField1;
private String responseField2;
}
You could also approach this using composition: Create an object type with the overlapping fields and include this object as a sub-object of the Request/Response types:
Composition
public class Common {
private String overlapfield1;
private String overlapfield2
}
public class Request {
private String requestField1;
private String requestField2;
private Common common;
}
public class Response {
private String responseField1;
private String responseField2;
private Common common;
}
There are pros and cons to each approach which are widely discussed on this and other boards. These however, are the two standard approaches to dealing with such a problem.
It really depends on what you are trying to achieve. I don't see it being a huge problem repeating the fields but you've given an abstract use case rather than a real world situation where I can understand what you're trying to achieve.
Perhaps you want to pass your #Data objects to the same services? In which case you might want to use interfaces because a class can implement multiple interfaces.
Eg
public interface AContiner {
A getA();
void setA(A a);
}
public interface BContiner {
B getB();
void setB(B b);
}
#Data
public class Bean1 implements AContainer {
private A a;
}
#Data
public class Bean2 implements AContainer, BContainer {
private A a;
private B b;
}
public class MyFantasticService {
public void doStuffWithA(AContainer data) {
System.out.println(data.getA());
}
public void doStuffWithB(BContainer data) {
System.out.println(data.getB());
}
}

Hibernate mapping of derived class with no fields

I've encountered situation when I need two classes have same fields, but a slightly different behavior, one of them possess some other field, but another doesn't, so the questions is : do i have to map derived class which have no special fields?
Here's example, let's say i have class A, and class B, both have to have some collection(e.g. of C objects) and some other field D, so i create abstract class AB, which is their parent, and while class B also have String field, class A - doesn't, and all of them are descendants of M, which have someMethod():
abstract class M {
//fields, getters, etc
public void someMethod(){
//doSmth
}
}
abstract class AB extends M {
private D d;
private Set<C> cs;
//getters, setters, etc
}
class A extends AB{
#Override
public void someMethod(){
for(C c : cs){
//doSmth
}
}
}
class B extends AB {
private String text;
//getters etc
}
So, I definitely should map M, AB and B, but should i map A?
P.S. we use xml mappings
If you want Hibernate to return you List<A> as a query result, yes you'd need to map A.

Inheritance for builders in lombok

I was trying to use lombok for my project.
I have a class A:
#Data
#Builder
public class A {
Integer a1;
}
and a class B:
#Data
public class B extends A {
Integer b1;
#Builder
public B(Integer b1, Integer a1) {
super(a1);
this.b1 = b1;
}
}
I am getting an error saying builder() in B cannot override builder() in A, as return type in BBuilder is not compatible with return type in ABuilder.
Is there some way to do this using lombok?
I do not want to write the complete builder for for B, unless I don't have any other option.
PS: I have given explicit constructor for class B due to Issue.
I tried searching, but I could not find a good solution for the same.
Here we just need to call super of the builder.
#Data
public class B extends A {
Integer b1;
#Builder
public B(Integer b1, Integer a1) {
super(a1);
this.b1 = b1;
}
public static class BBuilder extends ABuilder{
BBuilder() {
super();
}
}
}
If you are using Lombok 1.18.4 along with IntelliJ, following code shall work for you:
#Data
#Builder
class A {
Integer a1;
}
#Data
class B extends A {
Integer b1;
#Builder (builderMethodName = "BBuilder")
public B(Integer b1, Integer a1) {
super(a1);
this.b1 = b1;
}
}
public class Main {
public static void main(String[] args){
System.out.println(B.BBuilder().a1(1).b1(1).build());
}
}
One a side note, #SuperBuilder annotation didn't work in IntelliJ at time of writing this answer. If you have multiple level of inheritance, please avoid Lombok or it will make your Java models messy.
Lombok has introduced experimental features with version: 1.18.2 for inheritance issues faced with Builder annotation, and can be resolved with #SuperBuilder annotation
Please use lombok version: 1.18.2, #SuperBuilder annotations in child/parent class
Both child and parent should be marked with #SuperBuilder.
Having both parent and child as #Builder won't work.
Parent class A:
#Data
#SuperBuilder
public class A {
Integer a1;
}
Child class B:
#Data
#SuperBuilder
public class B extends A {
Integer b1;
}
After hours of hacking at this, I found a viable solution without using the #SuperBuilder. Consider an example -
public class A{
int x;
#Builder(toBuilder = true)
public A(int x){
this.x = x;
}
public static class ABuilder{
protected ABuilder(){} //Note this is important, otherwise BBuilder won't be able to access private no-args constructor of ABuilder
}
}
public class B extends A{
#Builder(builderMethodName="BBuilder", toBuilder=true)
public B(int x){
super(x);
}
public static class BBuilder extends ABuilder{
BBuilder(){
super();
}
}
}
public static void main(){
B obj = new B();
//we can use the existing obj as obj.toBuilder().x(5).build();
//this will return an object of B and not A
}
P.S : I am not sure if #SuperBuilder is an experimental feature still ; so didn't want to take chances

Mapping abstract class in dozer

I have the following class structure (it actually is a VO layer with Hibernate mappings):
public abstract class abstractClassVO {
private int id;
private String name;
}
public class concreteClassAVO extends abstractClassVO {
private String aAttribute;
}
public class concreteClassBVO extends abstractClassVO {
private Long bAttribute;
}
And the equivalent DTO objects:
public abstract class abstractClassDTO {
private int id;
private String name;
}
public class concreteClassADTO extends abstractClassDTO {
private String aAttribute;
}
public class concreteClassBDTO extends abstractClassDTO {
private Long bAttribute;
}
Then I have another object like this:
public class compositeObject {
private int anAttribute;
private abstractClassVO myInstance;
}
and its equivalent:
public class compositeObjectDTO{
private int anAttribute;
private abstractClassDTO myInstance;
}
How can I tell dozer to automatically map myInstance to the specific DTO that corresponds to the concrete class implementation in the VO layer?
Currently, out of the box, Dozer isn't even putting anything in the myInstance field of the compositeObjectDTO class. My guess is that it's due to the fact that abstractClassDTO it is an abstact class, and since it cannot determine the implementation, it does nothing. I am not getting any exceptions.
Dozer can't do it out of the box but you could write a helper that would determine destination class by source class. You can get this information from DozerBeanMapper.getMappingMetadata().getClassMappings* methods. These methods return list of ClassMappingMetadata that contains destination class. You just only need to chech whether destination class is inherited from abstractClassDTO. This check can be omitted if you only have one mapping for one VO.
For bi-directional mapping you should additionally check ClassMappingMetadata.MappingDirection field.

Categories