实例讲解PHP设计模式编程中的简单工厂模式


Posted in PHP onFebruary 29, 2016

简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出那一种产品类的实例。

1.工厂模式的几种形态
工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,不必事先知道每次要实例化哪一个类。工厂模式有以下几种形态:
(1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern)。
(2)工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式;
(3)抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。下面就是简单工厂模式的简略类图。

简单工厂模式,或称静态工厂方法模式,是不同的工厂方法模式的一个特殊实现。在其他文献中,简单工厂往往作为普通工厂模式的一个特例讨论。
学习简单工厂模式是对学习工厂方法模式的一个很好的准备,也是对学习其他模式,特别是单例模式和多例模式的一个很好的准备。

2 .简单工厂模式的引进

比如说有一个农场公司,专门向市场销售各类水果。在这个系统里需要描述下列的水果:
葡萄 Grape
草莓 Strawberry
苹果 Apple
水果与其他的植物有很大的不同,就是水果最终是可以采摘食用的。那么一个自然的作法就是建立一个各种水果都适用的接口,以便与农场里的其他植物区分开。如下图所示。

水果接口规定出所有的水果必须实现的接口,包括任何水果类必须具备的方法:种植plant(),生长grow()以及收获harvest()。接口Fruit 的类图如下所示。

这个水果接口的源代码如下所示。
代码清单1:接口Fruit 的源代码

interface Fruit
{
public function grow();
public function harvest();
public function plant();
}

Apple 类是水果类的一种,因此它实现了水果接口所声明的所有方法。另外,由于苹果是多年生植物,因此多出一个treeAge 性质,描述苹果树的树龄。下面是这个苹果类的源代码。
代码清单2:类Apple 的源代码

class Apple implements Fruit
{
private $_treeAge;
public function grow()
{
echo "Apple is growing.";
}
public function harvest()
{
echo "Apple has been harvested.";
}
public function plant()
{
echo "Apple has been planted.";
}
public function getTreeAge()
{
return $this->_treeAge;
}
public function setTreeAge($treeAge)
{
$this->_treeAge = (int) $treeAge;
}
}

同样,Grape 类是水果类的一种,也实现了Fruit 接口所声明的所有的方法。但由于葡萄分有籽和无籽两种,因此,比通常的水果多出一个seedless 性质,如下图所示。

葡萄类的源代码如下所示。可以看出,Grape 类同样实现了水果接口,从而是水果类型的一种子类型。
代码清单3:类Grape 的源代码

class Grape implements Fruit
{
private $seedless;
public function grow()
{
echo "Grape is growing.";
}
public function harvest()
{
echo "Grape has been harvested.";
}
public function plant()
{
echo "Grape has been planted.";
}
public function getSeedless()
{
return $this->seedless;
}
public function setSeedless($seedless)
{
$this->seedless = (boolean) $seedless;
}
}

Strawberry 类实现了Fruit 接口,因此,也是水果类型的子类型,其源代码如下所示。
代码清单4:类Strawberry 的源代码

class Strawberry implements Fruit
{
public function grow()
{
echo "Strawberry is growing.";
}
public function harvest()
{
echo "Strawberry has been harvested.";
}
public function plant()
{
echo "Strawberry has been planted.";
}
}

农场的园丁也是系统的一部分,自然要由一个合适的类来代表。这个类就是FruitGardener 类,其结构由下面的类图描述。

FruitGardener 类会根据客户端的要求,创建出不同的水果对象,比如苹果(Apple),葡萄(Grape)或草莓(Strawberry)的实例。而如果接到不合法的要求,FruitGardener 类会抛出BadFruitException 异常。
园丁类的源代码如下所示。
代码清单5:FruitGardener 类的源代码

class FruitGardener
{
public static function factory($which)
{
$which = strtolower($which);
if ($which == 'apple') {
return new Apple();
} elseif ($which == 'strawberry') {
return new Strawberry();
} elseif ($which == 'grape') {
return new Grape();
} else {
throw new BadFruitException('Bad fruit request');
}
}
}

可以看出,园丁类提供了一个静态工厂方法。在客户端的调用下,这个方法创建客户端所需要的水果对象。如果客户端的请求是系统所不支持的,工厂方法就会抛出一个BadFruitException 异常。这个异常类的源代码如下所示。
代码清单6:BadFruitException 类的源代码

class BadFruitException extends Exception
{
}

在使用时,客户端只需调用FruitGardener 的静态方法factory()即可。请见下面的示意
性客户端源代码。
代码清单7:怎样使用异常类BadFruitException

try {
FruitGardener::factory('apple');
FruitGardener::factory('grape');
FruitGardener::factory('strawberry');
//...
} catch (BadFruitException $e) {
//...
}

这样,农场一定会百果丰收啦!

3.使用简单工厂模式设计一个“面向对象的”计算器

/**
 * 面向对象计算器
 * 思路:
 * 1、面向对象的基本,封装、继承、多太
 * 2、父类公用类
 * 3、各种运算类
 */
 
/**
 * 基类,运算类
 * 只提供基本数据,不参与运算
 */
 
class Operation {
  
 // 第一个数
 public $first_num = 0;
  
 // 第二个数
 public $second_num = 0;
  
 /**
  * 获取结果,其他类覆盖此方法
  * @return double $result
  */
 public function getResult() {
  $result = 0.00;
   
  return $result;
 }
}
 
/**
 * 加法类
 */
class OperationAdd extends Operation {
 /**
  * 覆盖父类,实现加法算法
  */
 public function getResult() {
  $result = 0;
  return $this->first_num + $this->second_num;
 }
}
 
