Question

I have a data model object User. My app also has some other data model objects, Fork and Options, for example. Users have forks and branches. My app has to run a lot of queries with some combination of User/Fork/Options etc. information. For example, you can see a page of User's Forks. This would require a query that joins that User (e.g. the logged in user in the session) on Forks.

I don't want to violate the Law of Demeter, and I am also typically against getters (and setters) in general, so I don't want to implement User::getID() or User::getUsername().

However, the alternative does not seem much better to me. What has ended up happening is that I implement various methods in User to run these queries (e.g. User::getForks()). In general this works, but the User class has become monolithic which is bad in its own way.

What's more is I'm not sure how I would resolve a query of two data model objects. For example, I could have a User with an id and a Fork with an id and want to check that the fork belongs to the user. This requires that either the Fork exposes its id to the user, the User exposes its id to the Fork, or both expose their ids to the controller (or whatever else). None of these seem desirable, and I'm not sure which to choose. Is there some other alternative that I'm missing?

In another similar step, I'm not sure of the best way to add information to the view. I have a view object, and typically I use a method like User::addForksToView(View $view), which will run a query or queries and do some processing, but this also increases the size and responsibility of User. I think this is a similar problem.

Was it helpful?

Solution

Rather than a getForks method in the User object, use a ForksFactory or something like it. The factory can have methods like fromUser(User $user) and byId($id). That way the User object does not have a hidden dependency upon the Fork object, and probably a hidden dependency on database access. The ForksFactory has a dependency on database and contains the code needed only to get the data and create Forks, that's all it knows how to do. Where as the Fork object would not have a dependancy on the database, and now the User object doesn't need to know anything about Fork, indeed Fork could not exist at all and the User doesn't care. This is the idea of a single-purpose object.

As far as getters and setters, why not expose properties like id and name as public? Alternately, you can use the magic methods __get and __set to control which properties are treated as read-only, if that is your concern. Otherwise, you may as well expose the properties -- that is perfectly acceptable.

The other code you mention sounds like it suffers from the same problem -- one object, one purpose. Every object should, in theory, stand in isolation or have any dependencies injected in the constructor. Your objects should not require that another object is defined or one of its methods will fail.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top