Reputation: 2152
I'm not looking for the best way to do this, but rather for any way to do what i need while adhering to the DRY principle.
Let's say I have a class Source
as follows. Source
contains thirty strings.
class Source {
private String sourceAttrOne = "sourceValueOne";
private String sourceAttrTwo = "sourceValueTwo";
...
private String sourceAttrThirty = "sourceValueThirty";
}
This information is to be used to create a new object of class Destination
. 25 of Destination
's attributes have a name in a similar format (but not the same name). 10 of these are Strings, while 5 are Dates, and 5 are Integers. The last 5 fields, however, are totally different.
class Destination {
private String AttrOne;
...
private Date AttrSixteen;
...
private Integer AttrTwentyOne;
...
// Last 5 fields are different
private SomeOtherClass someOtherName;
private TheBestClass imAlsoSpecial;
// Standard getters and setters
}
For the 25 "normal" attributes, I need to use a helper method to get from the source value to the result. The helper method used depends on the destination type:
destination.setAttrOne(getResultingString(source.getSourceAttrOne()));
destination.setAttrSixteen(getResultingDate(source.getSourceAttrSixteen());
destination.setAttrSeventeen(getResultingDate(source.getSourceAttrSeventeen()/*, custom Date format for AttrSeventeen */));
The remaining 5 attributes need custom (individual) logic.
Any pointers in the right direction would be much appreciated, I don't need a complete solution :)
Upvotes: 1
Views: 7650
Reputation: 161
So in your case you have several possibilities.
The easiest but maybe not the nicest solution (depending on your further process/requirements) is to have a constructer which has the need Object as parameter.
public class Source {
private String sourceAttrOne;
private String sourceAttrTwo;
// further class attributes....
// getters (& setters)
}
public class Destination {
private String attrOne;
private String attTwo;
public Destination(Source source) {
this.attrOne = source.getSourceAttrOne;
this.attrTwo = source.getSourceAttrTwo;
// etc..
}
}
The problem in the solution above is, that depending of which fields are required for creating the Destination.class
the constructer is going to have a lot of parameters. In addition, if you have to change your constructer in the future (e.g. additional required fields), you have to create a new constructer or change the already existing one (which implies you have to change all the current usages of that).
Therefore to hold the DRY, I would recommend the Builder Patter.
public class Destination {
private String attrOne;
private String attTwo;
private String attThree; // attribute which comes not from any other source class and is e.g. not a required field
private Destination() {
// should not be accessible
}
public static class Builder {
private String attrOne;
private String attTwo;
private String attThree;
private Builder() {
// do nothing
}
public static Builder create(Source source) {
Builder builder = new Builder();
builder.attrOne = source.getSourceAttrOne();
builder.attrTwo = source.getSourceAttrTwo();
return builder;
}
public Builder attThree(String attThree) {
this.attThree = attThree;
return this;
}
public Destination build() {
Destination destination = new Destination();
destination.attrOne = builder.attrOne;
destination.attrTwo = builder.attrTwo;
destination.attrThree = builder.attrThree;
//add several validations e.g. assert destination.attrOne != null
return destination;
}
}
}
To create a Destination.class
with Source.class
you can do following:
Destination.Builder.create(source).build();
For having different Types e.g. Source.sourceAttrOne
is a String and the in the Destination.attrOne
is a Date, you just have to adjust the Destination.class
.
public class Destination {
private LocalDate attrOne;
// ...
private Destination() {}
public static class Builder {
private String attrOne;
// ...
private Builder() {}
public static Builder create(Source source) {
Builder builder = new Builder();
builder.attrOne = LocalDate.parse(source.getSourceAttrOne());
// ...
return builder;
}
public Destination build() {
Destination destination = new Destination();
destination.attrOne = builder.attrOne;
// ...
return destination;
}
}
}
Upvotes: 1
Reputation: 114
You can try Reflection for similar targets.
Something like:
public void fillFieldsHelper(Object source) {
List<Field> sourceFields = source.getClass().getDeclaredFields();
or
Field valueOne = source.getClass().getDeclaredField("sourceAttrOne");
System.out.println(valueOne.getName());
System.out.println(valueOne.getType());
...
Object value = valueOne.get(source);
Field attrOne = this.getClass().getDeclaredField(valueOne.getName().replace("source",""));
switch (attrOne.getType().getName()) {
case "java.lang.Integer":
attrOne.set(this, Integer.valueOf(value));
break;
default:
attrOne.set(this, value);
}
...
etc.
I can't say that Reflection is elegant but it's useful in many cases.
Upvotes: 1
Reputation: 374
N.B.: I'm probably totally mistaken, so nevermind me if that's the case.
I also haven't unlocked comments yet, while it would be more likely the best; sorry for the inconvenience.
If the 1st to 15th attributes are String, then supposedly, you can simply affect them to the corresponding attributes, or clone them first, if you prefer.
For the 16th to 21th(?), which are dates, you might be able to use DateFormat's parse(String) method; although, I'm clueless on how to help the compiler to get the used format or if it can do it properly by itself.
For the 22th to 27th(?), the Integers, you should be able to use Integer's parse(String) method, or possibly through Double's and then convert back to an Integer or an int.
Upvotes: 1