I am connecting with many social networks for login in my application.
I have one DTO for each social network response.
public class GoogleUserInfo {
private String id;
private String firstName;
private String lastName;
private String email;
private AgeRange ageRange;
// more specific fields
}
public class FacebookUserInfo {
private String id;
private String firstName;
private String lastName;
private String email;
private String picture;
// more specific fields
}
public class AppleUserInfo {
private String id;
private String firstName;
private String lastName;
private String email;
private Boolean emailVerified;
// more specific fields
}
In each social network connector, I make similar steps to fetch the information, so I thought I could go with some DTO as follows:
public class SocialNetworkInfo {
protected String id;
protected String firstName;
protected String lastName;
protected String email;
}
Social networks DTOs could extend this to obtain the common fields. Then I could use this generic DTO to implement an abstract connector that deals with all the duplicate logic between connectors (make request, parse response, etc...):
abstract class AbstractConnector {
abstract SocialNetworkInfo fetchUserInfo(String networkId);
...
}
But I realized that above, in my service layer, I would need those specific fields to make some changes and operations.
SocialNetworkInfo networkUserInfo = facebookConnector.fetchUserInfo(facebookId);
facebookService.updatePicture(networkUserInfo.getPicture()); // can't access this specific field
What do you think that's the best way to go through this situation without casting and avoiding logic or DTO duplication?
Would love to hear your thoughts.
Thanks!
According to your situation, all social network models have the same nature, so it's ok if you move common attributes to shared class like CommonSocialInfo. Then I would recommend to provide interface for the connectors like:
interface SocialNetworkConnector<T extends SocialNetworkInfo> {
T fetchUserInfo(String userId);
}
Of course for common functionality(for connectors) is great idea to define common abstract class that implements interface above (implement Template pattern). I see that you are using FacebookService and related connector separately. I think that good idea to use composition in this case and make SocialNetworkService dependent on it connector. In short, FacebookService depends on FacebookConnecter and so on. Just a quick example:
public class FacebookService implements SocialNetworkService {
private final SocialNetworkConnector<FacebookSocialInfo> connector;
...
}
And if you need to implement multiple social service, you can use Factory pattern to produce required service, quick example:
interface SocialNetworkServiceFactory {
SocialNetworkService getFacebookService();
...
}
If you need more detailed help or you have troubles with understanding of the idea - feel free to ask!
If you don't want to use inheritance, I'd suggest to consider composition. The code can look as follows:
public class SocialNetworkInfo {
private String id;
private String firstName;
private String lastName;
private String email;
}
public class GoogleUserInfo {
private SocialNetworkInfo socialNetworkInfo;
private AgeRange ageRange;
// more specific fields
}
public class FacebookUserInfo {
private SocialNetworkInfo socialNetworkInfo;
private String picture;
// more specific fields
}
public class AppleUserInfo {
private SocialNetworkInfo socialNetworkInfo;
private Boolean emailVerified;
// more specific fields
}
Related
I'm using the DTO pattern for managing HTTP bodies in a Spring Boot REST application. I have seperate DTOs for requests and responses as the response contains additional data. I'm using inheritance for the response DTO as the additional data included is the same for all response objects. The structure is as following (annotations and methods ommited for clearance):
public abstract class BaseResponseDTO {
protected UUID id;
protected Integer version;
protected Date created;
protected Date modified;
}
public class RequestUserDTO {
private String firstName;
private String lastName;
}
public class ResponseUserDTO extends BaseResponseDTO {
private String firstName;
private String lastName;
}
There is obvious code duplication here. It would be ideal for the ResponseUserDTO to extend both BaseResponseDTO and RequestUserDTO which is not allowed.
Other option would be to use composition and have something as follows:
public abstract class BaseResponseDTO {
protected UUID id;
protected Integer version;
protected Date created;
protected Date modified;
}
public class UserDTO {
private String firstName;
private String lastName;
}
public class RequestUserDTO {
private UserDTO payload;
}
public class ResponseUserDTO extends BaseResponseDTO {
private UserDTO payload;
}
The problems I have with this approach are:
It still does not prevent code duplication
It forces client to wrap the body in payload
What can be done about that?
I think you could replace both your ResponseUserDTO and RequestUserDTO with UserDTO and extend BaseResponseDTO on it, when you send a request body using UserDTO the code won't care if you send it along with the BaseResponseDTO or not (since I didn't see any validation here), so if you not gonna use those in your request function it will be just fine
public abstract class BaseResponseDTO {
protected UUID id;
protected Integer version;
protected Date created;
protected Date modified;
}
public class UserDTO extends BaseResponseDTO {
private String firstName;
private String lastName;
}
I have a model like this:
public class Employee {
#JsonProperty("emplyee_id")
private Integer id;
#JsonProperty("emplyee_first_name")
private String firstName;
#JsonProperty("emplyee_last_name")
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
private Byte level;
//getters and setters
}
now I need to create two JSONs using this (only) model.
the first one must like this for example:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
and the second one must like this for example:
{
"employee_id":101,
"employee_level":5
}
by the way, I already tested #JsonIgnore and #JsonInclude(JsonInclude.Include.NON_NULL).
the problem of the first one (as much as I know) is, those fields can't be included in other JSONs (for example if level get this annotation, it won't be included in the second JSON)
and the problem of the second one is, null values can't be included in JSON.
so can I keep null values and prevent some other property to be included in JSON without creating extra models? if the answer is yes, so how can I do it? if it's not I really appreciate if anyone gives me the best solution for this state.
thanks very much.
it could be useful for you using #JsonView annotation
public class Views {
public static class Public {
}
public static class Base {
}
}
public class Employee {
#JsonProperty("emplyee_id")
#JsonView({View.Public.class,View.Base.class})
private Integer id;
#JsonProperty("emplyee_first_name")
#JsonView(View.Public.class)
private String firstName;
#JsonProperty("emplyee_last_name")
#JsonView(View.Public.class)
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
#JsonView(View.Base.class)
private Byte level;
//getters and setters
}
in your json response add #JsonView(Public/Base.class) it will return based on jsonview annotations
//requestmapping
#JsonView(View.Public.class)
public ResponseEntity<Employee> getEmployeeWithPublicView(){
//do something
}
response:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
for the second one
//requestmapping
#JsonView(View.Base.class)
public ResponseEntity<Employee> getEmployeeWithBaseView(){
//do something
}
response
{
"employee_id":101,
"employee_level":5
}
During my initial approach of a Java application development with MongoDB, I find that my code doesn't look quite right with a flat Java class design and using inner classes seems to be a better approach. I am wondering how others design their Java classes in the regard and potential issues with inner classes for all embedded documents in DB. Here is a sample:
public class Account{
private String userName;
private String password;
//...
private Profile profile;
//...
public static class Profile{
//...
private Address address;
private List<Comment> comments;
//...
public static class Address {
// ...
}
public static class Comment {
// ...
}
}
}
Is the above nested class design better to a flat class design as the followings?
public class Account{
private String userName;
private String password;
//...
private Profile profile;
//...
}
public class Profile{
//...
private Address address;
private List<Comment> comments;
//...
}
public class Address {
// ...
}
public class Comment {
// ...
}
I have the following class structure
public Abstract class Person {
private String fullName;
private Address address;
private Phone ;
}
class Staff extends Person{
private String staffId;
}
I want to apply validation using JSR-303 on class Staff whereby Staff address and phone cannot have the value of null. However, I have some other classes that are class of Person where I don't wish to have the validation to be applied.
One way to do this that I could think of is by refactor Person and push the fields 'address' and 'phone' to Staff, but this means refactoring a lot of other classes (and not to mention redundancy this shall cause), and hence something I want to avoid.
Update.
I have changed Staff class, as follows
public class Staff extends Person {
#NotNull
private String staffEmploymentId;
public String getStaffEmploymentId() {
return staffEmploymentId;
}
public void setStaffEmploymentId(String id) {
this.staffEmploymentId = id;
}
#Override
#NotNull
public void setPhones(List<Phone> phones) {
super.phones = phones;
}
#Override
#NotNull
public void setAddress(Address a) {
super.address = a;
}
#Override
#NotNull
public Address getAddress(){
return super.address;
}
}
However, I've got the following error.
javax.validation.ValidationException: Property setAddress does not follow javabean conventions.
I am using Apache BVal, as opposed to Hibernate Validator.
Annotate getters instead of fields using annotations from JSR-330.
You can override getters in Stuff and annotate them.
Hi i want to know if exist a way to avoid duplicate code in this code. Now i have an action class named CustomerAction this class handle the behaviour of the request (it's like a controller) and i have a CustomerPOJO with attributes like id, name, last_name etc. Now i have to add attributes to CustomerAction to handle the data submited from the form. Is there any way to bypass the action with my CustomerPOJO ?
public class CustomerAction {
private String nombre;
private String apellido;
private String dni;
private String fechaNac;
private String obraSocial;
private String nroAsociado;
private String plan;
private String password;
private String email;
private String telParticular;
private String telCelular;
private static final Log log = LogFactory
.getLog(CustomerAction.class);
public String execute() throws Exception {
if ("cancelar".equals(this.getAccion())) {
log.debug("Executing 'cancelar' action");
return "login";
}
if ("registro".equals(accion)) {
log.debug("Executing 'registro' action");
IReferenceDataBusinessDelegate ud = new ReferenceDataBusinessDelegate();
ud.signCustomer(this.getNombre(), this.getApellido(),
this.getDni(), this.getCorreo(), this.getContrasena());
return "login";
}
}
public class Customers implements java.io.Serializable {
private long id;
private String dni;
private String name;
private String lastName;
private String email;
private String password;
private String phone;
private String cellphone;
private Date birthDate;
private Date creationDate;
private Date lastAccessDate;
private byte active;
private Set<Profesionales> profesionaleses = new HashSet<Profesionales>(0);
private Set<Pacientes> pacienteses = new HashSet<Pacientes>(0);
public Customers() {
}
}
Yes, use ModelDriven, and use a Customers as the model.
http://struts.apache.org/2.x/docs/model-driven.html
You'll need to make sure the "modelDriven" interceptor is in your stack.
How/where to initialize the model depends on your particular usage scenario; you can do it in a getter as shown in the docs, in a prepare() method if you need to reload it from the DB, etc.
I'm not sure what you mean by "bypass the action."
Please note that the ad-hoc dispatch mechanism implemented here with the accion parameter duplicates functionality provided by Struts 2 using the method attribute of the action configuration. I don't recommend using ad-hoc dispatch mechanisms as it makes understand program flow more difficult than necessary.