首頁 >後端開發 >php教程 >PHP原型模式案例解析

PHP原型模式案例解析

php中世界最好的语言
php中世界最好的语言原創
2018-05-17 11:42:152218瀏覽

這次帶給大家PHP原型模式案例解析,使用PHP原型模式的注意事項有哪些,以下就是實戰案例,一起來看一下。

原型設計模式(Prototype Design Pattern)很有趣, 因為它使用了一種克隆技術來複製實例化的物件. 新物件是透過複製原型實例來建立的. 在這裡, 實例是批次實例化的具體類別.原型設計模式的目的是透過使用克隆來減少實例化物件的開銷.與其從一個類別實例化新物件, 完全可以使用一個已有實例的克隆.

克隆函數

PHP中使用原型設計模式的關鍵是要了解如何使用內建函數clone().

<?php
abstract class CloneObject
{
  public $name;
  public $picture;
  abstract function clone();
}
class Boy extends CloneObject
{
  public function construct()
  {
    $this->face = "handsome";
    $this->name = "chenqionghe";
  }
  public function display()
  {
    echo &#39;look : &#39;.$this->face;;
    echo &#39;<br />&#39;.$this->name.&#39;<br />&#39;;
  }
  public function clone() {}
}
$boy = new Boy();
$boy->display();
$cloneBoy = clone $boy;
$cloneBoy->face = "still handsome";
$cloneBoy->display();

運行結果如下

look : handsome
chenqionghe
look : still handsome
chenqionghe

$cloneBoy實例是透過克隆Boy的實例$boy, 它可以像$boy一樣存取相同的屬性, 而且像Boy類別的直接實例一樣改變這些屬性.

##注意: 對於所複製的實例, clone關鍵字會為該實例的類別實例化另一個實例(使用克隆關鍵字可以建立一個類別的副本, 如果可能, 會自動呼叫物件的clone方法, 但不能直接呼叫物件的clone方法), 關於過程, 有一點要注意的是, 複製不會不會啟動建構子中的動作.

簡單的原型範例

我們以研究果蠅為例.研究的目標是建立一個原型果蠅, 然後一旦出現變異, 就建構這個變異果蠅

抽象類別介面和具體實作

原型(IPrototype)的兩個具體類別實作分別表示不同性別的果蠅, 包括性別變數(gender)和不同性別的和行為.

IPrototype.php

<?php
abstract class IPrototype
{
  public $eyeColor;
  public $winBeat;
  public $unitEypes;
  abstract function clone();
}

IPrototype的這兩個實現的區別體現在性別上, 性別用常數標識, 一個是MALE,另一個是FEMAIL.雄果蠅有一個$mated布爾變數, 雄果蠅交配後,這個布爾變數會設定為true, 雌果蠅有一個$fecundity變數,其中包含一個數字值, 表示這隻雄果蠅的繁殖能力(產卵數):

MaleProto.php

