0
\$\begingroup\$

I'm making the very common example of Factory design pattern which creates a factory of cars and return an instance of a car. I found a example here https://refactoring.guru/design-patterns/factory-method/php/example and decided to make my own factory. My question is why do I need a Creator for each car?

<?php


interface Car
{
    public function drive();
} 
<?php
require_once('Car.php');

class Audi implements Car
{
    public function drive()
    {
        return "driving an Audi";
    }
}
<?php
require_once('Car.php');

class Mercedes implements Car
{

    public function drive()
    {
       return "driving an Mercedes";
    }
}
<?php


abstract class CarFactory
{
    abstract public function factoryMethod(): Car;

    public function someOperation(): string
    {
        // Call the factory method to create a Product object.
        $product = $this->factoryMethod();
        // Now, use the product.
        $result = "I want to drive something..... " .
            $product->drive();

        return $result;
    }
}
<?php
require_once('CarFactory.php');
require_once('Car.php');
require_once('Audi.php');

class AudiCreator extends CarFactory
{

    public function factoryMethod(): Car
    {
        return new Audi();
    }
}
<?php
require_once('CarFactory.php');
require_once('Car.php');
require_once('Mercedes.php');

class MercedesCreator extends CarFactory
{

    public function factoryMethod(): Car
    {
       return new Mercedes();
    }
}
<?php

require_once('car/AudiCreator.php');
require_once('car/MercedesCreator.php');

function clientCode(CarFactory $carFactory)
{
    // ...
    echo "Client: "
        . $carFactory->someOperation();
    // ...
}

echo "The client . <br>";
clientCode(new AudiCreator());
echo "\r\n";
echo  "<br>";
echo  "<br>";

clientCode(new MercedesCreator());
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Find yourself a real world example of factory pattern. You've taken a misleading implementation written by Someone who probably did not understand the pattern himself and you renamed the classes. Cars, animals and alike are just crappy examples of something that lacks purpose, too far from reality, too far from problems we actually solve every day. \$\endgroup\$
    – slepic
    Commented Oct 11, 2020 at 9:37
  • 1
    \$\begingroup\$ Is this working code from a project you wrote, that is a requirement for the Code Review Site. Besides learning factory patterns what is the ultimate goal of what the code is supposed to do? That needs to be clearly stated in the question, generally as part of the title and in an English paragraph following the title. Please see the guidelines for asking a good question. As a side note, the model you chose is too simple which is why you are asking about different creators. \$\endgroup\$
    – pacmaninbw
    Commented Oct 11, 2020 at 12:29

1 Answer 1

3
\$\begingroup\$

I think you're thinking of this backwards. The point isn't that you need to define special subclasses to use the factory method. Rather, when you need to define special subclasses containing their own complex functionality, then you use the factory method.

From the Gang of Four:

Use the Factory Method pattern when a class can't anticipate the class of objects it must create, [or it] wants its subclasses to specify the classes it creates [...].

I think part of the problem is that your own example is a bit confusing. Interfaces are more for defining things different objects can do. You'll often hear people describe an interface as a sort of contract, because any object that implements an interface is required to implement every function of that interface. A Car would be more logically defined as a class because an Audi and a BMW are a type of car. On the other hand, it might make sense to define a Drivable interface, implemented by an abstract Vehicle class.

interface Drivable
{
    public function drive();
}

You could then define abstract base classes for each of the generic base object classes that implement this interface.

abstract class Vehicle implements Drivable
{
    abstract public function drive();
}

Once you do that, then you can implement functionality for each object.

abstract class Car extends Vehicle
{
    abstract public function drive();
}

class Audi extends Car
{
    public function drive()
    {
        //...
    }
}

abstract class Aircraft extends Vehicle
{
    abstract public function drive();
}

class Airbus extends Aircraft
{
    public function drive()
    {
        // ...
    }
}

Keeping with the literal nature of your example, think about how different an aircraft factory and a car factory are. They're both factories that make vehicles, sure, (which is why their respective factories could -and probably would- be derived from the same base abstract VehicleFactory class) but the process of building an aircraft is so different from that of building a car that you have to define special factory subclasses for each of them.

Getting more specific, let's focus on why and when you would use a factory. Suppose you're writing a game where a player can purchase a car. How do you know which car to instantiate until the user picks it?

A simple script might do something like this.

$car = null;

switch ($choice) {
    case 'BMW': {
        $car = new BMW();
    } break;

    case 'Audi': {
        $car = new Audi();
    } break;

    ...
}

$car->drive();

This is all the factory pattern is. The difference is that an actual factory class gives you several benefits that this simple script doesn't.

First of all, consider a CarFactory class that replicates the above script's functionality.

class CarFactory
{
    public static function create($car) : Car
    {
        switch ($car)
        {
            case 'BMW': return new BMW();
            case 'Audi': return new Audi();
            // ...
        }
    }
}

$car = CarFactory::create($choice);
$car->drive();

Right off the bat, you don't need null as the sentinel value for "the user hasn't picked a car yet." The car doesn't exist yet, so the car variable doesn't exist yet.

Second, a script can't inherit or be derived from anything; it just is. In contrast, if you wanted your player to also be able to purchase an Aircraft, or any kind of transport derived from the Vehicle class, you can define a base VehicleFactory class from which you can implement your CarFactory and AircraftFactory classes.

interface Build
{
    public static function build($choice);
}

abstract class VehicleFactory implements Build
{
    abstract public static function build($choice) : Vehicle;
}

class AircraftFactory extends VehicleFactory
{
    public static function build($aircraft) : Aircraft
    {
        switch ($aircraft)
        {
            case 'Airbus': return new Airbus();
            case 'Boeing': return new Boeing();
            // ...
        }
    }
}

class CarFactory extends VehicleFactory
{
    public static function build($car) : Car
    {
        switch ($car)
        {
            case 'BMW': return new BMW();
            case 'Audi': return new Audi();
            // ...
        }
    }
}

$aircraft = AircraftFactory::build($choice);
$aircraft->drive();

$car = CarFactory::build($choice);
$car->drive();

Hopefully this gives you an idea of why the factory pattern is useful. The upshot is that your base factory class defines the interface by which users will request an object be created, and the factory subclasses take care of the actual definition for how it happens. In addition, defining each factory's return type in terms of a base object (i.e..., the CarFactory returns a Car) allows us to choose the actual subclass of that object that actually gets created at runtime (users can pick a BMW or an Audi, both of which are Cars, all of which are Vehicles, all of which are Drivable).

\$\endgroup\$
2
  • \$\begingroup\$ Thank you for your explanation, now it's more clear. \$\endgroup\$ Commented Oct 11, 2020 at 12:18
  • \$\begingroup\$ Great answer to a poor question, you might want to flag questions that are too vague until you can vote to close. \$\endgroup\$
    – pacmaninbw
    Commented Oct 11, 2020 at 12:34

Not the answer you're looking for? Browse other questions tagged or ask your own question.