2

I am trying to apply the factory design pattern to a real world problem in an application. I am using data objects (kind of like ORM but not quite) where an object represents a row from the database and its properties are the columns. Having said this - I have 3 different types of users which all extend the base class User and are all kept in the same database table of users. I have an extra column type that I use to determine the type of user.

So to fetch a user with id 1 I would use this code:

$user = User::getByPK(1);

However the different types of users have different properties and methods and I need to get the appropriate instance, which is where I am stuck. I created a factory class but I am not quite sure how to call it. I need to have the information about the user from the database before I can determine what type of user it is, however I need to instantiate the appropriate class and these two things are kind of dependant of each other.

Here is what I made - I get the appropriate instance but all of the information for the user is kept in the base class.

class UserFactory {

    public function getUser($type) {

        switch($type) {
            case User::ORDINARY:
                return new OrdinaryUser();

            case User::MODERATOR:
                return new Moderator();

            case User::ADMINISTRATOR:
                return new Administrator();
        }
    }
}


$user = User::getByPK(1); // Has all the information

$factory = new UserFactory();

$object = $factory->getUser($user->type); // Is instance of Administrator

What is the proper way to use the factory pattern in this case?

1
  • 1
    getUser() is a good name for a method of a Repository class. The names of the methods of a Factory class usually start with create to better express what a Factory does: it creates new objects. Commented Aug 21, 2015 at 22:34

1 Answer 1

5

The idea of the factory pattern is to decouple and encapsulate the logic required to create an instance of an object, from the object itself. This separates concerns.

You currently have a static method on your User class that is responsible for fetching the user information from the database. This couples your User object to your database. It's also very rigid.

  • What if your users are stored in a different type of database?
  • What if your users are stored in an XML file?
  • What if your users are stored in a key/value store?
  • What if you want to create a mock repository of users for writing tests?

The factory pattern allows you to abstract these concerns away, and provide alternate implementations of a factory depending on your needs (assuming you had a common interface for each implementation). Maybe the way you create a user changes, but you don't want to also change everywhere you've needed to create a user - just how the user is created. Isolating this code into a factory allows for this.

A factory might take an instance to your DAL as well, if you've abstracted your DAL. (I'll be using IDataAccessLayer as a placeholder for your preferred DAL)

class UserFactory {
    private IDataAccessLayer $dataAccessLayer;

    public function __constructor($dataAccessLayer) {
        $this->dataAccessLayer = $dataAccessLayer;
    }

    public function createUser($userId) {
        $userDataSet = $this->dataAccessLayer->someQuery($userId);

        switch ($userDataSet->type) {
            case UserType::Administrator:
                return createAdministrator($userDataSet);
            case UserType::Moderator:
                return createModerator($userDataSet);
            case UserType::Ordinary:
                return createOrdinaryUser($userDataSet);
        }
    }

    private function createAdministrator($userDataSet) { /* Create and return an administrator */ }
    private function createModerator($userDataSet) { /* Create and return a moderator */ }
    private function createOrdinaryUser($userDataSet) { /* Create and return an ordinary user */ }
}

You'd then use this like:

$factory = new UserFactory($dataAccessLayer);
$factory->createUser(1);

(Bear with me, I haven't coded PHP in some years, so some of my syntax may be off, but the idea is the same)

Now, personally, it looks like you need to get even more granular here. I'd create a UserRepository following the Repository pattern. This is because the factory shouldn't really be accessing the database in my opinion. It should just use the data from the database to create the user. The repository pattern should be responsible for getting the data from the database, and then feeding that data to the factory pattern to get an object.

class UserRepository {
    private IDataAccessLayer $dataAccessLayer;

    public function __constructor($dataAccessLayer) {
        $this->dataAccessLayer = $dataAccessLayer;
    }

    public function getUserById($id) {
        $userData = $this->dataAccessLayer->fetch(someQuery);

        $factory = new UserFactory();
        return $factory->createUser($userData);
    }
}

Then you'd have a simpler factory:

class UserFactory {
    public function createUser($userData) {
        switch ($userData->type) {
            case UserType::Administrator:
                return createAdministrator($userData);
            case UserType::Moderator:
                return createModerator($userData);
            case UserType::Ordinary:
                return createOrdinaryUser($userData);
        }
    }

    private function createAdministrator($userDataSet) { /* Create and return an administrator */ }
    private function createModerator($userDataSet) { /* Create and return a moderator */ }
    private function createOrdinaryUser($userDataSet) { /* Create and return an ordinary user */ }
}

And from your code, you'd have something like:

$userRepository = new UserRepository($dataAccessLayer); //This is probably done somewhere higher in your code.
$userRepository->getUserById(1);

You'll probably want to go ahead and create the following interfaces and implement these as well. This will allow you to enforce your contract, as well as set yourself up to be able to swap out implementations if you need.

public interface IUserRepository {
    function getUserById($userId);
}

and

public interface IUserFactory {
    function createUser($userData);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Please let me know if anything is confusing, doesn't make sense, or question left unanswered and I'll revise!
No, this is all awesome! I was just wondering one thing - since most times there will be only 1 type of object - for example Note. I have a section which allows users to post notes to themselves and I have decided that there will not be other types of notes, just Note. In that case I think a factory would be not needed, I could feed the data from the gateway directly to the object constructor in the repository and have it populate itself. Would you agree with this?
Excuse me, but I don't understand - how is the Note class dependant on the library? Also, after a brief testing and getting to know this "mechanism", to get a data object I need to instantiate 3 classes in my controller - Repository, Gateway and Factory to which I need to pass a db connection, as opposed to what I used to do Model::byPK. Is this really how professionals are programming, it seems way too complicated q_q
Note would be dependent on the library that provides the domain model containing the record set from the database. In PHP, that probably isn't THAT big of a deal. However, in programming in general, this is something you should strive to avoid. Note is dependent because you're saying you'd place a creator function/constructor on your object that requires an instance of a class that is only available if a specific library is loaded. As far as your jab at professional programmers using complicated models - only you can decide if your project requires the use of design patterns.
This question was specifically about design patterns, so I'm not sure why you are now throwing off on their use. IF you don't think your project is complex enough to need design patterns, then don't use them. I'll tell you this though. Implementing design patterns and SOLID principles can make code maintenance a heck of a lot easier down the stretch. Until you start using them, you simply aren't going to understand the problems they solve.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.