php中的简单工厂模式、工厂模式、抽象工厂模式

距离上次更新博客已通过去10天了,按计划这篇博客早该更新了,可计划赶不上变化因为事情太多,致使该计划不断延期,这不终于有块空闲时间了,得赶忙补上。哈哈。php

好了,言归正传,今天我给你们说说php中的工厂模式。架构

工厂模式可分为简单工厂、工厂和抽象工厂,具体区别是什么呢?下面咱们经过实例来一步一步讲解:app

首先咱们设想一个任务场景:学习

假设有个关于我的事务管理的项目,功能之一就是管理Appointment(预定)对象。咱们的业务团队和A公司创建了关系,目前须要使用一个叫作BloggsCal格式来和他们交流预定相关的数据。可是业务部门提醒可能会有更多的数据格式。优化

任务分析:this

由于解码器可能会有多种,为了不在逻辑代码中使用过多的if else,这里须要使用工厂模式来将创造者和使用者分开,此时须要两个类,一个类AppEncoder用于定义一个解码器,将A公司传来的数据解码;另一个类CommsManager用于获取该解码器,用于与A公司进行通讯。使用模式术语说,CommsManager就是创造者,AppEncoder就是产品。(一个创造者、一个产品,将类的实例化和对象的使用分离开,这就是工厂模式的思想)编码

上面的任务场景咱们能够经过简单工厂模式实现,具体代码以下:spa

 1 <?php
 2 //产品类
 3 class BloggsApptEncoder {
 4     function encode()
 5     {
 6         return "Appointment data encoded in BloggsCal format\n";
 7     }  
 8 }
 9 
10 //创造者类
11 class CommsManager {
12     function static getBloggsApptEncoder()
13     { 
14         return new BloggsApptEncoder();
15 }
16 }

使用方法比较简单,这里不作赘述。设计

此时又有新的需求,业务部门告诉咱们须要新增一种数据格式MegCal,来完成数据交流。此时咱们须要新增对应的解码器类,而后直接在commsManager新增参数来标识须要实例化哪一个解码器。代码以下:code

<?php

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中简单工厂、工厂模式和抽象工厂的初步理解,感谢您的阅读

注:因本人的技术有限,若是有理解错误的地方,还请各位批评指正,共同交流学习,谢谢。我会继续努力的。

相关文章
相关标签/搜索