0

Say there is some view that displays user details like name, age, email, etc. That view could maybe be constructed with the following pseudocode:

UserView(User user);

And that view would be able to pull all needed info from the user object. But maybe the User class has a property ID, and we have some type of service that is able to get user objects based on a passed in ID. We could then consider constructing UserView using just an ID and the service, and delegating the object retrieval to the internals of the View:

UserView(long userId, UserService userService);

Is this correct? Where is the line drawn on when you provide something only with a concrete object, and when you provide something with means to retrieve a concrete object? Is there some sort of best practive for this? Where would I find more information on this type of design decisions?

4 Answers 4

1

I'm afraid all the answers will be a bit opinionated. I think however most perhaps would agree, that you should try to provide the necessary parameters with a minimum of knowledge required for the consuming class.

In your examples, I wouldn't do any of those. Too much unnecessary information in both. I would do this:

UserPanel(String name, Image profilePicture, String description)

Or even go further and say that is too much information too, since the UserPanel now has to know how to display a name and description. And instead do:

UserPanel(Component name, Component profilePicture, Component description)

We assume here, that Component is some abstract UI element that can be anything. This way UserPanel can be defined without the knowledge of how those pieces are rendered.

I personally would try to avoid "pulling" data out of an object if at all possible, since that is counter to basic object-oriented principles.

0

A useful way to approach this is by separating layers (or dependencies for different contexts) and focusing on what the object need rather than how he can get it, UserView only needs User right? it doesn't need to get the DB (or your userService) only to eventually arrive with User object that it could get directly, also, logically, it doesn't need to have access to the UserService (what does it need it for?), instead, make some other object that handles this, retrieves the user from the UserService and creating the UserView` with it as a dependency.

Is this correct? Where is the line drawn on when you provide something only with a concrete object, and when you provide something with means to retrieve a concrete object? Is there some sort of best practice for this? Where would I find more information on this type of design decisions?

A book I can recommend is "Clean Architecture" by Robert C. Martin (Uncle Bob), it describes what objects should depend on and how to create a good architecture in general.

0

This will work, of course, but the latter is crossing more abstraction levels.

The latter passes 2 items of lower abstraction instead of 1 of higher abstraction.  Especially when two items are essentially conceptually bundled to be used together, we should get to the individual abstraction that represents that bundling quickly rather than treating the two as a pair for an extended duration.

Further, if the consuming client code already has a User instance object, and ready access to the UserService, it still must re-obtain the user id to pass to the method, and that method must re-lookup the User instance object in the UserService — kind of silly, pulling apart a higher level abstraction into lower level ones only to go back higher level again.

And, if the consuming client code already has a User instance object, but doesn't have access to a UserService?  You'll have to refactor all intermediate methods to deal with the pair instead of one higher abstraction — a move in the wrong direction.

0

Letting the View do the lookup would break the first SOLID principle (Single Responsibility)

You could

A - Pass in the concrete user object. eg UserView(User user)

B - Pass a UserFactory object to the view, then the view would call UserFactory->get() , returning a concrete user object. e.g

Class Definitions

class UserFactory{
    function __construct($userID,$userService){
       $this->userID =  $userID;
       $this->userService =  $userService;
    }
    function get(){
       return this->userService->getUser($this->userID);
    }
}



UserView(UserFactory user_factory)

Setting up the view

UserView(new UserFactoryt($userID,$userService));

In the view template

$UserFactoryt->get();

either way, end up keeping the User object retrieval logic out of the view, so the view only deals with the User object.

Edit: To answer your question more directly: you COULD delay the user retrieval if you think that's better for your use case. If there's a chance the user data will never be used, then it's better to retrieve it on demand.

Understanding the following concepts could be helpful too:

Disclaimer: I'm not a Java programmer, my examples use pseudo PHP code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.