PHP设计模式之建造者模式(Builder)原理与用法案例详解


Posted in PHP onDecember 12, 2019

本文实例讲述了PHP设计模式之建造者模式(Builder)原理与用法。分享给大家供大家参考,具体如下:

这个建造者模式,我们也可以称为生成器模式,核心思想是将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式,简单点来说就是为了消除其它对象复杂的创建过程。

例如:汽车,他的发动机引擎有好多品牌,轮胎也有各种材质,内饰更是千奇百怪;鸟,他的头、翅膀以及脚有各种颜色和形状,在创建这种复杂对象的时候,我们建议使用建造者模式。

先来看一个案例来感受下什么是建造者模式:

  1. 有一个用户的UserInfo类,创建这个类,需要创建用户的姓名,年龄,金钱等信息,才能获得用户具体的信息结果。
  2. 创建一个UserInfoBuilder 用户建造者类,这个类,将UserInfo复杂的创建姓名,年龄,金钱等操作封装起来,简化用户类的创建过程

完事代码如下:

//建造者模式,目的是消除其它对象复杂的创建过程
/* 描述一个用户的类,包含用户姓名,年龄,金钱 */
class UserInfo {
    protected $userName = ''; 
    protected $userAge = '';
    protected $userMoney = '';
    public function setUserName($userName) {
        $this->userName = $userName;
    }
    public function setUserAge($userAge) {
        $this->userAge = $userAge;
    }
    public function setUserMoney($userMoney) {
        $this->userMoney = $userMoney;
    }
    public function getPeople() {
        echo "这个人的姓名是:" . $this->setUserName . ',年龄是:' . $this->userAge . ', 金钱:' . $this->userMoney;
    }
}
/* 实例化,并且创建这个用户的时候,是很痛苦的,需要设置用户名,年龄和金钱*/
$peopleInfo = array(
    'userName' => 'initphp',
    'userAge' => 28,
    'userMoney' => '100元'
    );
$UserInfo = new UserInfo;
//下面需要一步步的设置用户信息,才能得到用户详细信息,过程纠结而痛苦
$UserInfo->setUserName($peopleInfo['userName']); 
$UserInfo->setUserAge($peopleInfo['userAge']);
$UserInfo->setUserMoney($peopleInfo['userMoney']);
$UserInfo->getPeople();
//UserInfoBuilder 用户信息建造者类,将UserInfo的创建过程封装掉,开发者使用起来心情舒畅
<?php
//建造者模式,目的是消除其它对象复杂的创建过程
include("UserInfo.php");
class UserInfoBuilder {
    protected $obj;
    public function __construct() {
        $this->obj = new UserInfo;
    }
    public function buildPeople($peopleInfo) {
        $this->obj->setUserName($peopleInfo['userName']);
        $this->obj->setUserAge($peopleInfo['userAge']);
        $this->obj->setUserMoney($peopleInfo['userMoney']);
    } 
    public function getPeople() {
        $this->obj->getPeople();
    }
}
/* 创建过程被封装了,用户使用简单了 */
$peopleInfo = array(
    'userName' => 'initphp',
    'userAge' => 28,
    'userMoney' => '100元'
    );
$UserInfoBuilder = new UserInfoBuilder;
$UserInfoBuilder->buildPeople($peopleInfo); //直接一个build
$UserInfoBuilder->getPeople();

大概了解了之后,咱们就来继续看。

一般情况下,建造者模式一般有以下四种角色:

     1.产品角色,产品角色定义自身的组成属性

     2.抽象建造者,抽象建造者定义了产品的创建过程以及如何返回一个产品

     3.具体建造者,具体建造者实现了抽象建造者创建产品过程的方法,给产品的具体属性进行赋值定义

     4.指挥者,指挥者负责与调用客户端交互,决定创建什么样的产品

