本文实例讲述了PHP设计模式之工厂模式(Factory)。分享给大家供大家参考,具体如下:

工厂模式的意思其实就是提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤,实际上就是建立一个统一的类实例化的函数接口,完事统一调用,统一控制,它是PHP中常用的一种设计模式,一般会配合单例模式一起使用,来加载php类库中的类。来看一个简单的应用场景:

  1. 我们拥有一个Json类,String类,Xml类。
  2. 如果我们不使用工厂方式实例化这些类,则需要每一个类都需要new一遍,过程不可控,类多了,到处都是new的身影
  3. 引进工厂模式,通过工厂统一创建对象实例。

代码如下:

<?php
//工厂模式 提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤
//字符串类
class String {
 public function write() {}
}
//Json类
class Json {
 public function getJsonData() {}
}
//xml类
class Xml {
 public function buildXml() {}
}
//工厂类
class Factory {
 public static function create($class) {
 return new $class;
 }
}
Factory::create("Json"); //获取Json对象

我们现在应该对于工厂模式有了一个大概的理解了,咱们接下来可以从字面上来理解一下。

工厂么,它就是生产产品的地方,它有原料,设备和产品,那么在PHP中,我们可以理解为,这个工厂模式可以通过一个工厂类(设备),来调用自身的静态方法(生产方式)来产生对象实例(产品),在上述实例中的Json类等,就相当于原料了。

理解了上面的一段话之后,我们就可以再深入的了解下这个工厂模式了。

我们来考虑以下场景,如果项目中,我们通过一个类创建对象,在快完成或者已经完成,要扩展功能的时候,发现原来的类的类名不是很合适或者发现类需要添加构造函数参数才能实现功能扩展,在这种情况下,大家就可以感受到“高内聚低耦合”的博大精深,我们可以尝试使用工厂模式解决这个问题。

还有就是最经典的数据库连接问题等等,都可以使用工厂模式来接觉问题,咱们也不废话,来看一下网上一个比较经典的案例:

interface Transport{
  public function go();
 
}
 
class Bus implements Transport{
  public function go(){
    echo "bus每一站都要停";
  }
}
 
class Car implements Transport{
  public function go(){
    echo "car跑的飞快";
  }
}
 
class Bike implements Transport{
  public function go(){
    echo "bike比较慢";
  }
}
 
class transFactory{
  public static function factory($transport)
  {
    
    switch ($transport) {
      case 'bus':
        return new Bus();
        break;
 
      case 'car':
        return new Car();
        break;
      case 'bike':
        return new Bike();
        break;
    }
  }
}
 
$transport=transFactory::factory('car');
$transport->go();

大家有了解过工厂模式应该都知道,工厂模式有三种,那就是一般工厂模式(静态工厂模式),工厂模式,还有就是抽象工厂模式,咱这里并未把所有的案例全部介绍完毕,不过嘞,咱们可以跟着网上的一个案例,来简单了解下工厂模式的三种变形的过程。

首先,我们来假设有个关于个人事务管理的项目,功能之一就是管理Appointment(预约)对象。我们的业务团队和A公司建立了关系,目前需要使用一个叫做BloggsCal格式来和他们交流预约相关的数据,但是业务部门提醒可能会有更多的数据格式,所以解码器可能会有多种,我们呢,为了避免在逻辑代码中使用过多的if else,可能就会需要使用工厂模式来将创造者和使用者分开。

那么,我们就需要两个类,一个类AppEncoder用于定义一个解码器,将A公司传来的数据解码;另外一个类CommsManager用于获取该解码器,就是调用AppEncoder类,用于与A公司进行通信。使用模式术语说,CommsManager就是创造者,AppEncoder就是产品(一个创造者、一个产品,将类的实例化和对象的使用分离开,这就是工厂模式的思想)。

咱们先来通过简单工厂模式实现上述任务场景,如下:

//产品类
class BloggsApptEncoder {
  function encode()
  {
    return "Appointment data encoded in BloggsCal format\n";
  } 
}
 
//创造者类
class CommsManager {
  function static getBloggsApptEncoder()
  { 
    return new BloggsApptEncoder();
  } 
}

大概明白了奥,好啦,现在又有新任务了,业务部门告诉我们需要新增一种数据格式MegCal,来完成数据交流,那么我们就需要新增对应的解码器类,然后直接在commsManager新增参数来标识需要实例化哪个解码器,如下:

class CommsManager {
  const BLOGGS = 1;
  const MEGA = 2;
  private $mode;
 
  public function __construct( $mode )
  {
    $this->mode = $mode;
  } 
 
