PHP设计模式(三)建造者模式Builder实例详解【创建型】


Posted in PHP onMay 02, 2020

本文实例讲述了PHP设计模式:建造者模式Builder。分享给大家供大家参考,具体如下:

1. 概述

       在软件开发的过程中,当遇到一个“复杂的对象”的创建工作,该对象由一定各个部分的子对象用一定的算法构成,由于需求的变化,复杂对象的各个部分经常面临剧烈的变化,但将它们组合在一起的算法相对稳定。

       例子1:买肯德基

       典型的儿童餐包括一个主食,一个辅食,一杯饮料和一个玩具(例如汉堡、炸鸡、可乐和玩具车)。这些在不同的儿童餐中可以是不同的,但是组合成儿童餐的过程是相同的。

PHP设计模式(三)建造者模式Builder实例详解【创建型】

       客户端:顾客,想去买一套套餐(这里面包括汉堡,可乐,薯条),可以有1号和2号两种套餐供顾客选择。
       指导者角色:收银员。知道顾客想要买什么样的套餐,并告诉餐馆员工去准备套餐。
       建造者角色:餐馆员工。按照收银员的要求去准备具体的套餐,分别放入汉堡,可乐,薯条等。
       产品角色:最后的套餐,所有的东西放在同一个盘子里面。

        例子2:计算工资:工资的计算一般是:底薪+奖金-税。但底薪分为一级8000、二级6000、三级4000三个等级。根据岗位不同奖金的发放也不一样,管理及日常事务处理岗位(A类)每月根据领导及同事间的评议得分计算奖金,销售岗位(B类)则根据销售额发放提成。税金则根据奖金和底薪的数额进行计算。由此看出该工资的计算方式是比较稳定的构建算法,但对工资的每一部分都会根据不同的情况产生不同的算法,如何将客户端与变化巨烈的底薪、奖金和税金计算方式分离呢,这也比较适合用建造者模式。

2 . 问题

我们如何应对这种变化,如何提供一种“封装机制”来隔离“复杂对象的各个部”的变化,从而保持系统中的“稳定构建算法”而不随需求的变化而变化?

3. 解决方案

建造者模式: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

4. 适用性

在以下情况使用Builder模式

•当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。

•当构造过程必须允许被构造的对象有不同的表示时。

5. 结 构

此模式结构如下页上图所示。

PHP设计模式(三)建造者模式Builder实例详解【创建型】

6. 构建模式的组成

• 抽象建造者角色(Builder):为创建一个Product对象的各个部件指定抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此角色规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。

• 具体建造者(ConcreteBuilder)

1)实现Builder的接口以构造和装配该产品的各个部件。即实现抽象建造者角色Builder的方法。

2)定义并明确它所创建的表示,即针对不同的商业逻辑,具体化复杂对象的各部分的创建

3)  提供一个检索产品的接口

4)   构造一个使用Builder接口的对象即在指导者的调用下创建产品实例

指导者(Director):调用具体建造者角色以创建产品对象的各个部分。指导者并没有涉及具体产品类的信息,真正拥有具体产品的信息是具体建造者对象。它只负责保证对象各部分完整创建或按某种顺序创建。

产品角色(Product):建造中的复杂对象。它要包含那些定义组件的类,包括将这些组件装配成产品的接口。

7. 效果

Builder模式的主要效果:

1 ) 它使你可以改变一个产品的内部表示 Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。因为产品是通过抽象接口构造的,你在改变该产品的内部表示时所要做的只是定义一个新的生成器。

2) 它将构造代码和表示代码分开 Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息;这些类是不出现在Builder接口中的。每个Concrete Builder包含了创建和装配一个特定产品的所有代码。这些代码只需要写一次;然后不同的Director可以复用它以在相同部件集合的基础上构作不同的Product。

