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 相关文章推荐
linux下 C语言对 php 扩展
Dec 14 PHP
php mssql 分页SQL语句优化 持续影响
Apr 26 PHP
PHP闭包实例解析
Sep 08 PHP
PHP中上传多个文件的表单设计例子
Nov 19 PHP
php动态生成版权所有信息的方法
Mar 24 PHP
php实现搜索一维数组元素并删除二维数组对应元素的方法
Jul 06 PHP
php把数组值转换成键的方法
Jul 13 PHP
php实现的数字验证码及数字运算验证码
Jul 30 PHP
PHP使用SWOOLE扩展实现定时同步 MySQL 数据
Apr 09 PHP
[原创]php token使用与验证示例【测试可用】
Aug 30 PHP
基于swoole实现多人聊天室
Jun 14 PHP
Laravel自动生成UUID,从建表到使用详解
Oct 24 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的超级变量$_POST获取HTML表单(HTML Form) 数据
2011/05/07 PHP
深入HTTP响应状态码速查表的详解
2013/06/07 PHP
php Xdebug的安装与使用详解
2013/06/20 PHP
PHP 设计模式系列之 specification规格模式
2016/01/10 PHP
php使用curl代理实现抓取数据的方法
2017/02/03 PHP
PHP编译configure时常见错误的总结
2017/08/17 PHP
PHP静态延迟绑定和普通静态效率的对比
2017/10/20 PHP
js 手机号码合法性验证代码集合
2012/09/29 Javascript
window.location.href的用法(动态输出跳转)
2014/08/09 Javascript
js获取checkbox复选框选中的选项实例
2014/08/24 Javascript
上传文件返回的json数据会被提示下载问题解决方案
2014/12/03 Javascript
javascript实现动态导入js与css等静态资源文件的方法
2015/07/25 Javascript
jQuery实现向下滑出的二级菜单效果实例
2015/08/22 Javascript
安装使用Mongoose配合Node.js操作MongoDB的基础教程
2016/03/01 Javascript
JavaScript中的boolean布尔值使用学习及相关技巧讲解
2016/05/26 Javascript
JavaScript 闭包机制详解及实例代码
2016/10/10 Javascript
用v-html解决Vue.js渲染中html标签不被解析的问题
2016/12/14 Javascript
NodeJS 实现手机短信验证模块阿里大于功能
2017/06/19 NodeJs
常用的9个JavaScript图表库详解
2017/12/19 Javascript
vue 点击展开显示更多(点击收起部分隐藏)
2019/04/09 Javascript
jquery实现点击弹出对话框
2020/02/08 jQuery
vue基于better-scroll实现左右联动滑动页面
2020/06/30 Javascript
JavaScript 中的六种循环方法
2021/01/06 Javascript
[01:32]DOTA2 2015国际邀请赛中国区预选赛第四日战报
2015/05/29 DOTA
[05:59]2018DOTA2国际邀请赛寻真——只为胜利的Secret
2018/08/13 DOTA
Python中动态检测编码chardet的使用教程
2017/07/06 Python
基于Python对数据shape的常见操作详解
2018/12/25 Python
理肤泉俄罗斯官网:La Roche-Posay俄罗斯
2018/07/24 全球购物
北京麒麟网信息技术有限公司网络游戏测试面试题
2013/09/28 面试题
学生自我评语大全
2014/04/18 职场文书
共产党员岗位承诺书
2014/05/29 职场文书
2014年英语教师工作总结
2014/12/03 职场文书
《红领巾真好》教学反思
2016/02/16 职场文书
Nginx域名转发使用场景代码实例
2021/03/31 Servers
PHP实现创建以太坊钱包转账等功能
2021/04/21 PHP
PostgreSQL13基于流复制搭建后备服务器的方法
2022/01/18 PostgreSQL