Search code examples
phpoopautocompletephpstormphpdoc

Proper DocBlock comment for the method of a class, which implements Factory design pattern


What I mean by proper DocBlock comment is a comment which:

Here is the class itself:

class Factory_DomainObjects
{
    /**
     * Build domain object
     *
     * @param $name
     *
     * @return M_UserObject|M_TransactionObject
     */
    public function build($name)
    {
        $class = 'M_' . $name . 'Object';
        return new $class();
    }
}

It returns one object from Core_Object hierarchy depending on $name argument.

Currently Core_Object hierarchy looks like this:
enter image description here

I provided @return tag with a M_UserObject|M_TransactionObject type description. It provides autocompete for PHPStorm and conforms to PHPdoc standard.

- But that's exactly what you want, what's the problem?
- Yes and no, keep reading :)

The problem:
What if Core_Object hierarchy will grow to something like this? enter image description here

This will turn @return tag description into a mess:

/**
 * @return M_TransactionObject|M_UserObject|M_Foo|M_Foo1|M_Foo2|M_Foo3|M_Bar|M_Bar1|M_Bar2|M_Bar3
 */

The only workaround I found so far: using separate build method for each object, ie

/**
 * Build user domain object
 * 
 * @return M_UserObject
 */
public function buildUser()
{
    return new M_UserObject();
}

/**
 * Build transaction domain object
 * 
 * @return M_TransactionObject
 */
public function buildTransaction()
{
    return new M_TransactionObject();
}

What pitfalls you think my workaround has? What would you suggest instead?


Solution

  • The simple answer here is that you should not be returning multiple object types from a single method. Let me elaborate:

    When I say "types", I mean objects that don't all share the same type information in some way. In your case they are all CoreObject (which is a horrific name by the way). So I would simply label the return type-hint as CoreObject and be done with it.

    The preferred way of handling something like this though is to use an interface, and have your method return an implementation of that interface. If you don't have a common interface for all return types, then you need to implement different methods (at the least, or possibly different factories).