简体   繁体   中英

Factory design pattern with object replaceability?

There are a lot of articles about factory pattern, but I see it fails at the very gist of it. Example:

final class Car
{
    public function carMethod() { echo __CLASS__.'.'.__FUNCTION__.'<br>'; }
}

final class Vehicle
{
    public function vehicleMethod() { echo __CLASS__.'.'.__FUNCTION__.'<br>'; }

}

final class Factory
{
    public function createNewCar() : Car
    {
        return new Car();
    }

    public function createNewVehicle() : Vehicle
    {
        return new Vehicle();
    }
}

So far so good. Now I want to make it configurable, because at this point, classes are still coupled to it:

final class Factory
{
    private array $map;

    public function __construct(array $map)
    {
        $this->map = $map;
    }

    public function createNewCar() : Car
    {
        return new $this->map['carClass'];
    }

    public function createNewVehicle() : Vehicle
    {
        return new $this->map['vehicleClass'];
    }
}

$factory = new Factory(
    'carClass' => 'Car'
    'vehicleClass' => 'Vehicle'
]);
$factory->createNewCar();

now factory is configurable. But unfortunatly, classes are still coupled, by the language itself, Of course. I can always ommit classes at the end of createNew..., methods? but what if it was a typed language? How to fully replace objects: What if I try to do:

final class Car2 {}

final class Vehicle2 {}

$factory = new Factory(
    'carClass' => 'Car2'
    'vehicleClass' => 'Vehicle2'
]);
$factory->createNewCar();

it will throw a fatal error. Car2 is NOT Car . Even using inheritance, its not always the possibility (notice the final keyword). I can't replace objects!

Edit: of course it can be solved with interfaces:

interface ICar
{
    public function carMethod();
}
final class Car implements ICar
{
    public function carMethod() { echo __CLASS__.'.'.__FUNCTION__.'<br>'; }
}
final class Car2 implements ICar
{
    public function carMethod() { echo __CLASS__.'.'.__FUNCTION__.'<br>'; }
}

interface IVehicle
{
    public function vehicleMethod();
}
final class Vehicle implements IVehicle
{
    public function vehicleMethod() { echo __CLASS__.'.'.__FUNCTION__.'<br>'; }
}
final class Vehicle2 implements IVehicle
{
    public function vehicleMethod() { echo __CLASS__.'.'.__FUNCTION__.'<br>'; }
}

final class Factory
{
    public function createNewCar() : ICar
    {
        return new Car();
    }

    public function createNewVehicle() : IVehicle
    {
        return new Vehicle();
    }
}

but creating/updating those interfaces and adding "implement" keyword is tedious...

What are you trying to do? The goal of the factory pattern is to decouple the construction of the object from its use. The idea is that you will use multiple object in the same way without regard of the actual implementation. Your example is not very good and in that case, you are right, it does not make any sense to use the factory pattern.

Let's suppose instead that Car inherit from Vehicle, and that Plane also inherit from Vehicle, which is an interface that have a method move(distance). Both have a method having int move(distance) as signature. The int is the number of minutes to travel the given distance.

It turns out that in your system, the choice between taking a Car or a Plane is determined by the money you spend when starting the system. If you spend more than 1000 euros, then you will have a plane as vehicle, else you will have a car. As a user, you will travel using the move function of your vehicle, and as such all the logic describing your travel should not depend on the implementation of the vehicle interface. Moreover, as the price of a vehicle might vary in the future (1200 euros at the next run for instance), you do not want the User class to know how the price market is calculated. All you want to do is throw away your money on the market and get the best vehicle that you can get. In this case, we will apply the factory pattern, and the factory will probably be called something like VehicleMarket.

表示为此系统选择的实现的 UML 图

In this example, the user, which does not need to know if he has a car or a plane, is decoupled from those classes. However, the user is still coupled on the VehicleMarket and the Vehicle. This makes perfect sense, because if the functions of the VehicleMarket change, then there is high probability the user also need to change the way it interacts with this class. Same for vehicle, if the contract given by the move function change, then the user will probably have to use the vehicles differently.

The goal is not to decouple every part of your system with each other. The goal is to have the right boundaries between your modules so that you do not have to change the whole system when you will have to implement new features. This is what is meant by coherence. For instance, if the previous system must be extended to add a new vehicle, such as a truck, only the VehicleMarket will have to change.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM