PHP 工厂设计模式方法澄清

4

我想知道这个教程是否正确地在PHP中实现了工厂设计模式。以下是实际的源代码。

<?php
class Automobile
{
    private $vehicle_make;
    private $vehicle_model;

    public function __construct($make, $model)
    {
        $this->vehicle_make = $make;
        $this->vehicle_model = $model;
    }

    public function get_make_and_model()
    {
        return $this->vehicle_make . ' ' . $this->vehicle_model;
    }
}

class AutomobileFactory
{
    public static function create($make, $model)
    {
        return new Automobile($make, $model);
    }
}

// have the factory create the Automobile object
$veyron = AutomobileFactory::create('Bugatti', 'Veyron');

print_r($veyron->get_make_and_model()); // outputs "Bugatti Veyron"

根据《设计模式》一书,工厂模式的适用性包括:
  • 一个类无法预知它必须创建哪些对象
  • 一个类希望由其子类来指定创建的对象
  • 类将责任委托给多个辅助子类之一,并且您希望本地化哪个辅助子类是代理的知识
第一点,这个例子实际上知道要创建什么类的对象,即Automobile,不是吗?
第二点,没有子类。Automobile类不继承自AutomobileFactory。我认为AutomobileFactory应该至少有一个由Automobile实现的函数,处理对象创建。
有人能澄清一下吗?我刚开始学习设计模式,每次遇到与其他教程不同的教程,都会让我感到困惑。
4个回答

3

我非常赞同维基百科中提到的内容

  • 创建对象将导致在没有重复大量代码的情况下无法再次使用。
  • 创建对象需要访问不应包含在组合类中的信息或资源
  • 生成的对象的生命周期管理必须集中以确保应用程序内的一致行为。

我创建工厂的主要原因是上述黄色部分。

例如,假设有一个真实的工厂,遍布全国许多工厂。该工厂生产。门需要把手。出于物流原因,该工厂的每个工厂都有自己的把手供应商,这是另一个完全不同的工厂。

该工厂的生产经理软件将根据某些标准选择哪个工厂将大量生产门,但是它不需要知道把手来自哪里。所选工厂将为其制造的门请求其自己的供应商提供把手。

但是,对于客户而言,由哪个工厂制造门并不重要,他只关心拥有自己的门。

现在将其转化为代码:

class Knob {
    // something...
}

interface KnobSupplier {
    public function makeKnob();
}

class SaoPauloKnobSupplier {
    public function makeKnob() {
        return new Knob('Knob made in São Paulo');
    }
}

class NewYorkKnobSupplier {
    public function makeKnob() {
        return new Knob('Knob made in New York');
    }
}

class Door {
    public function __construct(Knob $knob) {
        // something...
    }
}

interface DoorFactory {
    public function makeDoor();
}

class SaoPauloDoorFactory {
    private $knobSupplier;

    public function __construct() {
        $this->knobSupplier = new SaoPauloKnobSupplier();
    }

    public function makeDoor() {
        return new Door($this->knobSupplier->makeKnob(), "Door made in São Paulo");
    }
}

class NewYorkDoorFactory {
    private $knobSupplier;

    public function __construct() {
        $this->knobSupplier = new NewYorkKnobSupplier();
    }

    public function makeDoor() {
        return new Door($this->knobSupplier->makeKnob(), "Door made in New York");
    }
}

class ProductionManager {
    private $plants = array();
    // methods for adding plants, etc...
    public function getDoor() {
        // Somehow decides which plant will create the door.
        return $plant->makeDoor();
    }
}

class Client {
    public function getMyDoor(ProductionManager $manager) {
        return $manager->getDoor();
    }
}

使用以下代码:

$manager = new ProductManager();
$manager->addPlant(new SaoPauloDoorFactory());
$manager->addPlant(new NewYorkDoorFactory());

$client  = new Client();

var_dump($client->getMyDoor($manager));

ProductManager并不知道旋钮的任何信息,Client也不知道工厂有多个工厂。


