0

I have an object that I want to populate with information. I retrieve the information from a number of different services. I made a helper class that has one public method and then has a number of private methods that do the work to call the services. What I have written works fine but I'm not sure if it is the correct way to do this.

You may be wondering why I need an object holding all this information. I need it all in one object because I create a json object from this java object and pass that to the javascript layer.

What is wrong with my approach and is there a programming paradigm I should be following to do something like this?

Example:

Person object with getters and setters for firstName, lastName, age, height, weight, list of favourite foods, list of favourite countries, list of comments.

  • Service 1 gives firstName, lastName, age, height and weight
  • Service 2 gives list of favourite countries and list of favourite foods
  • Service 3 gives a list of the comments made by the person

I have a personHelper class that looks like this:

public class PersonHelper{

public Person getPerson(userDetails){
    Person person = new Person();
    this.setPersonDetails(person, userDetails);
    this.setFavourites(person, userDetails);
    this.setComments(person, userDetails);

    return person;
}

private Person setPersonalDetails(Person person, UserDetails userDetails){

    returnedObj = callToService1(userDetails);
    person.setFirstName(returnedObj.getFirstName());
    person.setLastName(returnedObj.getLastName());
    person.setAge(returnedObj.getAge());
    person.setHeight(returnedObj.getHeight();
    person.setWeight(returnedObj.getWeight());

    return person;
}

private Person setFavourites(Person person, UserDetails userDetails){

    <List>favsList = callToService2(userDetails);
    person.setFavourites(returnedObj.getFavs(favsList));

    return person;
}

private Person setComments(Person person, UserDetails userDetails){

    <List>commentsList = callToService3(userDetails);
    person.setComments(returnedObj.getComments(commentsList));

    return person;
}

}

and then in my controller I call

person = personHelper.getPerson(userDetails);
jsonResponse = jsonProcessor.writeAsString(person);

return jsonResponse; // returns the ajax response to js

Thanks in advance for any help or suggestions.

EDIT: After more research I found that the object I am populating is referred to as a Data Transfer Object and I am populating it using the Java Bean method.

irl_irl
  • 3,785
  • 9
  • 49
  • 60
  • 1
    Looks fine, its the way I would have done it – Wand Maker Dec 13 '15 at 16:17
  • 1
    Looks good to me. I'm just stating my opinion here - you can have the statements from those 3 methods in `getPerson` method itself. As these are short methods they'll be inlined anyways at runtime. One more change is that you don't have to explicitly say `this.setPersonDetails()`, just `setPersonDetails()` is fine because `this` is implied there. – Arkantos Dec 13 '15 at 16:19
  • Thanks (both of you). I just felt something was incorrect about it. Some of the methods are larger than the ones in the question. Also, I have unit tests for all the private methods (I changed them to protected so I could test them). So that's why I split it out into private/protected methods. – irl_irl Dec 13 '15 at 16:25

2 Answers2

2

There's a trend these days to limit the mutability of objects so your setter-based approach, although workable, is sometimes not seen as the best way to create an object, even a data transfer type of object. One other thing to consider is how many objects know about each other and how much they know - it seems your PersonHelper class needs to know pretty much everything about UserDetails and Person. So if you add a field to Person, you need to add it to UserDetails and also add to PersonHelper to get that field populated.

For your type of object, you might find the Builder pattern useful. A builder is a short-term transient object designed to gather data for construction. Often the builder will have a fluent API, and gets passed to the (private) constructor of the transfer class. That means that all your code responsible for building the object is clear that that is its responsibility because it works with a Builder. Meanwhile, the constructed transfer object is effectively immutable and it becomes significantly easier to reason about the thread-safety of your code and to understand what values something might have at different parts.

public class Person {
    private final String firstName;
    private final String lastName;

    private Person(final PersonBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
    }
    ... usual getters etc ...

    public static class PersonBuilder {
        private String firstName;
        private String lastName;

        private PersonBuilder() {
        }

        public PersonBuilder withFirstName(final String name) {
            this.firstName = name;
            return this;
        }

        public PersonBuilder withLastName(final String name) {
            this.lastName = name;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }

    public static PersonBuilder newPerson() {
        return new PersonBuilder();
    }
}

In this example the builder is a little over-wieldy, but when you've got twenty or thirty different pieces of data which are somehow optional it can make sense and makes for very easy to read construction code...

Person.newPerson().withFirstName("Sam").withLastName("Spade").build()

It seems to me that your 'UserDetails' object could be turned into a kind of builder. And so your 'PersonHelper' class would end up just calling userDetails.build() rather than knowing all about what fields the Person object (and userDetails object) contains.

sisyphus
  • 6,174
  • 1
  • 25
  • 35
  • Thanks for the answer. Is using the builder pattern applicable if I always (and will always) populate all the fields on the Person object? It seems like it would be useful if some fields were optional. Also, (this may be a stupid question) where would I be calling out to my services in your scenario? In the builder methods or still in the helper/controller where the builder is called? – irl_irl Dec 13 '15 at 23:19
  • This answer gave me a lot of info but I am using Java Beans throughout the application so will stick to that pattern.. so I marked the other answer as correct. Thanks again though. – irl_irl Dec 14 '15 at 00:22
1

There is no general paradigm for your question, but here are a few tips for your design:

  1. It seems that your person data (names, favourites) is distributed among several data stores and you have to gether it all in your PersonHelper class. I don't know if this services are used anywhere else, but from the controller point of view this helper should be a service too.

  2. Since your service invocations are independent, you can execute them in parallel

  3. For some kind of applications it can be even better if you expose these services for UI level. For example, if data is presented in different UI blocks, client can make several asynchronous requests and display the data as soon as responses are received.

AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • 1) Yeah that is correct. The data is distributed. If this helper was a service it would be a service just calling other services. Is this common practice? – irl_irl Dec 13 '15 at 19:59
  • 2) Do you mean using threads, like in http://stackoverflow.com/questions/11218720/running-a-task-in-parallel-to-another-task? – irl_irl Dec 13 '15 at 20:00
  • @GreenRails 1. it depends on how services are implemented. if they are just singletons without AOP overhead, you can easily call one service from another. if service invocations call AOP hooks, it should be reviewed. 2. yes, you can use ExecutorService (for example), pass tasks to it, and then gether results from Futures. – AdamSkywalker Dec 13 '15 at 20:02
  • 3) That is the way the application is designed. This "Person" object is just an example. My actual object has some data and some rules and so it all needs to come together. – irl_irl Dec 13 '15 at 20:02