<?php
include_once(&#39;IPrototype.php&#39;);
class MaleProto extends IPrototype
{
  const gender = "MALE";
  public $mated;
  public function construct()
  {
    $this->eyeColor = "red";
    $this->winBeat = "220";
    $this->unitEypes = "760";
  }
  public function clone(){}
}

FemaleProto.php

<?php
include_once(&#39;IPrototype.php&#39;);
class FemaleProto extends IPrototype
{
  const gender = "FEMAIL";
  public $fecundity;
  public function construct()
  {
    $this->eyeColor = "red";
    $this->winBeat = "220";
    $this->unitEypes = "760";
  }
  public function clone(){}
}

#客戶

在原型設計模式中,Clien類別確實是一個不可缺少的部分.原因在於, 儘管要​​將子類別具體實現作為實例的模板,但使用相同的模板克隆實例的具體工作是由Client類別完成的

Client.php

<?php
function autoload($class_name)
{
  include_once $class_name.&#39;.php&#39;;
}
class Client
{
  //用于直接实例化
  private $fly1;
  private $fly2;
  //用于克隆
  private $c1Fly;
  private $c2Fly;
  private $updatedCloneFly;
  public function construct()
  {
    //实例化
    $this->fly1 = new MaleProto();
    $this->fly2 = new FemaleProto();
    //克隆
    $this->c1Fly = clone $this->fly1;
    $this->c2Fly = clone $this->fly2;
    $this->updatedCloneFly = clone $this->fly2;
    //更新克隆
    $this->c1Fly->mated = "true";
    $this->c2Fly->fecundity = &#39;186&#39;;
    $this->updatedCloneFly->eyeColor = "purple";
    $this->updatedCloneFly->winBeat = "220";
    $this->updatedCloneFly->unitEyes = &#39;750&#39;;
    $this->updatedCloneFly->fecundity = &#39;92&#39;;
    //通过类型提示方法发送
    $this->showFly($this->c1Fly);
    $this->showFly($this->c2Fly);
    $this->showFly($this->updatedCloneFly);
  }
  private function showFly(IPrototype $fly)
  {
    echo "Eye color: ".$fly->eyeColor."<br />";
    echo "Wing Beats/second: ".$fly->winBeat."<br />";
    echo "Eye units: ".$fly->unitEypes."<br />";
    $genderNow = $fly::gender;
    echo "Gender: ".$genderNow."<br />";
    if($genderNow == "FEMAIL")
    {
      echo "Number of eggs: ".$fly->fecundity."<hr />";
    }
    else
    {
      echo "Mated: ".$fly->mated."<hr />";
    }
  }
}
$worker = new Client();

運行結果如下

Eye color: red

Wing Beats/second : 220
Eye units: 760
Gender: MALE
Mated: trueEye color: red
Wing Beats/second: 220
Eye units: 760
Gender: FEMAIL
Number of eggs: 186Eye color: purple
Wing Beats/second: 220
Eye units: 760
Gender: FEMAIL
Number of eggs: 92

原型模式要依賴客戶透過不念克隆過程使用具體原型.在這個設計過程中, 客戶是完成克隆的參與者, 由於克隆是原型設計中的關鍵要素, 所以客戶是一個基本參與者, 而不僅僅是一個請求類.

現代企業組織

在創建型設計模式方面,現代企業組織就非常適合原型實現.現在企業組織往往是複雜而龐大的層次結構, 像物件導向程式設計一樣,要為很多共同的特徵建模.現在透過一個例子描述軟體工程公司.

軟體工程公司是一個典型的現代組織.工程部(Engineering Department)負責創建產品,管理部(Management)處理資源的協調組織,行銷部(Marketing)負責產品的銷售,推廣和整體行銷.

##介面中的封裝

#

在这个原型实现中,首先为程序的接口(一个抽象类)增加OOP,与所有原型接口一样,这个接口包含一个克隆操作.另外它还包含一些抽象和具体的获取方法和设置方法.其中有一个抽象获取方法/设置方法对,但要由3个具体原型实现为这个抽象获取方法/设置方法对提供具体实现.其他获取方法和设置方法分分别应用于员工名,ID码和照片等属性.注意所有这些属性都是保护属性(protected),所以尽管具体的获取方法和设置方法有公共可见性,但由于操作中使用的属性具有保护和可见性,这就提供了某种程度的封装:

<?php
abstract class IAcmePrototype
{
  protected $name;
  protected $id;
  protected $employeePic;
  protected $department;
  //部门
  abstract function setDept($orgCode);
  abstract function getDept();
  //名字
  public function setName($emName)
  {
    $this->name = $emName;
  }
  public function getName()
  {
    return $this->name;
  }
  //ID
  public function setId($emId)
  {
    $this->id = $emId;
  }
  public function getId()
  {
    return $this->id;
  }
  //雇员图像
  public function setPic($ePic)
  {
    $this->employeePic = $ePic;
  }
  public function getPic()
  {
    return $this->employeePic;
  }
  abstract function clone();
}

利用这些获取方法和设置方法, 所有属性的值都通过继承的保护变量来设置.采用这种设计, 扩展类及其实例可以得到更好的封装.

接口实现

3个IAcmePrototype子类都必须实现"dept"抽象方法以及clone()方法.类似地, 每个具体原型类还包含一个常量UNIT,它提供一个赋值,可以由实例(包括克隆的对象)作为标识

首先来看市场部的Marketing类:

Marketing.php

<?php
include_once(&#39;IAcmePrototype.php&#39;);
class Marketing extends IAcmePrototype
{
  const UNIT = "Marketing";
  private $sales = "sales";
  private $promotion = "promotion";
  private $strategic = "strategic planning";
  public function setDept($orgCode)
  {
    switch($orgCode)
    {
      case 101:
        $this->department = $this->sales;
        break;
      case 102:
        $this->department = $this->promotion;
        break;
      case 103:
        $this->department = $this->strategic;
        break;
      default :
        $this->department = "未识别的市场部";
    }
  }
  public function getDept()
  {
    return $this->department;
  }
  public function clone() {}
}

setDept()方法的实现使用了一个参数.并不是直接输入市场部的部门,这个方法的参数是一个数字码, 通过一个switch语句,限制了3种可接受的情况和默认情况,别外两个原型实现也类似

Management.php

<?php
include_once(&#39;IAcmePrototype.php&#39;);
class Management extends IAcmePrototype
{
  const UNIT = "Management";
  private $research = "research";
  private $plan = "planning";
  private $operations = "operations";
  public function setDept($orgCode)
  {
    switch($orgCode)
    {
      case 201:
        $this->department = $this->research;
        break;
      case 202:
        $this->department = $this->plan;
        break;
      case 203:
        $this->department = $this->operations;
        break;
      default :
        $this->department = "未识别的管理部";
    }
  }
  public function getDept()
  {
    return $this->department;
  }
  public function clone() {}
}

Engineering.php

<?php
include_once(&#39;IAcmePrototype.php&#39;);
class Engineering extends IAcmePrototype
{
  const UNIT = "Engineering";
  private $development = "development";
  private $design = "design";
  private $sysAd = "system administration";
  public function setDept($orgCode)
  {
    switch($orgCode)
    {
      case 301:
        $this->department = $this->development;
        break;
      case 302:
        $this->department = $this->design;
        break;
      case 303:
        $this->department = $this->sysAd;
        break;
      default :
        $this->department = "未识别的工程部";
    }
  }
  public function getDept()
  {
    return $this->department;
  }
  public function clone() {}
}

以上这3个具体原型实现分别有其特定用途,不过它们都符合接口,可以创建各个原型实现的一个实例, 然后根据需要克隆多个实例.这个克隆的工作由Client类完成

客户

客户的设置非常简单: 分别创建各个具体原型的一个实例, 然后按以下列表来克隆各个实例:

市场部门实例:
-----市场部门克隆
-----市场部门克隆
管理部门实例
-----管理部门克隆
工程部门实例
-----工程部门克隆
-----工程部门克隆

将来只使用这些克隆对象.使用获取方法和设置方法将各个特定情况的信息赋给这些克隆对象.以下是Client的实现

Client.php

<?php
function autoload($class_name)
{
  include_once $class_name.&#39;.php&#39;;
}
class Client
{
  private $market;
  private $manage;
  private $engineer;
  public function construct()
  {
    $this->makeConProto();
    $Tess = clone $this->market;
    $this->setEmployee($Tess, "Tess Smith" , 101, &#39;ts101-1234&#39;, &#39;tess.png&#39;);
    $this->showEmployee($Tess);
    $Jacob = clone $this->market;
    $this->setEmployee($Jacob, "Jacob Jones" , 101, &#39;jj101-2234&#39;, &#39;jacob.png&#39;);
    $this->showEmployee($Jacob);
    $Ricky = clone $this->manage;
    $this->setEmployee($Ricky, "Ricky Rodriguez" , 203, &#39;rr101-5634&#39;, &#39;ricky.png&#39;);
    $this->showEmployee($Ricky);
    $Olivaia = clone $this->engineer;
    $this->setEmployee($Olivaia, "Olivaia Perez" , 302, &#39;op301-1278&#39;, &#39;olivia.png&#39;);
    $this->showEmployee($Olivaia);
    $John = clone $this->engineer;
    $this->setEmployee($John, "John Jacson" , 101, &#39;jj301-14548&#39;, &#39;john.png&#39;);
    $this->showEmployee($John);
  }
  private function makeConProto()
  {
    $this->market = new Marketing();
    $this->manage = new Management();
    $this->engineer = new Engineering();
  }
  private function showEmployee(IAcmePrototype $employeeNow)
  {
    $px = $employeeNow->getPic();
    echo "<img src=$px width=&#39;150&#39; height=&#39;150&#39; /><br />";
    echo $employeeNow->getName().&#39;<br />&#39;;
    echo $employeeNow->getDept().&#39;:&#39;.$employeeNow::UNIT.&#39;<br />&#39;;
    echo $employeeNow->getId().&#39;<hr />&#39;;
  }
  private function setEmployee(IAcmePrototype $employeeNow, $nm, $dp, $id, $px)
  {
    $employeeNow->setName($nm);
    $employeeNow->setDept($dp);
    $employeeNow->setId($id);
    $employeeNow->setPic($px);
  }
}
$worker = new Client();

解释:

客户Client的构造函数类包含3个私有属性, 用来分别实例化3个具体原型类. makeConPro()方法生成必要的实例.

接下来,使用克隆技术创建一个"员工"实例.然后,这个实例向一个设置方法setEmployee()发送特定的实例信息,这个设置方法使用IAcmePrototype接口类型提示,不过需要说明, 它只对第一个参数使用类型提示,其他参数都没有类型提示, 并不要求它们派生自IAcmePrototype接口.克隆"员工"可以使用IAcmePrototype抽象类的所有设置方法以及具体原型类实现的setDept()方法.

要使用各个员工的数据,Client类可以使用继承的获取方法.以下是运行Client输出的结果

Tess Smith
sales:Marketing
ts101-1234
Jacob Jones
sales:Marketing
jj101-2234
Ricky Rodriguez
operations:Management
rr101-5634
Olivaia Perez
design:Engineering
op301-1278
John Jacson
未识别的工程部:Engineering
jj301-14548

可以根据需要增加更多的克隆, 而且只需要对具体原型类完成一次实例化.使用原型模式时, 并不是建立具体类的多个实例,而只需要一个类实例化和多个克隆.

完成修改,增加特性

要记住,最重要(可能也是最基本)的是, 设计模式允许开发人员修改和增补程序,而不必一切从头开始.例如, 假设总裁决定公司增加一个新的部门,比如研究部门(Research), 这会很难吗?一点也不难.Research可以扩展IAcmePrototype抽象类, 然后实现抽象获取方法和设置方法来反映这个研究部门的组织.需要说明,Client类中获取方法和设置方法使用的代码提示指示一个接口,而不是一个抽象类的具体实现.所以, 只要增加的单元正确地实现了这个接口,就能顺利地增加到应用中, 而不会带来波动,也不需要对程序中的其他参与者进行重构.

不僅可以增加更多的具體類, 還可以很容易地對各個類進行修改, 而不會造成破壞.例如假設這個組織的市場部決定,除了現有的部門外, 他們還需要一個特殊的線上市場部,. 這樣一來, switch/case操作需要一個新的分支(case), 還要有一個新的私有屬性(變數)來描述新增的這個部門.這個改變將封凍在單獨的類中, 而不會影響其他參與者.由於這種改變不會帶來破壞, 所以應用的規模越大, 這一點就越重要.可以看到原型設計模式不僅支持一致性, 還支持靈活的改變.

PHP世界中的原型

由於PHP是伺服器端語言,也是與MySQL資料庫互動的重要工具,所以原型設計模式尤其適用.並不是為資料庫的第一個元素分別創建物件,PHP可以使用原型模式創建具體類別的一個實例,然後利用資料庫中的資料克隆其餘的實例(記錄).

了解克隆過程之後,與直接實例化的過程相比,你可能會問:"這有什麼區別?" 換句話說,為什麼克隆比直接實例化對象需要的資源少?它們的區別並不能直接看到.一個物件透過複製建立實例時, 它不會啟動建構子.複製能得到原始類別的所有屬性, 甚至還包含父介面的屬性,另外也繼承了傳遞實例化物件的所有值.建構函式產生的所有值以及儲存在物件屬性中的值都會成為克隆物件的一部分.所以沒有傳回建構子.如果發現你的複製物件確實需要存取建構函式產生的值但又無法存取, 這說明需要對類別進行重構,使實例能擁有它們需要的一切資訊, 而且可以把這些資料傳遞給克隆物件.

總的來說, 原型模式在很多不同類型的PHP專案中都很適用, 如果解決一個問題需要乃至創建型模式, 都可以使用原型模式.

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

PHP封閉原則(OCP)使用案例解析

PHP依賴倒置案例詳解

以上是PHP原型模式案例解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
上一篇:PHP中遞歸詳解下一篇:PHP中遞歸詳解