Search code examples
phpoopfactoryfactory-pattern

Factory Method - Can / should a Factory have several methods that create the same class?


Let's say I want to build a House and I have a house factory. The factory can already build houses using a blueprint. However, a new client comes in and asks us if we can build them a house from a sketch they provide. Would it be correct to add a new assembly line to the factory to handle these new houses? Or would it be preferable to create an entirely new factory for the homes built from a sketch? The output will always be a House, with specific attributes (i.e num sides, color, roof)

class House_Factory {
  public function create(Blueprint $blueprint) {
    $house = new House();
    $house->num_sides = $blueprint->num_sides;
    $house->color = $blueprint->color;
    $house->roof = $blueprint->roof;
    return $house;
  }

  // Is having another create method acceptable in a Factory?
  public function createFromSketch(Sketch $sketch) {
    $house = new House();
    $house->num_sides = $sketch->num_exterior_walls;
    $house->color = $sketch->exterior_wall_color;
    $house->roof = $sketch->roof;
    return $house;
  }
}

This doesn't seem right to me but I can't put my finger on it. Is there a better design pattern to follow for a situation like this?


Solution

  • In my opinion, it would be acceptable to have both methods in the same class. Where it makes sense to break out into different classes (and implement an interface) is when you are creating different outputs with the same type of inputs - for instance if you needed to make Houses, Churches, and Sheds, they would each have a factory inheriting a Building interface, each with a create method that takes a Blueprint as an argument (where Blueprint may be an interface and each concrete factory requires it's own variation of Blueprint, etc...).

    What makes your approach feel a little funny to me is that you have to name the create methods differently. In a strongly typed language like Java you would have multiple create methods, differentiated by the argument type, which is nice and clean.

    Another approach you could consider, if appropriate, is translate your Sketches to Blueprints with some kind of adapter.

    <?php
    
    class Blueprint
    {
        public $num_sides;
        public $color;
        public $roof;
    
        public function __construct($num_sides, $color, $roof)
        {
            $this->num_sides = $num_sides;
            $this->color     = $color;
            $this->roof      = $roof;
        }
    
    
    }
    
    class Sketch
    {
        public $num_exterior_walls;
        public $exterior_wall_color;
        public $roof;
    
        public function __construct($num_exterior_walls, $exterior_wall_color, $roof)
        {
            $this->num_exterior_walls  = $num_exterior_walls;
            $this->exterior_wall_color = $exterior_wall_color;
            $this->roof                = $roof;
        }
    }
    
    class House
    {
        public $num_sides;
        public $color;
        public $roof;
    
        public function toString()
        {
            return json_encode([
                'Sides' => $this->num_sides,
                'Color' => $this->color,
                'Roof' => $this->roof
            ]);
        }
    }
    
    class HouseFactory
    {
        public function create(Blueprint $blueprint)
        {
            $house            = new House();
            $house->num_sides = $blueprint->num_sides;
            $house->color     = $blueprint->color;
            $house->roof      = $blueprint->roof;
            return $house;
        }
    }
    
    class BlueprintMapper
    {
        public static function fromSketch(Sketch $sketch)
        {
            return new Blueprint($sketch->num_exterior_walls, $sketch->exterior_wall_color, $sketch->roof);
        }
    }
    
    $houseFactory = new HouseFactory();
    $blueprint    = new Blueprint(4, 'Red', 'Slate');
    $house1       = $houseFactory->create($blueprint);
    
    $sketch          = new Sketch(4, 'Blue', 'Tin');
    $sketchBlueprint = BlueprintMapper::fromSketch($sketch);
    $house2          = $houseFactory->create($sketchBlueprint);
    
    echo $house1->toString() . PHP_EOL;
    echo $house2->toString() . PHP_EOL;
    
    {"Sides":4,"Color":"Red","Roof":"Slate"}
    {"Sides":4,"Color":"Blue","Roof":"Tin"}