/**
 * 减法类
 *
 */
class OperationSub extends Operation {
 /**
  * 覆盖父类,实现加法算法
  */
 public function getResult() {
  $result = 0;
  return $this->first_num - $this->second_num;
 }
}
 
/**
 * 乘法类
 *
 */
class OperationMul extends Operation {
 /**
  * 覆盖父类,实现加法算法
  */
 public function getResult() {
  $result = 0;
  return $this->first_num * $this->second_num;
 }
}
 
/**
 * 除类
 *
 */
class OperationDiv extends Operation {
 /**
  * 覆盖父类,实现加法算法
  */
 public function getResult() {
  $result = 0;
   
  if ($this->second_num == 0) {
   throw new Exception('除法操作第二个参数不能为零!');
   return 0;
  }
   
  return $this->first_num / $this->second_num;
 }
}
 
/**
 * 工厂类
 */
class OperationFactory {
 /**
  * 工厂函数
  * @param string $operation
  * @return object
  */
 public function createOperation($operation) {
  $oper = null;
   
  switch($operation) {
   case '+':
    $oper = new OperationAdd();
    break;
   case '-':
    $oper = new OperationSub();
    break;
   case '*':
    $oper = new OperationMul();
    break;
   case '/':
    $oper = new OperationDiv();
    break;
   default:
    return 0;
  }
  return $oper;
 }
}
 
 
$operation = new OperationFactory();
$oper = $operation->createOperation('/');
 
$oper->first_num = 10;
$oper->second_num = 20;
var_dump($oper->getResult());

实例讲解PHP设计模式编程中的简单工厂模式

PHP 相关文章推荐
PHP截取中文字符串的问题
Jul 12 PHP
基于mysql的论坛(2)
Oct 09 PHP
mysql 全文搜索 技巧
Apr 27 PHP
php中计算中文字符串长度、截取中文字符串的函数代码
Aug 09 PHP
php删除文件夹及其文件夹下所有文件的函数代码
Jan 23 PHP
php用header函数实现301跳转代码实例
Nov 25 PHP
PHP实现利用MySQL保存session的方法
Aug 23 PHP
php中base64_decode与base64_encode加密解密函数实例
Nov 24 PHP
PHP中数组转换为SimpleXML教程
Jan 27 PHP
Laravel5框架自定义错误页面配置操作示例
Apr 17 PHP
PHP操作路由器实现方法示例
Apr 27 PHP
PHP操作Redis常用命令的实例详解
Dec 23 PHP
PHP Try-catch 语句使用技巧
Feb 28 #PHP
linux下为php添加iconv模块的方法
Feb 28 #PHP
PHP中模拟链表和链表的基本操作示例
Feb 27 #PHP
PHP使用Memcache时模拟命名空间及缓存失效问题的解决
Feb 27 #PHP
简单谈谈PHP中strlen 函数
Feb 27 #PHP
详解PHP的Laravel框架中Eloquent对象关系映射使用
Feb 26 #PHP
PHP文件缓存smarty模板应用实例分析
Feb 26 #PHP
You might like
Linux中用PHP判断程序运行状态的2个方法
2014/05/04 PHP
ThinkPHP3.1新特性之对页面压缩输出的支持
2014/06/19 PHP
php的XML文件解释类应用实例
2014/09/22 PHP
PHP 输出缓冲控制(Output Control)详解
2016/08/25 PHP
PHP+Redis 消息队列 实现高并发下注册人数统计的实例
2018/01/29 PHP
thinkPHP5框架实现多数据库连接,跨数据连接查询操作示例
2019/05/29 PHP
Javascript的一种模块模式
2008/03/22 Javascript
asp.net下利用js实现返回上一页的实现方法小集
2009/11/24 Javascript
jQuery的三种$()
2009/12/30 Javascript
js修改input的type属性问题探讨
2013/10/12 Javascript
jquery判断小数点两位和自动删除小数两位后的数字
2014/03/19 Javascript
jquery日历插件datepicker用法分析
2016/01/22 Javascript
JavaScript判断表单为空及获取焦点的方法
2016/02/12 Javascript
js判断输入字符串是否为空、空格、null的方法总结
2016/06/14 Javascript
JS实现简单的tab切换选项卡效果
2016/09/21 Javascript
微信小程序 location API接口详解及实例代码
2016/10/12 Javascript
jQuery基于Ajax方式提交表单功能示例
2017/02/10 Javascript
vue使用技巧及vue项目中遇到的问题
2018/06/04 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
回顾Javascript React基础
2019/06/15 Javascript
js实现直播点击飘心效果
2020/08/19 Javascript
vue中利用three.js实现全景图的完整示例
2020/12/07 Vue.js
python记录程序运行时间的三种方法
2017/07/14 Python
NumPy 基本切片和索引的具体使用方法
2019/04/24 Python
Python获取时间范围内日期列表和周列表的函数
2019/08/05 Python
python实现图像拼接功能
2020/03/23 Python
python将数据插入数据库的代码分享
2020/08/16 Python
工作中的自我评价如何写好
2013/10/28 职场文书
数控技校生自我鉴定
2014/03/02 职场文书
自考毕业自我鉴定
2014/03/18 职场文书
住房抵押登记委托书
2014/09/27 职场文书
2015年世界环境日活动总结
2015/02/11 职场文书
全国法制宣传日活动总结
2015/05/05 职场文书
观看《信仰》心得体会
2016/01/15 职场文书
Windows11性能真的上涨35%? 桌面酷睿i9实测结果公开
2021/11/21 数码科技
动画「半妖的夜叉姬」新BD特典图公开
2022/03/22 日漫