【问题标题】:PHP Factory design pattern method clarificationPHP工厂设计模式方法说明
【发布时间】:2023-04-02 07:14:01
【问题描述】:

我想知道this tutorial 是否在 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"

根据Gang of Four的一本书“设计模式”,工厂模式的适用性是

  • 一个类无法预测它必须创建的对象类
  • 类希望其子类指定它创建的对象
  • 类将责任委托给几个助手子类之一,并且您希望本地化有关哪个助手子类是委托的知识

第一点,这个例子其实知道要创建什么类的对象,也就是Automobile,不是吗?

第二点,没有子类。 Automobile 类不继承自 AutomobileFactory。我认为汽车工厂应该至少有一个由汽车实现的功能,它处理对象的创建。

有人可以澄清一下吗?刚开始学习设计模式,每次遇到和别人不一样的教程,都让我很困惑。

【问题讨论】:

    标签: php design-patterns factory factory-pattern


    【解决方案1】:

    我非常同意维基百科的说法

    • 对象的创建会阻止其重用,而无需大量重复代码。
    • 创建对象需要访问不应包含在组合类中的信息或资源
    • 生成对象的生命周期管理必须集中进行,以确保应用程序内的行为一致。

    我创建工厂的主要原因是我强调的这个。

    例如,让我们想象一个现实世界的工厂,在全国有许多工厂。这家工厂生产。门需要旋钮。由于物流原因,工厂的每个工厂都有自己的旋钮供应商,另一个完全不同的工厂。

    这家工厂的生产经理软件会根据一些标准来选择哪个工厂会生产很多门,但它不需要知道旋钮从哪里来。被选中的工厂将要求其自己的供应商为生产的门提供一个把手。

    但是,对于客户来说,门是哪个工厂做的并不重要,他只关心有他的门。

    让我们把它写在代码上:

    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 对拥有多个工厂的工厂一无所知。

    【讨论】:

    • 现在想象一下,工厂不仅生产门,还生产需要玻璃的窗户。每个工厂都有自己的玻璃供应商,但同样,客户或经理都不需要知道这一点。
    • 非常感谢您的详细回答。我理解您代码中关注点的明确分离,我与您同在。但是,我有点困惑。哪个类使用严格的传统工厂方法模式? DoorFactory 类似乎在使用工厂模式,因为它将 Door 对象的创建推迟到其子类(SaoPauloDoorFactoryNYDoorFactory)。但我看不出DoorFactoryKnobSupplier 在设计模式方面有任何关系。
    • 它们之间没有任何关系。想象一下还有另一种植物也在门旁边制造旋钮。它不需要供应商。在此示例中,所有工厂都返回相同类型的门。要查看严格的传统工厂方法,您应该只看到一对工厂/产品。在这种情况下,所有的工厂都会创建相同类型的对象,但它不需要是这样的。
    【解决方案2】:

    我不太喜欢这个教程。正如您在有关工厂的 WikiPedia 页面中看到的 (https://en.wikipedia.org/wiki/Factory_pattern) - 通常以不同的方式完成。 WikiPedia 示例确实符合您提到的规则。查看那里的 PHP 部分。

    【讨论】:

    • 这几乎是同一个例子:)
    • 我不同意——除了他们都生产汽车这一事实。让我们看看您的列表,然后看看 Wiki 示例。 1. 一个类无法预测它必须创建的对象类——对于 CarFactory 来说是这样——不知道它将产生什么。 2. 一个类希望它的子类指定它创建的对象——没错,看看 SedanFactory
    • 但它知道,它是Sedan。下面还有一个例子(封装),确实不一样
    • 但是 CarFactory 没有 - 该列表是否适用于此类?
    • 它不是一个真正的类,它是一个接口。当然你可以说它适用,但实现它的类仍然违反了 OP 提到的规则。就个人而言,我不认为这些规则是有效的。工厂应该简单地创建对象,就是这样
    【解决方案3】:

    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 类根本不处理任何对象的创建。如果我错了,请纠正我。
    【解决方案4】:

    实际上有一个工厂方法设计模式遵循最初的四个目录。抽象工厂完全不同,并且基于不同的结构假设。简单工厂不是一种设计模式,而是弗里曼所说的“编程习惯”。 Factory方法包括一个抽象的Creator和Product,Client一般通过Creator进行请求。具体的工厂位于 ConcreteCreator(s) 中,具体产品是 Product 类的子类,由具体创建者实例化。完整而简单的 PHP 示例请参见http://www.php5dp.com/a-simple-php-design-pattern-the-factory-method/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-11
      相关资源
      最近更新 更多