  function getApptEncoder()
  {
    switch($this->mode) {
      case (self::MEGA):
        return new MegaApptEncoder();
      default:
        return new BloggsApptEncoder();
    }  
  }
}

上述两个案例综合起来就是简单工厂模式了,它符合现实中的情况,而且客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品(正如暴发户所为)。

接下来,我们从开闭原则上来分析下简单工厂模式,当新增一种数据格式的时候,只要符合抽象产品格式,那么只要通知工厂类知道就可以被使用了(即创建一个新的解码器类,继承抽象解码器ApptEncoder),那么对于产品部分来说,它是符合开闭原则的——对扩展开放、对修改关闭,但是对于工厂类不太理想,因为每增加一各格式,都要在工厂类中增加相应的商业逻辑和判断逻辑,这显自然是违背开闭原则的。

然而在实际应用中,很可能产品是一个多层次的树状结构,这时候由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,因此简单工厂模式只适用于业务简单的情况下或者具体产品很少增加的情况,而对于复杂的业务环境可能不太适应了,这个时候就应该由工厂方法模式来出场了。

又来新需求了,那就是每种格式的预约数据中,需要提供页眉和页脚来描述每次预约。

咱们先来用简单工厂模式来实现上述功能,如下:

// 简单工厂模式
class CommsManager {
  const BLOGGS = 1;
  const MEGA = 2;
  private = $mode;
 
  public function __construct( $mode )
  {
    $this->mode = $mode;
  }
 
  // 生成解码器对应的页眉
  public function getHeaderText() 
  {
    switch( $this->mode ) {
      case ( self::MEGA ):
        return "MegaCal header\n";
      default:
        return "BloggsCal header\n";
    }
  }
  
  // 生成解码器
  public function getApptEncoder()
  {
    switch( $this->mode ) {
      case ( self::MEGA ):
        return new MegaApptEncoder();
      default:
        return new BloggsApptEncoder();;
    }  
  }
}

从上述代码中,我们可以看到,相同的条件语句switch在不同的方法中出现了重复,而且如果添加新的数据格式,那么需要改动的类过多。所以需要对我们的结构进行修改,以求更容易扩展和维护。

我们可以使用创造者子类分别生成对应的产品,这样添加新的数据格式时,只需要添加一个创造者子类即可,方便扩展和维护,这也就是比简单工厂模式更复杂一点的工厂模式,如下:

// 工厂模式
abstract class CommsManager {
  abstract function getHeaderText();
  abstract function getApptEncoder();
  abstract function getFooterText();
}
 
class BloggsCommsManager extends CommsManager {
  function getHeaderText()
  {
    return "BloggsCal Header\n"; 
  }
 
  function getApptEncoder()
  {
    return new BloggsApptEncoder();
  }
 
  function getFooterText()
  {
    return "BloggsCal Footer\n";
  }
}
 
class MegaCommsManager extends CommsManager {
  function getHeaderText()
  {
    return "MegaCal Header\n"; 
  }
 
  function getApptEncoder()
  {
    return new MegaApptEncoder();
  }
 
  function getFooterText()
  {
    return "MegaCal Footer\n";
  }
}

在这个时候,如果有新的数据格式,只需要添加一个创造类的子类即可,例如此时想获取MegaCal对应的解码器直接通过MegaCommsManager::getApptEncoder()获取即可。

完了么?我只能说,这个实例还没有完事。

新需求又来了,那就是不仅需要和A公司交流预约数据(Appointment),还需要交流待办事宜(Ttd)、联系人(Contact)等数据,同样的这些数据交流的格式也是BloggsCal和MegaCal,这个时候,我们可以直接在对应解码器的子类中添加处理事宜(TtD)和联系人(Contact)的方法,如下:

// 抽象工厂模式
abstract class CommsManager {
  abstract function getHeaderText();
  abstract function getApptEncoder();
  abstract function getTtdEncoder();
  abstract function getContactEncoder();
  abstract function getFooterText();
}
 
class BloggsCommsManager extends CommsManager {
  function getHeaderText()
  {
    return "BloggsCal Header\n"; 
  }
 
  function getApptEncoder()
  {
    return new BloggsApptEncoder();
  }
 
  function getTtdEncoder()
  {
    return new BloggsTtdEncoder();
  }
 
  function getContactEncoder()
  {
    return new BloggsContactEncoder();
  }
 
  function getFooterText()
  {
    return "BloggsCal Footer\n";
  }
}
 
class MegaCommsManager extends CommsManager {
  function getHeaderText()
  {
    return "MegaCal Header\n"; 
  }
 
  function getApptEncoder()
  {
    return new MegaApptEncoder();
  }
 
