Freeman
Freeman

Reputation: 1190

OOP Design - Performance vs Loose coupling

I'm looking to improve my OOP code design as far as I can, but I'm stuck on this issue.

For example, I want to get the profiles of all friends of a user, in a social networking site. So I have tables Friendships and Profiles. And I have classes Friends and Profile.

Let Friends be the class that handle a user's friendships and Friends::getFriendsProfiles() be the function that fetches and returns all the user's friends' profiles.

So inside my function Friends::getFriendsProfiles() I can either do a

  1. A table join (e.g. SELECT * FROM Friends LEFT JOIN Profiles ON Friends.user2 = Profile.userId WHERE Friends.user1 = :userid), or

  2. I can just fetch the user ids of the friends, create a Profile object for each friend id, and call $profile->getProfile($friendid) which runs a query (SELECT * FROM Profiles WHERE userId = $friendid) to fetch a friend's profile. Then return the set of all the friends' Profile object.

Option 1 Disadvantage: My Friendship class knows about Profiles. And when I need to make a change on the way a profile is returned (e.g. I want to add another property to each profile object), I need to change it in 2 different places.

Option 2 Disadvantages: Instead of making 1 query (which I think should run in O(1) ?), it is now O(n) where n is the number of the user's friends.

But Option 2 is so much cleaner and loosely coupled. Which option should I take?

Upvotes: 1

Views: 358

Answers (2)

bitWorking
bitWorking

Reputation: 12675

I would definitely go with Option 1 and only use 1 query. The Friends class must not know much about the Profiles if the constructor could work with an array. You could do something like:

SELECT Profiles.*
FROM Friends
LEFT JOIN Profiles ON Friends.user2 = Profile.userId
WHERE Friends.user1 = :userid

Then in the loop:

$profiles = array()
while ($data = mysqli_fetch_assoc($result)){
    $profiles[] = new Profile($data);
}

One perhaps cleaner solution would be to make it a method of the Profile class.

Profile::getFriendsProfiles()

The loop:

$profiles = array()
while ($data = mysqli_fetch_assoc($result)){
    $profiles[] = new self($data);
}

The constructor from Profile could be:

function __constructor(array $data = null)
{
    if (null !== $data) {
        // fill properties
        $this->id_profile = $data['id_profile']; // example
        ...
    }
}

It would be better if the SQL code would be in another object the Table Data Gateway. If you really wan't to improve your OOP then read about software design patterns. You can start with Martin Fowlers's site here.

Upvotes: 2

user1914530
user1914530

Reputation:

Option 1 Disadvantage: My Friendship class knows about Profiles. And when I need to make a change on the way a profile is returned (e.g. I want to add another property to each profile object), I need to change it in 2 different places.

Domain objects are naturally going to have some coupling. This is just the reality of the system you are modelling. This is not so much a coupling problem between friendship and profile its a problem of tight coupling between your business layer and your data layer. If you had a datamapper, finder class etc and made your business object persitance ignorant then changes like this should not matter too much.

If you use the second option you run into the n+1 select problem. In this instance I would not sacrifice performance when there are more important areas that you could consider decoupling.

Upvotes: 1

Related Questions