侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

PHP设计模式之工厂模式(Factory)入门与应用详解

php  /  管理员 发布于 7年前   145

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

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

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

代码如下:

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

工厂么,它就是生产产品的地方,它有原料,设备和产品,那么在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设计模式 php实现策略模式(strategy)
  • php设计模式 Strategy(策略模式)
  • PHP实现的策略模式简单示例
  • php设计模式之策略模式应用案例详解
  • PHP设计模式之策略模式原理与用法实例分析
  • php策略模式简单示例分析【区别于工厂模式】
  • PHP设计模式之单例模式入门与应用详解
  • PHP设计模式之观察者模式入门与应用案例详解
  • PHP设计模式之中介者模式(Mediator Pattern)入门与应用案例详解
  • PHP设计模式之迭代器(Iterator)模式入门与应用详解
  • PHP设计模式之解释器(Interpreter)模式入门与应用详解
  • PHP设计模式之策略模式(Strategy)入门与应用案例详解


  • 上一条:
    PHP设计模式之解释器(Interpreter)模式入门与应用详解
    下一条:
    简述PHP7.4 新特性和废弃的功能
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客