  function getTtdEncoder()
  {
    return new MegaTtdEncoder();
  }
 
  function getContactEncoder()
  {
    return new MegaContactEncoder();
  }
 
  function getFooterText()
  {
    return "MegaCal Footer\n";
  }
}
 
//当然需要添加对应的TtdEncoder抽象类和ContactEncoder抽象类,以及他们的子类。

好啦,到这里就算是差不多结束了工厂模式的变形过程,我们可以来简单归纳下这个变形过程中的核心,如下:

1.将系统和实现的细节分离开,我们可在示例中移除或者添加任意数目的编码格式而不会影响系统。

2.对系统中功能相关的元素强制进行组合,因此,通过使用BloggsCommsManager,可以确定只使用与BloggsCal有关的类。

3.添加新产品时将会令人苦恼,因为不仅需要创建新产品的具体实现,而且为了支持它,我们必须修改抽象创建者和它的每个具体实现。

当然,我们可以创建一个标志参数来决定返回什么对象的单一的make()方法,而不用给每个工厂创建独立的方法,如下:

abstract class CommsManager {
  const APPT = 1;
  const TTD = 2;
  const CONTACT = 3;
 
  abstract function getHeaderText();
  abstract function make ( $flag_init );
  abstract function getFooterText();
}
 
class BloggsCommsManager extends CommsManager {
  function getHeaderText()
  {
    return "BloggsCal Header\n";
  }
 
  function make( $flag_init )
  {
    switch ($flag_init) {
      case self::APPT:
        return new BloggsApptEncoder();
      case self::TTD:
        return new BloggsTtdEncoder();
      case self::CONTACT:
        return new BloggsContactEncoder();
    }
  }
 
  function getFooterText()
  {
    return "BloggsCal Header\n";
  }
}

此时,如果还需要添加交流其它的数据,此时只需在抽象创造者中添加一个新的flag_init标识,并在子创造者中的make方法中添加一个条件,相比来说比原先的更加容易扩展,只需修改少数地方即可。

最后,来简单总结下:

简单工厂:适用于生成数量少,功能简单的产品(BloggApptEncoder和MegaApptEncoder)

工厂模式:适用于生成数量多,功能复杂的产品(多个产品树[BloggCal,MegaCal]、单个产品族[apptEncoder]),相比简单工厂来说:业务更复杂,功能更多,但是产品族还是单个。

抽象工厂:适用于生成多个产品族、多个产品树的情景(产品族[appt,ttd,contact],产品树[Bloggcal,megaCal])。相比于工厂模式,更容易扩展添加新的产品族

好啦,本次记录就到这里了。

更多关于PHP相关内容感兴趣的读者可查看本站专题:《php面向对象程序设计入门教程》、《PHP数组(Array)操作技巧大全》、《PHP基本语法入门教程》、《PHP运算与运算符用法总结》、《php字符串(string)用法总结》、《php mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家PHP程序设计有所帮助。

PHP设计模式之工厂模式(Factory)入门与应用详解的更多相关文章

  1. 从iOS应用程序发送帖子到PHP脚本不工作…简单的解决方案就像

    我之前已经做了好几次了但是由于某些原因我无法通过这个帖子…我尝试了设置为_POST且没有的变量的PHP脚本……当它们未设置为发布时它工作精细.这是我的iOS代码:这里是PHP的一大块,POST变量不在正确的位置?我想这对于更有经验的开发人员来说是一个相当简单的答案,感谢您的帮助!解决方法$_POST是一个数组,而不是一个函数.您需要使用方括号来访问数组索引:

  2. swift学习2 元组 tuples

    swift中出现了一种新的数据结构,非常牛掰的元组tuples如果懂PHP的猿,会发现这个元组和PHP的数组非常类似,同样是可以默认不指定key,也可以指定key目前的学习疑问是,如何进行元组的遍历?

  3. MVVM 不是那么好

    我觉得MVVM是一种反人类的设计模式,它使架构更加混乱而非清晰。MVVM命名很糟糕名称是很重要的。ViewModel这一名称则没有发挥任何作用。ViewModel的第一种含义是modelfortheview。MVVM引进太多职责命名不够具体,导致这个类的任务无休止地增长。MVVM不改变你的架构viewmodel并不能从根本上改变你的应用程序的架构。我能想到的MVVM模式最大的好处就是它把“下水道”从苹果自带的viewcontrooller类转移到了viewmodel这一自定义的对象。

  4. 尝试使用swift mailer,gmail smtp,php发送邮件

    这里是我的代码:在运行时出现此错误…

  5. 开发Swift iOS应用程序“正确的方式”

    最近,我学习了Swift和开发iOS应用程序的基础知识。现在,我想自己开发一个真正的应用程序,但我非常关心编写好的代码,所以我已经寻找“最佳实践”,“设计模式”和“正确的方式”来实现它。在我的搜索中,我发现这个greattutorial关于SwiftiOS应用程序中通常使用的所有设计模式,以及他们使用的示例。不应该将httpClient和persistencyManager声明为协议,然后HttpClient和PersistencyManager类实现该协议?我应该在哪里告诉应用程序?最后但并非最不重要的

  6. PHP使用JpGraph绘制折线图操作示例【附源码下载】

    这篇文章主要介绍了PHP使用JpGraph绘制折线图操作,结合实例形式分析了php使用JpGraph的相关操作技巧与注意事项,并附带源码供读者下载参考,需要的朋友可以参考下

  7. jQuery的Cookie封装,与PHP交互的简单实现

    下面小编就为大家带来一篇jQuery的Cookie封装,与PHP交互的简单实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  8. PHP+jquery+CSS制作头像登录窗(仿QQ登陆)

    本篇文章介绍了PHP结合jQ和CSS制作头像登录窗(仿QQ登陆),实现了类似QQ的登陆界面,很有参考价值,有需要的朋友可以了解一下。

  9. 如何在PHP环境中使用ProtoBuf数据格式

    这篇文章主要介绍了如何在PHP环境中使用ProtoBuf数据格式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

  10. PHP rsa加密解密算法原理解析

    这篇文章主要介绍了PHP rsa加密解密算法原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

