2

I am searching for a architectural solution in instantiating different child classes based on an object type or extending Base class with the methods of child classes.

To give an example: There is a base class User and several child classes Partner, Client, Moderator which have specific methods an their own constructors. When I am calling

$user = new User($userid);

I want User class

class User
{
  public function __construct($userid) {
    self::initDB(); 

    if ($this->isPartner()) {
        //extend this class with the methods of "Partner" child class and run "Partner" class constructor
    }

    if ($this->isClient()) {
        //extend this class with the methods of "Client" child class and run "Client" class constructor
    }

    if ($this->isModerator()) {
        //extend this class with the methods of "Moderator" child class and run "Moderator" class constructor
    }
  }
}

To return me an object with all of the methods depending on what roles does user have.

I know my logic is broken somewhere and the example I provided is wrong. But the only solution I see now is to build one giant class that has all of the methods for all of the roles - which looks like a mess.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262

2 Answers2

4

First of all, your database logic should be totally separate from your domain objects (User, etc). Otherwise you are violating the single responsibility principle (SRP).

Set up your classes something like the following (base class User and multiple subclasses):

class User 
{
  private $id;
  // getters and setters go here
}

class Moderator extends User {}
class Partner extends User {}
// etc

Then, create some sort of UserManager class that implements an interface that looks like the following:

interface UserManagerInterface {
  function loadUserById($id);
}

The implementation of that method should load the passed user id's information from the database, look at what type it is (partner, moderator, etc) and then instantiate the appropriate class and hydrate the appropriate information.

Lusitanian
  • 11,012
  • 1
  • 41
  • 38
  • Lusitanian, thank you for the answer! So the way how I would create an instance of a User (or child) object would be 1) Create an instance of a UserManager class 2) Call the method loadUserById which will return a proper object It looks like the same as factory pattern does (By having class userManager to have one static method). But factory pattern just looks shorter and nicer. Why it's worse? – Anton Velikanov Jan 15 '13 at 02:17
  • UserManager would not be a static class. It can be instantiated and promotes code that is extensible and easy to unit test, and also keeps inline with the single responsibility principle (of OOP). – Lusitanian Jan 15 '13 at 02:49
2

The problem is that you cannot call new User and get anything other than a User object.

This sounds like the perfect use-case for the factory pattern.

The simplest form of this uses a static method to invoke the correct constructor.

So you could have code like this:

class User {
    public static function create($userid) {
        // get user from the database

        // set $isPartner to true or false
        // set $isClient to true or false
        // set $isModerator to true or false

        if ($isPartner) {
            return new Partner($userid);
        } elseif ($isClient) {
            return new Client($userid);
        } elseif ($isModerator) {
            return new Moderator($userid);
        } else {
            return new User($userid);
        }
    }
}

You can then call User::create($userid) to get the appropriate object.

If your code is appropriately structured, it may well be possible to have code along the lines of Lusitanian's answer (fleshed out) that would do a better, more flexible job.

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • This doesn't solve the problem of the SRP violation and in fact introduces the static factory anti-pattern. A factory is the right way to go, but it should be in another class to ensure testable, SOLID code. – Lusitanian Jan 15 '13 at 00:49
  • @Lusitanian I don't think you're going to achieve single responsibility starting from this point, to be honest. Of course an instantiated, flexible, testable factory is often the ideal, but it's not always achievable. I have edited my answer slightly; I hope it pleases you. – lonesomeday Jan 15 '13 at 00:56
  • Hehe, I suppose my main issue is that he asked about architecture and tagged this with oop so if he's just starting his application pointing him in the right direction would be good, but I do understand where you're coming from. – Lusitanian Jan 15 '13 at 00:58
  • What if **User** is both **Client** and **Partner**. Is it possible to have multiple extending/inheritance then? – Anton Velikanov Jan 15 '13 at 02:20
  • @AntonVelikanov The only way to do multiple inheritance is through the traits feature, but this could easily make your code an utter mess. – lonesomeday Jan 15 '13 at 07:29