PHP設(shè)計(jì)模式(四)原型模式Prototype實(shí)例詳解【創(chuàng)建型】
本文實(shí)例講述了PHP設(shè)計(jì)模式:原型模式Prototype。分享給大家供大家參考,具體如下:
1. 概述
我們都知道,創(chuàng)建型模式一般是用來創(chuàng)建一個(gè)新的對(duì)象,然后我們使用這個(gè)對(duì)象完成一些對(duì)象的操作,我們通過原型模式可以快速的創(chuàng)建一個(gè)對(duì)象而不需要提供專門的new()操作就可以快速完成對(duì)象的創(chuàng)建,這無疑是一種非常有效的方式,快速的創(chuàng)建一個(gè)新的對(duì)象。
例子1:孫悟空拔下一嘬猴毛,輕輕一吹就會(huì)變出好多的孫悟空來。
例子2:寄個(gè)快遞
下面是一個(gè)郵寄快遞的場(chǎng)景:
“給我寄個(gè)快遞?!鳖櫩驼f。
“寄往什么地方?寄給……?”你問。
“和上次差不多一樣,只是郵寄給另外一個(gè)地址,這里是郵寄地址……”顧客一邊說一邊把寫有郵寄地址的紙條給你。
“好!”你愉快地答應(yīng),因?yàn)槟惚4媪擞脩舻囊郧班]寄信息,只要復(fù)制這些數(shù)據(jù),然后通過簡(jiǎn)單的修改就可以快速地創(chuàng)建新的快遞數(shù)據(jù)了。
2. 問題
當(dāng)對(duì)象的構(gòu)造函數(shù)非常復(fù)雜,在生成新對(duì)象的時(shí)候非常耗時(shí)間、耗資源的情況?我們是怎么來創(chuàng)建呢?
3. 解決方案
通過復(fù)制(克隆、拷貝)一個(gè)指定類型的對(duì)象來創(chuàng)建更多同類型的對(duì)象。這個(gè)指定的對(duì)象可被稱為“原型”對(duì)象,也就是通過復(fù)制原型對(duì)象來得到更多同類型的對(duì)象。即原型設(shè)計(jì)模式。在php的很多模板庫,都用到clone。如smarty等。
4. 適用性
原型模式的主要思想是基于現(xiàn)有的對(duì)象克隆一個(gè)新的對(duì)象出來,一般是有對(duì)象的內(nèi)部提供克隆的方法,通過該方法返回一個(gè)對(duì)象的副本,這種創(chuàng)建對(duì)象的方式,相比我們之前說的幾類創(chuàng)建型模式還是有區(qū)別的,之前的講述的工廠模式與抽象工廠都是通過工廠封裝具體的new操作的過程,返回一個(gè)新的對(duì)象,有的時(shí)候我們通過這樣的創(chuàng)建工廠創(chuàng)建對(duì)象不值得,特別是以下的幾個(gè)場(chǎng)景的時(shí)候,可能使用原型模式更簡(jiǎn)單也效率更高。
• 1)當(dāng)一個(gè)系統(tǒng)應(yīng)該獨(dú)立于它的產(chǎn)品創(chuàng)建、構(gòu)成和表示時(shí),要使用 Prototype模式
• 2)當(dāng)要實(shí)例化的類是在運(yùn)行時(shí)刻指定時(shí),例如,通過動(dòng)態(tài)裝載;
• 3)為了避免創(chuàng)建一個(gè)與產(chǎn)品類層次平行的工廠類層次時(shí)
• 4)當(dāng)一個(gè)類的實(shí)例只能有幾個(gè)不同狀態(tài)組合中的一種時(shí)。建立相應(yīng)數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實(shí)例化該類更方便一些。(也就是當(dāng)我們?cè)谔幚硪恍?duì)象比較簡(jiǎn)單,并且對(duì)象之間的區(qū)別很小,可能只是很固定的幾個(gè)屬性不同的時(shí)候,可能我們使用原型模式更合適)。
5. 結(jié)構(gòu)
原型模式結(jié)構(gòu)如下頁上圖所示:
6. 組成
客戶(Client)角色:使用原型對(duì)象的客戶程序
抽象原型(Prototype)角色:規(guī)定了具體原型對(duì)象必須實(shí)現(xiàn)的接口(如果要提供深拷貝,則必須具有實(shí)現(xiàn)clone的規(guī)定)
具體原型(ConcretePrototype):從抽象原型派生而來,是客戶程序使用的對(duì)象,即被復(fù)制的對(duì)象。此角色需要實(shí)現(xiàn)抽象原型角色所要求的接口。
7. 效果
Prototype模式有許多和Abstract Factory模式 和 Builder模式一樣的效果:它對(duì)客戶隱藏了具體的產(chǎn)品類,因此減少了客戶知道的名字的數(shù)目。此外,這些模式使客戶無需改變即可使用與特定應(yīng)用相關(guān)的類。
下面列出Prototype模式的另外一些優(yōu)點(diǎn)。
1 ) 運(yùn)行時(shí)刻增加和刪除產(chǎn)品: Prototype允許只通過客戶注冊(cè)原型實(shí)例就可以將一個(gè)新的具體產(chǎn)品類并入系統(tǒng)。它比其他創(chuàng)建型模式更為靈活,因?yàn)榭蛻艨梢栽谶\(yùn)行時(shí)刻建立和刪除原型。
2 ) 改變值以指定新對(duì)象: 高度動(dòng)態(tài)的系統(tǒng)允許你通過對(duì)象復(fù)合定義新的行為—例如,通過為一個(gè)對(duì)象變量指定值—并且不定義新的類。你通過實(shí)例化已有類并且將這些實(shí)例注冊(cè)為客戶對(duì)象的原型,就可以有效定義新類別的對(duì)象??蛻艨梢詫⒙氊?zé)代理給原型,從而表現(xiàn)出新的行為。這種設(shè)計(jì)使得用戶無需編程即可定義新“類” 。實(shí)際上,克隆一個(gè)原型類似于實(shí)例化一個(gè)類。Prototype模式可以極大的減少系統(tǒng)所需要的類的數(shù)目。
3) 改變結(jié)構(gòu)以指定新對(duì)象:許多應(yīng)用由部件和子部件來創(chuàng)建對(duì)象。
4) 減少子類的構(gòu)造 Factory Method 經(jīng)常產(chǎn)生一個(gè)與產(chǎn)品類層次平行的 Creator類層次。Prototype模式使得你克隆一個(gè)原型而不是請(qǐng)求一個(gè)工廠方法去產(chǎn)生一個(gè)新的對(duì)象。因此你根本不需要Creator類層次。這一優(yōu)點(diǎn)主要適用于像 C + +這樣不將類作為一級(jí)類對(duì)象的語言。像Smalltalk和Objective C這樣的語言從中獲益較少,因?yàn)槟憧偸强梢杂靡粋€(gè)類對(duì)象作為生成者。在這些語言中,類對(duì)象已經(jīng)起到原型一樣的作用了。
5) 用類動(dòng)態(tài)配置應(yīng)用 一些運(yùn)行時(shí)刻環(huán)境允許你動(dòng)態(tài)將類裝載到應(yīng)用中。在像 C + +這樣的語言中,Prototype模式是利用這種功能的關(guān)鍵。一個(gè)希望創(chuàng)建動(dòng)態(tài)載入類的實(shí)例的應(yīng)用不能靜態(tài)引用類的構(gòu)造器。而應(yīng)該由運(yùn)行環(huán)境在載入時(shí)自動(dòng)創(chuàng)建每個(gè)類的實(shí)例,并用原型管理器來注冊(cè)這個(gè)實(shí)例(參見實(shí)現(xiàn)一節(jié)) 。這樣應(yīng)用就可以向原型管理器請(qǐng)求新裝載的類的實(shí)例,這些類原本并沒有和程序相連接。 E T + +應(yīng)用框架[ W G M 8 8 ]有一個(gè)運(yùn)行系統(tǒng)就是使用這一方案的。
Prototype的主要缺陷是每一個(gè)Prototype的子類都必須實(shí)現(xiàn)clone操作,這可能很困難。
例如,當(dāng)所考慮的類已經(jīng)存在時(shí)就難以新增 clone操作。當(dāng)內(nèi)部包括一些不支持拷貝或有循環(huán)引用的對(duì)象時(shí),實(shí)現(xiàn)克隆可能也會(huì)很困難的。
8. 實(shí)現(xiàn)
<?php /** * 原型模式 */ /** * 抽象原型角色 */ interface Prototype { public function copy(); } /** * 具體原型角色 */ class ConcretePrototype implements Prototype{ private $_name; public function __construct($name) { $this->_name = $name; } public function setName($name) { $this->_name = $name; } public function getName() { return $this->_name; } public function copy() { /** 深拷貝 */ return clone $this; /** 淺拷貝 */ //return $this; } } class Client { /** * Main program. */ public static function main() { $object1 = new ConcretePrototype(11); $object_copy = $object1->copy(); var_dump($object1->getName()); echo '<br />'; var_dump($object_copy->getName()); echo '<br />'; $object1->setName(22); var_dump($object1->getName()); echo '<br />'; var_dump($object_copy->getName()); echo '<br />'; } } Client::main(); ?>
9. 淺拷貝和深拷貝
原型模式的原理圖:
淺拷貝
被拷貝對(duì)象的所有變量都含有與原對(duì)象相同的值,而且對(duì)其他對(duì)象的引用仍然是指向原來的對(duì)象。即淺拷貝只負(fù)責(zé)當(dāng)前對(duì)象實(shí)例,對(duì)引用的對(duì)象不做拷貝。
淺復(fù)制后的對(duì)象和對(duì)象副本的情況:
深拷貝
被拷貝對(duì)象的所有的變量都含有與原來對(duì)象相同的值,除了那些引用其他對(duì)象的變量。那些引用其他對(duì)象的變量將指向一個(gè)被拷貝的新對(duì)象,而不再是原有那些被引用對(duì)象。即 深拷貝把要拷貝的對(duì)象所引用的對(duì)象也都拷貝了一次,而這種對(duì)被引用到的對(duì)象拷貝叫做間接拷貝。
深復(fù)制的對(duì)象和對(duì)象副本的情況:
深拷貝要深入到多少層,是一個(gè)不確定的問題。
在決定以深拷貝的方式拷貝一個(gè)對(duì)象的時(shí)候,必須決定對(duì)間接拷貝的對(duì)象是采取淺拷貝還是深拷貝還是繼續(xù)采用深拷貝。
因此,在采取深拷貝時(shí),需要決定多深才算深。此外,在深拷貝的過程中,很可能會(huì)出現(xiàn)循環(huán)引用的問題。
10. 帶Prototype Manager的原型模式
原型模式的第二種形式是帶原型管理器的原型模式,其UML圖如下:
原型管理器(Prototype Manager)角色:創(chuàng)建具體原型類的對(duì)象,并記錄每一個(gè)被創(chuàng)建的對(duì)象。
下面這個(gè)例子演示了在原型管理器中存儲(chǔ)用戶預(yù)先定義的顏色原型,客戶通過原型管理器克隆顏色對(duì)象。
<?php /** * abstract Prototype * */ abstract class ColorPrototype { //Methods abstract function copy(); } /** * Concrete Prototype * */ class Color extends ColorPrototype{ //Fields private $red; private $green; private $blue; //Constructors function __construct( $red, $green, $red) { $this->red = $red; $this->green = $green; $this->blue = $red; } /** * set red * * @param unknown_type $red */ public function setRed($red) { $this->red = $red; } /** * get red * */ public function getRed(){ return $this->red; } /** *set Green * * @param $green */ public function setGreen($green) { $this->green = $green; } /** * get Green * * @return unknown */ public function getGreen() { return $this->green ; } /** *set Blue * * @param $Blue */ public function setBlue($Blue) { $this->blue = $Blue; } /** * get Blue * * @return unknown */ public function getBlue() { return $this->blue ; } /** * Enter description here... * * @return unknown */ function copy(){ return clone $this; } function display() { echo $this->red , ',', $this->green, ',', $this->blue ,'<br>'; } } /** * Enter description here... * */ class ColorManager { // Fields static $colors = array(); // Indexers public static function add($name, $value){ self::$colors[$name] = $value; } public static function getCopy($name) { return self::$colors[$name]->copy(); } } /** *Client * */ class Client { public static function Main() { //原型:白色 ColorManager::add("white", new Color( 255, 0, 0 )); //紅色可以由原型白色對(duì)象得到,只是重新修改白色: r $red = ColorManager::getCopy('white'); $red->setRed(255); $red->display(); //綠色可以由原型白色對(duì)象得到,只是重新修改白色: g $green = ColorManager::getCopy('white'); $green->setGreen(255); $green->display(); //綠色可以由原型白色對(duì)象得到,只是重新修改白色: b $Blue = ColorManager::getCopy('white'); $Blue->setBlue(255); $Blue->display(); } } ini_set('display_errors', 'On'); error_reporting(E_ALL & ~ E_DEPRECATED); Client::Main(); ?>
更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》、《PHP數(shù)組(Array)操作技巧大全》、《PHP基本語法入門教程》、《PHP運(yùn)算與運(yùn)算符用法總結(jié)》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》
希望本文所述對(duì)大家PHP程序設(shè)計(jì)有所幫助。
- PHP設(shè)計(jì)模式之原型模式示例詳解
- PHP設(shè)計(jì)模式之命令模式示例詳解
- PHP設(shè)計(jì)模式(三)建造者模式Builder實(shí)例詳解【創(chuàng)建型】
- PHP設(shè)計(jì)模式(一)工廠模式Factory實(shí)例詳解【創(chuàng)建型】
- PHP設(shè)計(jì)模式概論【概念、分類、原則等】
- PHP設(shè)計(jì)模式之 策略模式Strategy詳解【對(duì)象行為型】
- PHP設(shè)計(jì)模式入門之狀態(tài)模式原理與實(shí)現(xiàn)方法分析
- PHP設(shè)計(jì)模式入門之迭代器模式原理與實(shí)現(xiàn)方法分析
- PHP設(shè)計(jì)模式之迭代器模式Iterator實(shí)例分析【對(duì)象行為型】
- php設(shè)計(jì)模式之適配器模式實(shí)例分析【星際爭(zhēng)霸游戲案例】
- php設(shè)計(jì)模式之迭代器模式實(shí)例分析【星際爭(zhēng)霸游戲案例】
- 詳解PHP八大設(shè)計(jì)模式
相關(guān)文章
laravel Validator ajax返回錯(cuò)誤信息的方法
今天小編就為大家分享一篇laravel Validator ajax返回錯(cuò)誤信息的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09總結(jié)PHP如何獲取當(dāng)前主機(jī)、域名、網(wǎng)址、路徑、端口和參數(shù)等
這篇文章給大家分享了利用php如何獲取當(dāng)前域名或主機(jī)地址、網(wǎng)頁地址、網(wǎng)址參數(shù)、用戶代理、完整的url、包含端口號(hào)的完整url以及只取路徑等信息,有需要的朋友們可以參考借鑒。2016-09-09php 使用mpdf實(shí)現(xiàn)指定字段配置字體樣式的方法
前兩天在做一個(gè)pdf導(dǎo)出功能,使用的插件是kartik-v/yii2-mpdf,此插件使用的是mpdf。接下來通過本文給大家介紹php 使用mpdf實(shí)現(xiàn)指定字段配置字體樣式的方法,需要的朋友可以參考下2019-07-07PHP安裝threads多線程擴(kuò)展基礎(chǔ)教程
php5.3或以上,且為線程安全版本。apache和php使用的編譯器必須一致,通過phpinfo()查看Thread Safety為enabled則為線程安全版,通過phpinfo()查看Compiler項(xiàng)可以知道使用的編譯器,本文給大家介紹PHP安裝threads多線程擴(kuò)展基礎(chǔ)教程,需要的朋友參考下2015-11-11php安裝擴(kuò)展mysqli的實(shí)現(xiàn)步驟及報(bào)錯(cuò)解決辦法
這篇文章主要介紹了 php安裝擴(kuò)展mysqli的實(shí)現(xiàn)步驟及報(bào)錯(cuò)解決辦法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09PHP 面向?qū)ο蟪绦蛟O(shè)計(jì)(oop)學(xué)習(xí)筆記 (四) - 異常處理類Exception
異常經(jīng)常被用來處理一些在程序正常執(zhí)行中遇到的各種類型的錯(cuò)誤。比如做數(shù)據(jù)庫鏈接時(shí),你就要處理數(shù)據(jù)庫連接失敗的情況。使用異??梢蕴岣呶覀兂绦虻娜蒎e(cuò)特性,從而使我們的應(yīng)用程序更加的穩(wěn)定和健壯。2014-06-06