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 相关文章推荐
第十二节 类的自动加载 [12]
Oct 09 PHP
自动跳转中英文页面
Oct 09 PHP
PHPMailer 中文使用说明小结
Jan 22 PHP
php实现天干地支计算器示例
Mar 14 PHP
php下Memcached入门实例解析
Jan 05 PHP
PHP实现CSV文件的导入和导出类
Mar 24 PHP
php隐藏实际地址的文件下载方法
Apr 18 PHP
php序列化函数serialize() 和 unserialize() 与原生函数对比
May 08 PHP
适合PHP初学者阅读的4本经典书籍
Sep 23 PHP
yii2 resetful 授权验证详解
May 18 PHP
PHP实现支持CURL字符串证书传输的方法
Mar 23 PHP
PhpStorm2020 + phpstudyV8 +XDebug的教程详解
Sep 17 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
ThinkPHP后台首页index使用frameset时的注意事项分析
2014/08/22 PHP
showModelessDialog()使用详解
2006/09/07 Javascript
Javascript倒计时页面跳转实例小结
2013/09/11 Javascript
仿当当网淘宝网等主流电子商务网站商品分类导航菜单
2013/09/25 Javascript
Javascript中的apply()方法浅析
2015/03/15 Javascript
JavaScript中的toLocaleDateString()方法使用简介
2015/06/12 Javascript
你所不了解的javascript操作DOM的细节知识点(一)
2015/06/17 Javascript
轻松掌握JavaScript中介者模式
2016/08/26 Javascript
深入分析node.js的异步API和其局限性
2016/09/05 Javascript
使用Math.max,Math.min获取数组中的最值实例
2017/04/25 Javascript
vue中axios防止多次触发终止多次请求的示例代码(防抖)
2020/02/16 Javascript
vue npm install 安装某个指定的版本操作
2020/08/11 Javascript
原生js实现俄罗斯方块
2020/10/20 Javascript
vue + el-form 实现的多层循环表单验证
2020/11/25 Vue.js
python在多玩图片上下载妹子图的实现代码
2013/08/13 Python
python如何实现excel数据添加到mongodb
2015/07/30 Python
Python运行报错UnicodeDecodeError的解决方法
2016/06/07 Python
python实现用户答题功能
2018/01/17 Python
Python基于matplotlib实现绘制三维图形功能示例
2018/01/18 Python
Python Numpy 数组的初始化和基本操作
2018/03/13 Python
Python Logging 日志记录入门学习
2018/06/02 Python
python实现音乐下载的统计
2018/06/20 Python
python 地图经纬度转换、纠偏的实例代码
2018/08/06 Python
python模块hashlib(加密服务)知识点讲解
2019/11/25 Python
使用遗传算法求二元函数的最小值
2020/02/11 Python
python实现全排列代码(回溯、深度优先搜索)
2020/02/26 Python
Python多线程threading创建及使用方法解析
2020/06/17 Python
北京-环亚运商测试题.net程序员初步测试题
2013/05/28 面试题
五一劳动节活动记录
2014/03/23 职场文书
英语教研活动总结
2014/07/02 职场文书
党性修养心得体会2016
2016/01/21 职场文书
创业计划书之蛋糕店
2019/08/29 职场文书
公文写作:工伤事故分析报告怎么写?
2019/11/05 职场文书
php 解析非标准json、非规范json
2021/04/01 PHP
生命的关键成分来自太空?陨石说是的
2022/04/29 数码科技
Python保存并浏览用户的历史记录
2022/04/29 Python