3 ) 它使你可对构造过程进行更精细的控制 Builder模式与一下子就生成产品的创建型模式不同,它是在导向者的控制下一步一步构造产品的。仅当该产品完成时导向者才从生成器中取回它。因此Builder接口相比其他创建型模式能更好的反映产品的构造过程。这使你可以更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。

8. 实现:

指导者:收银员

<?php
 /**
 * 指导者:收银员
 *
 */
 class DirectorCashier
 {
 /**
  * 收银餐馆员工返回的食物
  *
  */
 public function buildFood(Builder $builder) {
  $builder->buildPart1();
  $builder->buildPart2();
 }
 }

抽象建造者:

/**
 * 抽象建造者
 *
 */
 abstract class Builder
 {
 /**
  * 创建产品的第一部分
   */
 public abstract function buildPart1();
 
 /**
  * 
  * 创建产品的第二部分
   */
 public abstract function buildPart2();
 
 /**
  * 
  * 返回产品
   */
 public abstract function getProduct();
 }

具体建造者类:

/**
 * 具体建造者类:餐馆员工,返回的套餐是:汉堡两个+饮料一个
 *
 */
 class ConcreteBuilder1 extends Builder
 {
 protected $_product = null;//产品对象
 function __construct(){
  $this->_product = new Product();
 }
 
 /**
  * 创建产品的第一部分::汉堡=2
   */
 public function buildPart1()
 {
  $this->_product->add('Hamburger',2);
 }
 /**
  * 
  * 创建产品的第二部分:
   */
 public function buildPart2()
 {
  $this->_product->add('Drink', 1);
 }
 /**
  * 返回产品对象 :
  * 
  */
 public function getProduct() {
  return $this->_product;
 }
 }
/**
 * 具体建造者类:餐馆员工,汉堡1个+饮料2个
 *
 */
 class ConcreteBuilder2 extends Builder
 {
 protected $_product = null;//产品对象
 function __construct(){
  $this->_product = new Product();
 }
 
 /**
  * 创建产品的第一部分:汉堡
   */
 public function buildPart1()
 {
  $this->_product->add('Hamburger', 1);
 }
 /**
  * 
  * 创建产品的第二部分:drink=2
   */
 public function buildPart2()
 {
  $this->_product->add('Drink', 2);
 }
 /**
  * 返回产品对象 :
  * 
  */
 public function getProduct() {
  return $this->_product;
 }
 }

产品类:

/**
 * 产品类
 */
 class Product
 {
 public $products = array();
 /**
  * 添加具体产品
  */
 public function add($name, $value) {
  $this->products[$name] = $value;
 }
 /**
  * 给顾客查看产品
  */
 public function showToClient()
 {
  foreach ($this->products as $key => $v) {
  echo $key , '=' , $v ,'<br>';
  }
 }
 }

客户程序:

//客户程序
 class Client
 {
 /**
  * 顾客购买套餐
  *
  */
 public function buy($type) {
  //指导者,收银员
  $director = new DirectorCashier(); 
  //餐馆员工,收银员
     $class = new ReflectionClass('ConcreteBuilder' .$type );
     $concreteBuilder = $class->newInstanceArgs();
     //收银员组合员工返回的食物
     $director->buildFood($concreteBuilder);
     //返回给顾客
     $concreteBuilder->getProduct()->showToClient();
 }
 }
 //测试
 ini_set('display_errors', 'On');
 $c = new Client();
 $c->buy(1);//购买套餐1
 $c->buy(2);//购买套餐1

9. 建造者模式的优点

       首先,建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。

       其次,建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。

10. 建造者模式与工厂模式的区别

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

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

11. 总结

      建造者模式与工厂模式类似,他们都是建造者模式,适用的场景也很相似。一般来说,如果产品的建造很复杂,那么请用工厂模式;如果产品的建造更复杂,那么请用建造者模式。

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