这四个角色也可以按照如下方式来理解:

  1. 抽象建造者(Builder)角色:定义一个抽象接口,规范产品各个组成成分的建造(即规范具体建造者的方法实现)。其中所规范的方法中必须包括建造方法和结果返回方法
  2. 具体建造者(ConcreteBuilder)角色:实现抽象建造者角色所定义的方法。具体建造者与业务逻辑关联性较大,应用程序最终会通过调用此角色中所实现的建造方法按照业务逻辑创建产品,在建造完成后通过结果返回方法返回建造的产品实例。一般在外部由客户或一个抽象工厂创建。
  3. 导演者(Director)角色:此角色的作用是调用具体的建造者角色建造产品。导演者与产品类没有直接关系,与产品类交谈的是具体抽象角色。
  4. 产品(Product)角色:在指导者的指导下由建造者所创建的那个复杂的对象导演者角色与客户端直接打交道,它理解客户端的业务逻辑,将客户端创建产品的请求拆分成对产品组成部分的请求,然后调用具体产品角色执行建造操作。它分离了客户端与具体建造者。

再来看个实例:

<?php
/**
 * Created by PhpStorm.
 * User: Jiang
 * Date: 2015/4/25
 * Time: 9:31
 */
/**具体产品角色 鸟类
 * Class Bird
 */
class Bird
{
  public $_head;
  public $_wing;
  public $_foot;
  function show()
  {
    echo "头的颜色:{$this->_head}<br/>";
    echo "翅膀的颜色:{$this->_wing}<br/>";
    echo "脚的颜色:{$this->_foot}<br/>";
  }
}
/**抽象鸟的建造者(生成器)
 * Class BirdBuilder
 */
abstract class BirdBuilder
{
  protected $_bird;
  function __construct()
  {
    $this->_bird=new Bird();
  }
  abstract function BuildHead();
  abstract function BuildWing();
  abstract function BuildFoot();
  abstract function GetBird();
}
/**具体鸟的建造者(生成器)  蓝鸟
 * Class BlueBird
 */
class BlueBird extends BirdBuilder
{
  function BuildHead()
  {
    // TODO: Implement BuilderHead() method.
    $this->_bird->_head="Blue";
  }
  function BuildWing()
  {
    // TODO: Implement BuilderWing() method.
    $this->_bird->_wing="Blue";
  }
  function BuildFoot()
  {
    // TODO: Implement BuilderFoot() method.
    $this->_bird->_foot="Blue";
  }
  function GetBird()
  {
    // TODO: Implement GetBird() method.
    return $this->_bird;
  }
}
/**玫瑰鸟
 * Class RoseBird
 */
class RoseBird extends BirdBuilder
{
  function BuildHead()
  {
    // TODO: Implement BuildHead() method.
    $this->_bird->_head="Red";
  }
  function BuildWing()
  {
    // TODO: Implement BuildWing() method.
    $this->_bird->_wing="Black";
  }
  function BuildFoot()
  {
    // TODO: Implement BuildFoot() method.
    $this->_bird->_foot="Green";
  }
  function GetBird()
  {
    // TODO: Implement GetBird() method.
    return $this->_bird;
  }
}
/**指挥者
 * Class Director
 */
class Director
{
  /**
   * @param $_builder   建造者
   * @return mixed     产品类:鸟
   */
  function Construct($_builder)
  {
    $_builder->BuildHead();
    $_builder->BuildWing();
    $_builder->BuildFoot();
    return $_builder->GetBird();
  }
}
//调用代码
header("Content-Type:text/html;charset=utf-8");
//------------------------生成器模式测试代码------------------
require_once "./Builder/Builder.php";
$director=new Director();
echo "蓝鸟的组成:<hr/>";
$blue_bird=$director->Construct(new BlueBird());
$blue_bird->Show();
echo "<br/>Rose鸟的组成:<hr/>";
$rose_bird=$director->Construct(new RoseBird());
$rose_bird->Show();

建造者模式它的优点很明显,就是它可以很好的将一个对象的实现与相关的“业务”逻辑分离开来,从而可以在不改变事件逻辑的前提下,使增加(或改变)实现变得非常容易,缺点也是同样,那就是建造者接口的修改会导致所有执行类的修改。

关于这个建造者模式,它还有以下三个扩展模式:

  1. 抽象工厂模式(abstract factory模式):在抽象工厂模式中,每一次工厂对象被调用时都会返还一个完整的产品对象,而客户端可能会将这些产品组装成一个更大更复杂的产品,也可能不会。建造者模式则不同,它一点一点地建造出一个复杂的产品,而这个产品的组装过程发生在建造者内部。二者的区别在于是否有组装过程,组装过程发生的位置。这两个设计模式可以连起来用,客户端通过调用一个建造角色,间接调用另一个抽象工厂模式的工厂角色。工厂模式返还不同产品族的零件,而建造者模式则把它们组装起来。
  2. 策略模式(strategy模式):建造者模式在结构上很接近于策略模式,事实上建造者模式是策略模式的一种特殊情况。二者的区别在于用意不同。建造者模式作用于客户端一点一点的建造新的对象,而策略模式的目的是为算法提供抽象的接口。
  3. 建造者模式与模板方法模式:建造者模式在退化、失去导演者角色后,可以发展到模板方法模式(即将建造过程的算法实现放在建造角色中)。

