工厂方法有个问题是每个工厂只生产一个产品;
导致存在大量的工厂;
类比我们现实中的工厂;
生产牙刷造了一个牙刷厂;
生成鞋刷又造了一个鞋刷厂;
但是其实牙刷和鞋刷都属于刷子;
这就是产品族的概念了;
它们同属于一个产品族;
我们只造一个刷子厂同时生产一个产品族下的牙刷和鞋刷即可;
要实例话的对象充满不确定性可能会改变的时候;
要创建的对象的数目和类型是未知的;
多个 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
本文为白俊遥原创文章,转载无需和我联系,但请注明来自白俊遥博客https://baijunyao.com 欢迎捐赠赞赏加入组织创建QQ群及捐赠渠道
WalkreHuang :博主的语言通俗易懂,支持一下!!
2018-10-15 20:35:15 回复
向阳而生 :博主,考虑加个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 回复
PJPCCC :博主你好呀,请问下统计网站下所有文章的总点击数,思路是什么?怎么加到一起呢?
2018-08-07 12:13:03 回复
丫丫 :redis的zscore可以很简单的实现,访问就加1,总点击数就是所有文章的点击数加起来,方法有很多种。
2018-08-09 00:29:37 回复
永威 :兄弟们,抢沙发罗。
2018-08-06 16:37:17 回复
最新评论