5

I was reading up on factory methods. Can someone explain why it is suggested that factory methods be located in a separate factory class?

I am pulling my example from here: http://www.devshed.com/c/a/PHP/Design-Patterns-in-PHP-Factory-Method-and-Abstract-Factory/

class ETF {
  var $data;
  function __construct($data) {
    $this->data = $data;
  }
  function process() {}
  function getResult() {}
}

class VirtualCheck extends ETF {}
class WireTransfer extends ETF {}

class ETFFactory {
  function createETF($data) {
      switch ($data[‘etfType’]) {
      case ETF_VIRTUALCHECK : 
        return new VirtualCheck($data);
      case ETF_WIRETRANSFER :
        return new WireTransfer($data);
      default :
        return new ETF($data);
      }
  }
}

$data = $_POST;
$etf = ETFFactory::createETF($data);
$etf->process();

I would tend to instead write it like this:

class ETF {
    final public static function factory($data) {
        switch ($data[‘etfType’]) {
            case ETF_VIRTUALCHECK :
                return new VirtualCheck($data);
            case ETF_WIRETRANSFER :
                return new WireTransfer($data);
            default :
                return new ETF($data);
        }
    }

    var $data;
    function ETF($data) {
        $this->data = $data;
    }
    function process() {}
    function getResult() {}
}

class VirtualCheck extends ETF {}
class WireTransfer extends ETF {}

$data = $_POST;
$etf = ETF::factory($data);
$etf->process();

Am I wrong in doing this?

2
  • 1
    I see at least one disadvantage: in your configuration, all classes extending ETF are factories. It is unnecessary behaviour (yet factory is for hiding implementations). Commented Dec 12, 2016 at 14:51
  • Where it was suggested? I would like to see that statement in the context. Commented Dec 12, 2016 at 15:12

2 Answers 2

4

I would not say you're "wrong", but there is a "smell". By combining the factory method in the manufactured class, the architecture violates a few of the SOLID guidelines:

  1. Single responsibility: the class now does two things (when it should do one).
  2. Open/closed principle: the class is only half open (when it should be fully open).
  3. Interface segregation: consumers of the class use the manufactured object proper methods, without the factory and vice versa (when consumers should depend only on needed methods)

I've found that the more SOLID a class is, the easier the class is to maintain in the long-term. Thus I wouldn't consider SOLID violations immediate problems, just a signal of possible trouble down the line.

So, what trouble might you run into down the line? Well, if the factory itself becomes more complex, you'll need methods to handle that additional work. These methods would not, necesasrily, be used by the class proper methods. So you'd end up with code like this:

class ETF {
    final public static factory($kind) {
        switch ($kind) {
        case 'A':
            $etf = static::factoryHelperForA();
            break;
        case 'B':
            $etf = static::factoryHelperForA();
            break;
        }
        return $etf;
    }

    public function apiMethod1() {
        $this->apiMethod1Helper();
    }

    public function apiMethod2() {
        $this->apiMethod2Helper();
    }

    // factory helper
    private static function factoryHelperForA() {
        /* lots of code */
    }

    // another factory helper
    private static function factoryHelperForB() {
        /* lots of code */
    }

    // ugh, now we have api method helpers... totally different responsibility
    private function apiMethod1Helper() {
        /* lots of code */
    }

    // still more...
    private function apiMethod2Helper() {
        /* lots of code */
    }
}

So you can see it starts to become messy as the needs of the factory grow, and as the needs of the manufactured class grow. This is what the SOLID principles are guiding you away from.

If it's important to you now to build a future flexible class, then I'd suggest busting the factory out into its own EtfFactory class.

Sign up to request clarification or add additional context in comments.

5 Comments

Factory method doesn't violate SOLID principles. To simplify counter example lets imagine factory method is protected. 1) It doesn't break single responsibility because it is even not in the public interface 2) Factory method doesn't create any problems with extending class and often simplifies it 3) That comment is irrelevant to the case because existence of factory methods in the code doesn't tell us anything about the nature of the interfaces we use
Can you describe why factory helpers would be used? Shouldn't any helper code be in the sub-classes themselves. Isn't the only goal of the factory to determine which sub-class is correct, then return an instance of that class, while allowing the sub-class to do all the actual work?
@IhorBurlachenko I don't understand your comment. I'm not saying factory methods violate SOLID. I'm saying factory methods inside the manufactured class violate SOLID. You go on to say the same thing I do in your own answer: "Technically it violates single responsibility principle".
@bishop and I explained why it doesn't :) I mentioned in my answer why it technically violates single responsibility principle and why in my opinion we shouldn't care about it.
@Beachhouse The goal of this factory at this time is to determine which sub-class is correct. Will that be the only goal for all time? Who's to know. SOLID helps us minimize total work over time as goals shift. And that's my point. The code you have now is like limburger cheese. It's a delicious cheese, fully servicable for enjoyment, but it has an unmistakable odor. That's not a problem, it's just an attribute of the situation.
0

I suspect there is some misusage of Factory Method and Abstract Factory terms.

Factory Method allows us to specify interface of some object which we expect to use in the code but don't know its implementation at the moment or we know only some default implementation which can be different in some cases. So we leave this decision to subclasses. Another possible usage is hiding object creation details or defining a constructor-like method for simpler usage.

Abstract Factory provides an interface for creating families of related objects. We use it when want to isolate the whole creation process from the usage. For example, I used it when developed several different turn-based card games. All games were different and had different logic (diff number of players, diff winning combinations, the order of turns, point system, etc). But since they were turn-based card games they were in the same time the same (from diff angle): you wait for enough amount of players (each player object is diff for each game), you have a dealer (another object which encapsulates rules and is diff for each implementation) and you run the game just switching turns between players until some condition happens. So I was able to use the same gaming framework which I initialized with diff abstract factory implementation.

Regarding your question. In your case you have a final static constructor-like method. Technically it violates single responsibility principle but I would say it is rather related to discussion "should we call static methods on instances". If we don't call static methods on instances it violates nothing. In my opinion, it is a valid usage of factory method and you won't have any problems with this code.

Useful links:

3 Comments

Why do you suspect the OP misunderstandings concrete and abstract factories? And could you provide some more insight as to the OP specific architectural question about the pros and cons of embedding the factory method inside the manufactured class?
@bishop OP said it was suggested to use factory methods in separate factory classes. That's obviously wrong and looks like misunderstandings between patterns. I asked where it was suggested btw.
Regarding pros and cons of that method. Its only cons is that you'll have to update it every time new class is added. This method doesn't increase coupling between components, in the worst case, it is easy to refactor this code. If you look at a class as at something which creates instances of the class this approach doesn't look that bad. It is static(class)-method and it relates to the class, not the object it describes. For example in Python class is an object which creates instances of the class and is a factory method itself.

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.