以下情况应当使用建造者模式:

   1、 需要生成的产品对象有复杂的内部结构。
   2、 需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
   3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

使用建造者模式主要有以下效果:

   1、 建造者模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。
   2、 每一个Builder都相对独立,而与其它的Builder无关。
   3、 模式所建造的最终产品更易于控制。

咱们接下来,来尝试设计一个车的组装过程,这个是网上经典的案例,如下:

<?php
/** 
 * 建造者模式
 */ 
//需要建造的产品(product)
class Car
{/*{{{*/
  public $name;
  public $engine;//发动机
  public $chassis;//底盘
  public $body;//车身
  public $equipment;//电器设备
  public function setName($name)
  {
    $this->name = $name;
  }
  public function setEngine($engine)
  {
    $this->engine = $engine;
  }
  public function setChassis($chassis)
  {
    $this->chassis = $chassis;
  }
  public function setBody($body)
  {
    $this->body = $body;
  }
  public function setEquipment($equipment)
  {
    $this->equipment = $equipment;
  }
  public function show()
  {
    echo "名称:".$this->name."\r\n";
    echo "引擎:".$this->engine."\r\n";
    echo "底盘:".$this->chassis."\r\n";
    echo "车身:".$this->body."\r\n";
    echo "电子设备:".$this->equipment."\r\n";
  }
}/*}}}*/
//builder
interface IBuilder
{/*{{{*/
  public function builderName();
  public function builderEngine();
  public function builderChassis();
  public function builderBody();
  public function builderEquipment();
  public function getCar();
}/*}}}*/
//红旗车builder
class RedBuilder implements IBuilder
{/*{{{*/
  public $car;
  public function __construct()
  {
    $this->car = new Car();
  }
  public function builderName()
  {
    $this->car->setName('红旗'); 
  }
  public function builderEngine()
  {
    $this->car->setEngine('国产发动机'); 
  }
  public function builderChassis()
  {
    $this->car->setChassis('超大底盘'); 
  }
  public function builderBody()
  {
    $this->car->setBody('超大'); 
  }
  public function builderEquipment()
  {
    $this->car->setEquipment('电子设备'); 
  }
  public function getCar()
  {
    return $this->car;
  }
}/*}}}*/
//QQ车builder
class QQBuilder implements IBuilder
{/*{{{*/
  public $car;
  public function __construct()
  {
    $this->car = new Car();
  }
  public function builderName()
  {
    $this->car->setName('QQ'); 
  }
  public function builderEngine()
  {
    $this->car->setEngine('国产发动机'); 
  }
  public function builderChassis()
  {
    $this->car->setChassis('小底盘'); 
  }
  public function builderBody()
  {
    $this->car->setBody('小'); 
  }
  public function builderEquipment()
  {
    $this->car->setEquipment('电子设备'); 
  }
  public function getCar()
  {
    return $this->car;
  }
}/*}}}*/
//组装者(director)
class CarDirector
{/*{{{*/
  public function make(IBuilder $builder)
  {
    $builder->builderName();
    $builder->builderEngine();
    $builder->builderChassis();
    $builder->builderBody();
    $builder->builderEquipment();
    return $builder->getCar();
  }
}/*}}}*/
class Client
{/*{{{*/
  public static function main($argv)
  {
    $director = new CarDirector(); 
    $redBuilder = new RedBuilder();
    $car = $director->make($redBuilder);
    $car->show();
    echo "\r\n";
    $qqBuilder = new QQBuilder();
    $car = $director->make($qqBuilder);
    $car->show();
  }
}/*}}}*/
Client::main($argv);
?>

咱们可以观察到,建造者模式与工厂模式是极为相似的,并且总体上,建造者模式仅仅只比工厂模式多了一个“导演类”的角色,在建造者模式中,假如把这个导演类看做是最终调用的客户端,那么图中剩余的部分就可以看作是一个简单的工厂模式了。

