变种的数据中心:实现数据的组合返回

PHP , 实战832 字

网上有很多数据模式的文章,这里不会按照已经成型的书和文章来进行编写,这样没有任何意义,在应用设计模式的时候一定要根据自己业务和使用的语言来进行编写。

在工厂模式中,我们基于商城的案例编写了一个书、狗和酒的工厂案例,在线我们对需求进行一次迭代,老板决定每个商品的 getList 方法都要额外加上狗的信息(狗本身除外)。

当我们接到这个需求的时候,首先第一个想法肯定是这样实现:

include 'autoload.php';
//$object = new Books();
$book = ProductFactory::getProduct('book');
$bookList = $book->getList();
$dog = ProductFactory::getProduct('dog');
$dogList = $dog->getList();

接下来,我们利用设计模式来设计一下,不过首先我们需要解决一个问题,目前的代码虽然都有 getList 但是却并不是一个硬性的规范,如果现在商城增加品类了,例如增加了一个 Phone 交给了一个新的开发人员去开发,获取列表的方法它给写成了 getPhones 那么我们对应的 ProductFactory 在实现上可能就会存在很大的麻烦。

对于约束,我们可以先定义个接口:

<?php

interface IProduct
{
    public function getList();
} 

这样无论以后我们新增什么品类都使用这个方法去进行创建,我们目前已有的三个类都去 implements ,接下来我们对工厂模式进行一些改进。

<?php

class ProductFactory
{
    public static function getProduct($type)
    {
        switch ($type) {
            case "book" :
                $obj = new Books();
                break;
            case "dog" :
                $obj = new Dogs();
                break;
            case "wine" :
                $obj = new Wines();
                break;
            default:
                $obj = null;
        }
        if (is_subclass_of($obj, "IProduct")) {
            return $obj;
        }
        return null;
    }
}

我们在最后增加了 is_subclass_of 判断,该函数可以判断当前的变量是否是某一个类、抽象类、接口的子类,这样可以让我们的工厂更加健全,避免返回的对象不满足 IProduct 的约束,可以减少运行时的报错,因为你无法保证其他人修改工厂方法的时候放进来的类到底满不满足我们的业务要求。

如果要实现我们现在的需求,需要用到注册树模式,我们在这里叫它数据中你想你模式,我们把数据全部都放到数据中心中进行统一的管理,取数据的时候也从数据中心取,坚决不合类本身私自交往。

<?php

class ProductDataCenter
{
    public static $objectList = [];

    public static function set($k, $v)
    {
        self::$objectList[$k] = $v;
    }

    public static function get($key)
    {
        return self::$objectList[$key] ?? [];
    }
}

ProductDataCenter 的职责非常简单,就是存储我们的产品列表,对外提供两个方法:

  • set 存值

接下来我们继续改造工厂方法,工厂不再继续返回值,而是向 ProductDataCenter 中增加数据。

<?php

class ProductFactory
{
    public static function getProduct($type)
    {
        switch ($type) {
            case "book" :
                $obj = new Books();
                break;
            case "dog" :
                $obj = new Dogs();
                break;
            case "wine" :
                $obj = new Wines();
                break;
            default:
                $obj = null;
        }
        if (is_subclass_of($obj, "IProduct")) {
            // 把创建的对象放到数据中心
            ProductDataCenter::set($type, $obj);
        }
    }
}

这样一来,我们在外面调用的时候就变成了这个样子:

<?php

include 'autoload.php';
//$object = new Books();
ProductFactory::getProduct('book');

$data = ProductDataCenter::get;
var_dump($data);

但是目前还是没有解决我们的需求,我们还需要继续修改数据中心。

<?php

class ProductDataCenter
{
    public static $objectList = [];

    public static function set($k, $v)
    {
        self::$objectList[$k] = $v;
    }

    public static function __callStatic($name, $arguments)
    {
        $result = [];
        foreach (self::$objectList as $k => $v) {
            if (method_exists($v, $name)) {
                $ret = $v->$name($arguments);

                if ($ret) {
                    foreach ($ret as $item) {
                        $result[] = $item;
                    }

                }
            }
        }
        if (count($result) > 0) {
            return $result;
        }
    }
}

在这里,我们增加了一个 __callStatic 魔术方法,这个魔术方法的作用是,当调用静态方法不存在的时候就会走到这个函数。

其主要作用就是从注册树种取出 Proudct 类,然后执行 getList 方法,并且将在注册树中存在的 Product 都执行一遍,组成成我们想要的数据返回回去。

现在如果我们想要展示 Book 和 Dog 的数据只需要这样获取即可:

<?php

include 'autoload.php';
//$object = new Books();
ProductFactory::getProduct('book');
ProductFactory::getProduct('dog');
$data = ProductDataCenter::getList();
var_export($data);

## 执行结果如下:
array (
  0 => 
  array (
    'product_id' => 101,
    'product_name' => 'java 从入门到精通',
  ),
  1 => 
  array (
    'product_id' => 102,
    'product_name' => 'PHP 从入门到精通',
  ),
  2 => 
  array (
    'product_id' => 201,
    'product_name' => '泰迪',
  ),
  3 => 
  array (
    'product_id' => 202,
    'product_name' => '金毛',
  ),
)%

既然是注册树模式,就还需要有从树上摘除的操作:

public static function remove($key)
{
        unset(self::$objectList[$key]);
}

这一节就结束了。

maksim
Maksim(一笑,吡罗),PHPer,Goper
OωO
开启隐私评论,您的评论仅作者和评论双方可见