php设计模式(五)抽象工厂模式

工厂方法有个问题是每个工厂只生产一个产品;
导致存在大量的工厂;
类比我们现实中的工厂;
生产牙刷造了一个牙刷厂;
生成鞋刷又造了一个鞋刷厂;
但是其实牙刷和鞋刷都属于刷子;
这就是产品族的概念了;
它们同属于一个产品族;
我们只造一个刷子厂同时生产一个产品族下的牙刷和鞋刷即可;

应用场景

要实例话的对象充满不确定性可能会改变的时候;
要创建的对象的数目和类型是未知的;

结构

多个 interface 或者 abstract 产品父类;
多个实现 interface 或者继承 abstract 的具体产品类;

1个 interface 或者 abstract 工厂父类;
1个实现 interface 或者继承 abstract 的具体工厂类;

具体的工厂类里面有多个方法分别实例化具体的产品类;

示例

先创建产品和工厂 interface ;
User.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * User 产品接口
 *
 * Interface User
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
interface User
{
    /**
     * 新增
     *
     * @return mixed
     */
    public function insert();

    /**
     * 查询
     *
     * @return mixed
     */
    public function select();
}

Factory.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * 工厂接口
 *
 * Interface Factory
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
interface Factory
{
    /**
     * 创建 User 产品
     *
     * @return mixed
     */
    public function createUser();
}

然后分别创建对应的 MySQL 产品和工厂;
MySQLUser.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * 应用于 MySQL 的 User
 *
 * Class MySQLUser
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class MySQLUser implements User
{
    /**
     * 新增
     */
    public function insert()
    {
        echo '向 MySQL 数据库中插入 User';
    }

    /**
     * 查询
     */
    public function select()
    {
        echo '从 MySQL 数据库中查询 User';
    }
}

MySQLFactory.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * MySQL 工厂
 *
 * Class MySQLFactory
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class MySQLFactory implements Factory
{
    /**
     * 创建 MySQLUser 产品
     *
     * @return MySQLUser|mixed
     */
    public function createUser()
    {
        return new MySQLUser();
    }
}

运行
index.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

require __DIR__.'/../vendor/autoload.php';

/**
 * 客户端
 *
 * Class Client
 * @package Baijunyao\DesignPatterns\FactoryMethod
 */
class Client
{
    /**
     * 运行
     */
    public function run()
    {
        // 使用 MySQL
        $factory = new MySQLFactory();
        $user = $factory->createUser();
        $user->insert();
        echo '<br>';
        $user->select();
    }
}

$client = new Client();
$client->run();

当我们只有一个产品的时候和工厂方法看起来没啥区别;
我们需要再增加一个产品组成产品族;
Article.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * Article 产品接口
 *
 * Interface Article
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
interface Article
{
    /**
     * 新增
     *
     * @return mixed
     */
    public function insert();

    /**
     * 查询
     *
     * @return mixed
     */
    public function select();
}

具体产品;
MySQLArticle.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * 应用于 MySQL 的 Article
 *
 * Class MySQLArticle
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class MySQLArticle implements Article
{
    /**
     * 新增
     */
    public function insert()
    {
        echo '向 MySQL 数据库中插入 Article';
    }

    /**
     * 查询
     */
    public function select()
    {
        echo '从 MySQL 数据库中查询 Article';
    }
}

产品中现在我们有 User 和 Article 两个接口;
分别有 MySQLUser 和 MySQLArticle 两个具体产品类;
工厂中现在我们有 Factory 接口;
有 MySQLFactory 具体工厂类;
MySQLFactory 类中实现了实例化 MySQLUser 的方法;

工厂方法和抽象工厂之间一个巨大的差别就在于如何实例化 MySQLArticle 产品了;
如果我们再建一个工厂专门用来实例化 MySQLArticle 那就成了上篇文章的工厂方法了;
而我们的抽象工厂则是在 MySQLFactory 增加一个实例化 MySQLArticle 的方法;

先增加 interface 中的 createArticle 方法;
Factory.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * 工厂接口
 *
 * Interface Factory
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
interface Factory
{
    /**
     * 创建 User 产品
     *
     * @return mixed
     */
    public function createUser();

    /**
     * 创建 Article 产品
     *
     * @return mixed
     */
    public function createArticle();
}

再增加具体工厂中的方法;
MySQLFactory.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * MySQL 工厂
 *
 * Class MySQLFactory
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class MySQLFactory implements Factory
{
    /**
     * 创建 MySQLUser 产品
     *
     * @return MySQLUser|mixed
     */
    public function createUser()
    {
        return new MySQLUser();
    }

    /**
     * 创建 MySQLArticle 产品
     *
     * @return MySQLArticle|mixed
     */
    public function createArticle()
    {
        return new MySQLArticle();
    }

}

运行
index.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

require __DIR__.'/../vendor/autoload.php';

/**
 * 客户端
 *
 * Class Client
 * @package Baijunyao\DesignPatterns\FactoryMethod
 */