与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。

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

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

PHP 相关文章推荐
php 数组的一个悲剧?
May 11 PHP
给初学者的30条PHP最佳实践(荒野无灯)
Aug 02 PHP
php 模拟POST提交的2种方法详解
Jun 17 PHP
php实现简单洗牌算法
Jun 18 PHP
如何修改和添加Apache的默认站点目录
Jul 05 PHP
php中将数组转成字符串并保存到数据库中的函数代码
Sep 29 PHP
输入值/表单提交参数过滤有效防止sql注入的方法
Dec 25 PHP
ThinkPHP表单数据智能写入create方法实例分析
Sep 27 PHP
php官方微信接口大全(微信支付、微信红包、微信摇一摇、微信小店)
Dec 21 PHP
CodeIgniter生成静态页的方法
May 17 PHP
PHP实现的简单sha1加密功能示例
Aug 27 PHP
PHP结合Ffmpeg快速搭建流媒体服务的实践记录
Oct 31 PHP
PHP设计模式之适配器模式(Adapter)原理与用法详解
Dec 12 #PHP
PHP学习记录之常用的魔术常量详解
Dec 12 #PHP
记Laravel调用Gin接口调用formData上传文件的实现方法
Dec 12 #PHP
PHP命名空间(namespace)原理与用法详解
Dec 11 #PHP
在 Laravel 6 中缓存数据库查询结果的方法
Dec 11 #PHP
PHP超级全局变量【$GLOBALS,$_SERVER,$_REQUEST等】用法实例分析
Dec 11 #PHP
关于PHP5.6+版本“No input file specified”问题的解决
Dec 11 #PHP
You might like
PHP中在数据库中保存Checkbox数据(2)
2006/10/09 PHP
php str_pad() 将字符串填充成指定长度的字符串
2010/02/23 PHP
PHP实现的多进程控制demo示例
2019/07/22 PHP
PHP扩展安装方法步骤解析
2020/11/24 PHP
如何让您的中波更粗更长 - 中波框形天线制作
2021/03/10 无线电
页面回到顶部的三种实现(锚标记,js)
2012/10/01 Javascript
nodejs批量修改文件编码格式
2015/01/22 NodeJs
详解addEventListener的三个参数之useCapture
2015/03/16 Javascript
vue小图标favicon不显示的解决方案
2017/09/19 Javascript
JavaScript 日期时间选择器一些小结
2018/04/02 Javascript
微信小程序解析富文本过程详解
2019/07/13 Javascript
layui 对table中的数据进行转义的实例
2019/09/12 Javascript
Python中解析JSON并同时进行自定义编码处理实例
2015/02/08 Python
Python中with及contextlib的用法详解
2017/06/08 Python
Python为何不能用可变对象作为默认参数的值
2019/07/01 Python
Python lambda表达式filter、map、reduce函数用法解析
2019/09/11 Python
推荐技术人员一款Python开源库(造数据神器)
2020/07/08 Python
基于python爬取链家二手房信息代码示例
2020/10/21 Python
Python+unittest+DDT实现数据驱动测试
2020/11/30 Python
马来西亚太阳镜、眼镜和隐形眼镜网上商店:Focus Point
2018/12/13 全球购物
Allen Edmonds官方网站:一家美国优质男士鞋类及配饰制造商
2019/03/12 全球购物
美国围栏公司:Walpole Outdoors
2019/11/19 全球购物
英国奢侈品在线精品店:Hervia
2020/09/03 全球购物
大学生自我评价怎样写好
2013/10/23 职场文书
十佳大学生村官事迹
2014/01/09 职场文书
2014新课程改革心得体会
2014/03/10 职场文书
初三学习计划书范文
2014/04/30 职场文书
企业人事任命书
2014/06/05 职场文书
食堂标语大全
2014/06/11 职场文书
工商局所长四风自我剖析及整改措施
2014/10/26 职场文书
团代会开幕词
2015/01/28 职场文书
python用字节处理文件实例讲解
2021/04/13 Python
如何获取numpy array前N个最大值
2021/05/14 Python
MySQL时间盲注的五种延时方法实现
2021/05/18 MySQL
深入解读Java三大集合之map list set的用法
2021/11/11 Java/Android
一文弄懂MySQL中redo log与binlog的区别
2022/02/15 MySQL