1
现在想象一下,工厂不仅制造门,还要制造需要玻璃的窗户。每个工厂都有自己的玻璃供应商,但是客户或经理都不需要知道这一点。 - Henrique Barcelos
非常感谢您详细的回答。我理解您代码中的职责明确分离,我赞同您的观点。但是,我有些困惑。哪个类严格使用传统的工厂方法模式?DoorFactory 类似乎使用了工厂模式,因为它将 Door 对象的创建推迟到其子类(SaoPauloDoorFactoryNYDoorFactory)中。但是我没有看到 DoorFactoryKnobSupplier 之间在设计模式方面的任何关系。 - kidonchu
它们之间没有任何关系。想象一下还有另一家工厂也生产门把手。它不需要供应商。在这个例子中,所有工厂都生产相同类型的门。要看严格传统的工厂方法,应该只看一对工厂/产品。在这种情况下,所有工厂都创建相同类型的对象,但不一定非得是这样。 - Henrique Barcelos

1

我支持你,kidonchu。我不认为这个例子是传统的工厂方法模式。

我会像这样写你的例子(伪代码)

<?php

abstract class CarAbstract
{
    protected $_vehicleMake;
    protected $_vehicleModel;

    public function __construct($model)
    {
        $this->_vehicleModel = $model;
    }

    public function getMakeAndModel()
    {
        return $this->_vehicleMake . ' ' . $this->_vehicleModel;
    }
}

class Bugatti extends CarAbstract
{
    public function __construct($model)
    {
        parent::__construct($model);

        $this->_vehicleMake = get_class($this);
    }
}

class AutomobileFactory
{
    public static function getInstance($make, $model)
    {
        if (is_file('Model/Car/' . $make . '.php')){
            require_once 'Model/Car/' . $make . '.php';
            $car = new $make($model);
        }else{
            throw new Exception('Car not found');
        }
    }
}

$veyron = AutomobileFactory::getInstance('Bugatti', 'Veyron');

print_r($veyron->getMakeAndModel()); // outputs "Bugatti Veyron"

感谢您对代码的修改。严格来说,这个修改仍然不是“工厂方法”,是吗?据我所知,至少有三种类型的工厂方法——抽象工厂方法、工厂方法和简单工厂方法。而修改后的代码看起来更像是简单工厂方法,因为CarAbstract类根本不涉及任何对象的创建。如果我错了,请纠正我。 - kidonchu

1
我不是很喜欢这个教程。正如您可以在有关工厂的维基百科页面中看到的那样(https://en.wikipedia.org/wiki/Factory_pattern),通常情况下会有所不同。维基百科的例子符合您提到的规则。请查看其中的PHP部分。

这个例子基本上是一样的 :) - nice ass
我不同意 - 除了它们都生产汽车这个事实。让我们看看你的清单,然后再看维基百科的例子。
  1. 一个类无法预测它必须创建的对象的类 - 对于CarFactory来说是正确的 - 不知道它将要生产什么。
  2. 一个类希望它的子类指定它所创建的对象 - 是的,看看SedanFactory就知道了。
- Jochen van Wylick
但它知道它是“轿车”。下面有另一个例子(封装),它确实是不同的。 - nice ass
但是CarFactory不会 - 这个列表不适用于这个类吗? - Jochen van Wylick
这并不是一个类,而是一个接口。当然你可以说它适用,但实现它的类仍然违反了OP提到的规则。个人认为,那些规则本来就无效。工厂只应该简单地创建对象,就这样。 - nice ass
好的观点。我谷歌了Martin Fowler和工厂模式。他是现代模式大师,你可能知道。我看到了这篇文章http://en.wikipedia.org/wiki/Factory_method_pattern,它展示了一个PHP工厂,不知道它正在创建哪个类。那个怎么样? - Jochen van Wylick

1
实际上,遵循原先的四人帮设计模式目录只有一个工厂方法设计模式。抽象工厂完全不同,是基于不同的结构假设而建立的。简单工厂不是一个设计模式,而是Freemans所说的“编程惯用语”。工厂方法包括一个抽象创建者和产品,客户端通常通过创建者进行请求。具体工厂可以在ConcreteCreator中找到,具体产品是Product类的子类,并由具体创建者实例化。关于PHP的完整而简单的示例,请参见http://www.php5dp.com/a-simple-php-design-pattern-the-factory-method/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接