决定工厂中的 Class 到 Return

Deciding Which Class to Return in Factory

我正在创建一个 Product 摘要 class,它应该是 subclass根据产品类别分类(每个类别都有 不同的内部逻辑但相同的界面,因此我选择了 抽象模型)。但是,我希望用户调用工厂方法 它自动决定什么 subclass 到 return。首先是坏了吗 练习在抽象 class 本身中包含此工厂方法?

abstract class Product
{
    abstract public function getDescription();
    abstract public function getValue();

    public static function
    createProduct($category)
    {
        return # ...
    }
}

我如何决定 class 到 return 的哪个子项?我当然可以做一个 switch-case:

switch($category) {
    case PROD_BOOK:
        return new Book();
        break;
    case PROD_FOOD:
        return new Food();
        break;
}

但是这种硬编码 classes。添加新产品时,我会 必须为类别添加新常量并修改工厂 逻辑。我想保持工厂逻辑不变。有什么办法 将 table 类别键和 class 引用作为值?所以,我可以 做类似的事情:

return new ($classes[$category])();

然后我只需要在添加新的时候更新$classes数组 产品。

看看 Zend Framework 工厂接口:

  interface FactoryInterface
  {
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null);
  }

其中 $container 具有调用其他注册实体的逻辑,requestedName 是从实体管理器请求实体时使用的名称,以及在需要自定义构建逻辑时使用的可选数组选项。

然后在 factory 内部你做一些类似的事情

public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
  /** all parameters initialisation goes here **/
  $result = new $requestedName(..$params);
  return $result;
}

并通过

获取对象实例
$myAwesomeFactory = new MyAwesomeFactory();
$book = $myAwesomeFactory($container, Book::class);
$food = $myAwesomeFactory($container, Food::class);

你应该有一个想法:)

如何实施的示例:

namespace Whatever;

use Any\Other\Book;
use Namespace\Food;

abstract class Product
{
    protected static $categories = [
        'book' => Book::class;
        'food' => Food::class;
    ];

    abstract public function getDescription();
    abstract public function getValue();

    public static function createProduct($category)
    {
        if (!isset(self::$categories[$category])) {
            throw new \InvalidArgumentException("There is no such thing as $category.");
        }
        return new self::$categories[$category];
    }
}

这是一个已经回答过的旧线程,但是当我在寻找类似的东西时,我 post 我找到了解决方案:对于这样一个抽象的 class 工厂方法,也可以使用 get_called_class() 这将 return 调用 createProduct() 的子 class 的名称。

<?php
namespace Whatever;

abstract class Product
{   
    public static function createProduct() {
        $class = get_called_class();
        /** @var  $inst Product */
        $inst = new $class();
        return $inst;
    }
}

class Book extends Product
{
}

class Food extends Product
{   
}

$product = Book::createProduct();
var_dump($product);

使用这种方法,在抽象基础 class 中不需要子 classes 的映射。