随机推荐

  1. PHP个人网站架设连环讲(一)

    先下一个OmnihttpdProffesinalV2.06,装上就有PHP4beta3可以用了。PHP4给我们带来一个简单的方法,就是使用SESSION(会话)级变量。但是如果不是PHP4又该怎么办?我们可以假设某人在15分钟以内对你的网页的请求都不属于一个新的人次,这样你可以做个计数的过程存在INC里,在每一个页面引用,访客第一次进入时将访问时间送到cookie里。以后每个页面被访问时都检查cookie上次访问时间值。

  2. PHP函数学习之PHP函数点评

    PHP函数使用说明,应用举例,精简点评,希望对您学习php有所帮助

  3. ecshop2.7.3 在php5.4下的各种错误问题处理

    将方法内的函数,分拆为2个部分。这个和gd库没有一点关系,是ecshop程序的问题。会出现这种问题,不外乎就是当前会员的session或者程序对cookie的处理存在漏洞。进过本地测试,includes\modules\integrates\ecshop.php这个整合自身会员的类中没有重写integrate.php中的check_cookie()方法导致,验证cookie时返回的username为空,丢失了登录状态,在ecshop.php中重写了此方法就可以了。把他加到ecshop.php的最后面去就可

  4. NT IIS下用ODBC连接数据库

    $connection=intodbc_connect建立数据库连接,$query_string="查询记录的条件"如:$query_string="select*fromtable"用$cur=intodbc_exec检索数据库,将记录集放入$cur变量中。再用while{$var1=odbc_result;$var2=odbc_result;...}读取odbc_exec()返回的数据集$cur。最后是odbc_close关闭数据库的连接。odbc_result()函数是取当前记录的指定字段值。

  5. PHP使用JpGraph绘制折线图操作示例【附源码下载】

    这篇文章主要介绍了PHP使用JpGraph绘制折线图操作,结合实例形式分析了php使用JpGraph的相关操作技巧与注意事项,并附带源码供读者下载参考,需要的朋友可以参考下

  6. zen_cart实现支付前生成订单的方法

    这篇文章主要介绍了zen_cart实现支付前生成订单的方法,结合实例形式详细分析了zen_cart支付前生成订单的具体步骤与相关实现技巧,需要的朋友可以参考下

  7. Thinkphp5框架实现获取数据库数据到视图的方法

    这篇文章主要介绍了Thinkphp5框架实现获取数据库数据到视图的方法,涉及thinkPHP5数据库配置、读取、模型操作及视图调用相关操作技巧,需要的朋友可以参考下

  8. PHP+jquery+CSS制作头像登录窗(仿QQ登陆)

    本篇文章介绍了PHP结合jQ和CSS制作头像登录窗(仿QQ登陆),实现了类似QQ的登陆界面,很有参考价值,有需要的朋友可以了解一下。

  9. 基于win2003虚拟机中apache服务器的访问

    下面小编就为大家带来一篇基于win2003虚拟机中apache服务器的访问。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. Yii2中组件的注册与创建方法

    这篇文章主要介绍了Yii2之组件的注册与创建的实现方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下

返回
顶部