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
).