class Client
{
    /**
     * 运行
     */
    public function run()
    {
        // 使用 MySQL
        $factory = new MySQLFactory();
        // 创建 user
        $user = $factory->createUser();
        $user->insert();
        echo '<br>';
        $user->select();

        echo '<hr>';

        $factory = new MySQLFactory();
        // 创建 article
        $article = $factory->createArticle();
        $article->insert();
        echo '<br>';
        $article->select();

    }
}

$client = new Client();
$client->run();

现在我们的系统已经支持 MySQL 了;
如果想支持 SQLite;
首先需要创建 SQLiteFactory ;
SQLiteFactory.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * SQLite 工厂
 *
 * Class SQLiteFactory
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class SQLiteFactory implements Factory
{
    /**
     * 创建 SQLiteUser 产品
     *
     * @return SQLiteUser|mixed
     */
    public function createUser()
    {
        return new SQLiteUser();
    }

    /**
     * 创建 SQLiteArticle 产品
     *
     * @return SQLiteArticle|mixed
     */
    public function createArticle()
    {
        return new SQLiteArticle();
    }
}

分别创建 SQLiteUser 和 SQLiteArticle 产品;
SQLiteUser.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * 适用于 SQLite 的 User
 *
 * Class SQLiteUser
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class SQLiteUser implements User
{
    /**
     * 新增
     */
    public function insert()
    {
        echo '向SQLite数据库中插入 User';
    }

    /**
     * 查询
     */
    public function select()
    {
        echo '从SQLite数据库中查询 User';
    }
}

SQLiteArticle.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

/**
 * 适用于 SQLite 的 Article
 *
 * Class SQLiteArticle
 * @package Baijunyao\DesignPatterns\AbstractFactory
 */
class SQLiteArticle implements Article
{
    /**
     * 新增
     *
     * @return mixed|void
     */
    public function insert()
    {
        echo '向 SQLite 数据库中插入 Article';
    }

    /**
     * 查询
     *
     * @return mixed|void
     */
    public function select()
    {
        echo '从 SQLite 数据库中查询 Article';
    }
}

运行;
index.php

<?php

namespace Baijunyao\DesignPatterns\AbstractFactory;

require __DIR__.'/../vendor/autoload.php';

/**
 * 客户端
 *
 * Class Client
 * @package Baijunyao\DesignPatterns\FactoryMethod
 */
class Client
{
    /**
     * 运行
     */
    public function run()
    {
        // 使用 MySQL
        $factory = new MySQLFactory();
        // 创建 user
        $user = $factory->createUser();
        $user->insert();
        echo '<br>';
        $user->select();

        echo '<hr>';

        $factory = new MySQLFactory();
        // 创建 article
        $article = $factory->createArticle();
        $article->insert();
        echo '<br>';
        $article->select();

        echo '<br><br>';

        // 使用 SQLite
        $factory = new SQLiteFactory();
        // 创建 user
        $user = $factory->createUser();
        $user->insert();
        echo '<br>';
        $user->select();

        echo '<hr>';

        $factory = new SQLiteFactory();
        // 创建 article
        $article = $factory->createArticle();
        $article->insert();
        echo '<br>';
        $article->select();
    }
}

$client = new Client();
$client->run();

抽象工厂的优点是产品跟客户端完全分离;
我们在 Client 中只调用了 Factory;
甚至连产品的类名都不需要知道;
但是缺点也很明显;
那如果要增加一个 category 表;
那我们需要增加 Category接口类、MySQLCategory、SQLiteCategory;
还需要在 Factory、MySQLFactory、SQLiteFactory 中增加 createCategory 方法;
分别新增3个文件和改动3个文件才行;
对于聪(lan)明(duo)的程序猿来说;
这想想都是一种会呼吸的痛;
之前文章一直都在说设计模式之前是可以相互配合的;
下篇文章我们就用简单工厂配合抽象工厂来减轻这种痛苦;

github示例:https://github.com/baijunyao/design-patterns/tree/master/AbstractFactory

白俊遥博客
请先登录后发表评论
  • 最新评论
  • 总共4条评论
白俊遥博客

向阳而生:博主,考虑加个RSS订阅不?

2018-08-10 14:06:23 回复

白俊遥博客
  • 云淡风晴 回复 向阳而生:有的: https://baijunyao.com/feed 
  • 2018-08-12 23:13:48 回复
白俊遥博客

丫丫:博主,你的html代码显示格式用的是什么js插件呢?

2018-08-09 00:22:56 回复

白俊遥博客
  • 云淡风晴 回复 丫丫:https://prismjs.com  
  • 2018-08-12 23:16:19 回复
白俊遥博客

daji:博主你好呀,请问下统计网站下所有文章的总点击数,思路是什么?怎么加到一起呢?

2018-08-07 12:13:03 回复

白俊遥博客
  • 丫丫 回复 daji:redis的zscore可以很简单的实现,访问就加1,总点击数就是所有文章的点击数加起来,方法有很多种。
  • 2018-08-09 00:29:37 回复
白俊遥博客

永威:兄弟们,抢沙发罗。

2018-08-06 16:37:17 回复