PHP 相关文章推荐
杏林同学录(二)
Oct 09 PHP
dedecms系统常用术语汇总
Apr 03 PHP
PHP 生成的XML以FLASH获取为乱码终极解决
Aug 07 PHP
PHP 学习路线与时间表
Feb 21 PHP
php !function_exists(&quot;T7FC56270E7A70FA81A5935B72EACBE29&quot;))代码解密
Jan 07 PHP
php中用foreach来操作数组的代码
Jul 17 PHP
PHP中抽象类和抽象方法概念与用法分析
May 24 PHP
php array_keys 返回数组的键名
Oct 25 PHP
PHP 二级子目录(后台目录)设置二级域名
Mar 02 PHP
PHP框架laravel的.env文件配置教程
Jun 07 PHP
PHP面向对象五大原则之单一职责原则(SRP)详解
Apr 04 PHP
Laravel 微信小程序后端实现用户登录的示例代码
Nov 26 PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
May 02 #PHP
PHP设计模式概论【概念、分类、原则等】
May 01 #PHP
PHP设计模式之 策略模式Strategy详解【对象行为型】
May 01 #PHP
php如何获取Http请求
Apr 30 #PHP
PHP 命名空间和自动加载原理与用法实例分析
Apr 29 #PHP
Yii使用EasyWechat实现小程序获取用户的openID的方法
Apr 29 #PHP
Thinkphp集成抖音SDK的实现方法
Apr 28 #PHP
You might like
php防止用户重复提交表单
2015/11/02 PHP
PHP编程入门的基本语法知识点总结
2016/01/26 PHP
php使用Jpgraph创建柱状图展示年度收支表效果示例
2017/02/15 PHP
javascript-TreeView父子联动效果保持节点状态一致
2007/08/12 Javascript
js表格分页实现代码
2009/09/18 Javascript
extjs 学习笔记(一) 一些基础知识
2009/10/13 Javascript
js的压缩及jquery压缩探讨(提高页面加载性能/保护劳动成果)
2013/01/29 Javascript
javascript计算星座属相(十二生肖属相)示例代码
2014/01/09 Javascript
js 调用百度地图api并在地图上进行打点添加标注
2014/05/13 Javascript
实现图片预加载的三大方法及优缺点分析
2014/11/19 Javascript
Js可拖拽放大的层拖动特效实现方法
2015/02/25 Javascript
JavaScript取得WEB安全颜色列表的方法
2015/07/14 Javascript
JavaScript入门系列之知识点总结
2016/03/24 Javascript
Node.js利用js-xlsx处理Excel文件的方法详解
2017/07/05 Javascript
Angular网络请求的封装方法
2018/05/22 Javascript
vue中设置height:100%无效的问题及解决方法
2018/07/27 Javascript
js+canvas实现简单扫雷小游戏
2021/01/22 Javascript
js实现3D粒子酷炫动态旋转特效
2020/09/13 Javascript
win7+Python3.5下scrapy的安装方法
2018/07/31 Python
启动Atom并运行python文件的步骤
2018/11/09 Python
Python 确定多项式拟合/回归的阶数实例
2018/12/29 Python
Python线程之定位与销毁的实现
2019/02/17 Python
Python中类的创建和实例化操作示例
2019/02/27 Python
python3.5安装python3-tk详解
2019/04/26 Python
PyQt5 在label显示的图片中绘制矩形的方法
2019/06/17 Python
python 三元运算符使用解析
2019/09/16 Python
解决pyecharts运行后产生的html文件用浏览器打开空白
2020/03/11 Python
浅谈pandas dataframe对除数是零的处理
2020/07/20 Python
美国内衣品牌:Leonisa
2016/08/14 全球购物
如何让Java程序执行效率更高
2014/06/25 面试题
医学生毕业自我鉴定
2014/03/26 职场文书
求职导师推荐信范文
2015/03/27 职场文书
2015年学生管理工作总结
2015/05/26 职场文书
粗暴解决CUDA out of memory的问题
2021/05/22 Python
SQL Server代理:理解SQL代理错误日志处理方法
2021/06/30 SQL Server
MongoDB支持的数据类型
2022/04/11 MongoDB