一起聊聊PHP中的備忘錄模式
在先前的文章《深入淺析PHP中的建造者模式》中我們介紹了PHP中的建造者模式,以下這篇文章帶大家了解一下PHP設計模式中的備忘錄模式。
備忘錄,這個名字其實已經很形象的解釋了它的作用。典型的例子就是我們原來玩硬碟遊戲時的存檔功能。當你對即將面對的大BOSS有所顧慮時,一般都會先保存一次進度存檔。如果挑戰失敗了,直接讀取存檔就可以恢復到挑戰BOSS前的狀態,然後你就開開心心的再去練一會級回來解決這個大BOSS就好了。不過,為了以防萬一,在挑戰BOSS之前就先存檔總是好的。另外一個例子就是我們碼農們天天要用到的程式碼管理工具Git或Svn了。每次的提交都像是存檔備份,當新程式碼出現問題的時候,直接回溯恢復就行了。這些,都是備忘錄模式的典型應用,以下就一起來看看這個模式吧。
Gof類別圖及解釋
GoF定義:在不破壞封裝性的前提下,捕捉一個物件的內部狀態,並在該物件之外保存這個狀態。這樣以後就可將該物件還原到原先儲存的狀態
GoF類別圖:
程式碼實作:
class Originator { private $state; public function SetMeneto(Memento $m) { $this->state = $m->GetState(); } public function CreateMemento() { $m = new Memento(); $m->SetState($this->state); return $m; } public function SetState($state) { $this->state = $state; } public function ShowState() { echo $this->state, PHP_EOL; } }
原發器,也可以叫做發起人。它有一個內部狀態(state),這個狀態可以在不同的情況下進行改變。當某一個事件發生時,需要將這個狀態恢復到原先的狀態。在這裡,我們有一個CreateMemento()用於建立一個備忘錄(存檔),有一個SetMeneto()用於還原狀態(讀檔)。
class Memento { private $state; public function SetState($state) { $this->state = $state; } public function GetState() { return $this->state; } }
備忘錄,非常簡單,就是用來記錄狀態。將這個狀態以物件的形式保存,就可以讓原發器非常方便地建立許多存檔用來記錄各種不同的狀態。
class Caretaker { private $memento; public function SetMemento($memento) { $this->memento = $memento; } public function GetMemento() { return $this->memento; } }
負責人,也叫做管理者類,保存備忘錄,當需要的時候從這裡取出備忘錄。它只負責保存,不能修改備忘錄。在複雜的應用中,可以將這裡做成列表,就像遊戲中可以選擇性的展現多條存檔記錄供玩家選擇。
$o = new Originator(); $o->SetState('状态1'); $o->ShowState(); // 保存状态 $c = new Caretaker(); $c->SetMemento($o->CreateMemento()); $o->SetState('状态2'); $o->ShowState(); // 还原状态 $o->SetMeneto($c->GetMemento()); $o->ShowState();
客戶端的呼叫中,我們的原發器初始化狀態後進行了保存,然後人為的更改了狀態。這時只需要透過負責人將狀態還原回來就可以了。
- 備忘錄模式說白了就是讓一個外部類別B來保存A的內部狀態,然後在適當的時候可以方便的還原這個狀態。
- 備忘錄模式的應用場景其實非常多,瀏覽器的回退、資料庫的備份還原、作業系統的備份還原、文件的撤銷重做、棋牌遊戲的悔棋等等
- 這個模式能夠保持原發器的封裝,也就是這些狀態需要對外部的物件隱藏,所以只能交給一個備忘錄物件來記錄
- 狀態在原發器和備忘錄之間的拷貝可能帶來效能問題,特別是大型物件的複雜繁多的內部狀態,而且也會帶來一些編碼方面的漏洞,例如遺漏某些狀態
##Mac的時光機功能大家有了解吧,可以將電腦恢復到某個時間點的狀態。其實windows的ghost也是類似的功能。我們的手機作業系統上也決定開發這樣的一個功能。當我們點擊時光機備份時,將手機上所有的資料、數據、狀態資訊都壓縮保存起來,如果用戶允許的話,我們將這個壓縮包上傳到我們的雲端伺服器上避免佔用用戶的手機內存,否則就只能儲存到用戶的手機內存了。當用戶的手機需要恢復到某個時間點,我們將所有的時光機備份列出,用戶只需要用手指輕輕一按就可以把手機系統狀態恢復到當時的樣子了,是不是非常方便! !
完整程式碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/17.memento/source/memento.php
#實例
這次又回到簡訊發送的範例。通常我們做簡訊或郵件發送這些功能時,會有一個佇列從資料庫或快取中讀取要傳送的內容進行傳送,如果成功了就不管了,如果失敗了會將簡訊的狀態改成失敗或重發。在這裡,我們直接將它改回到之前未發送的狀態然後等待下次發送的佇列再次執行發送。簡訊發送類別圖
#完整原始碼:https://github.com/zhangyue0503/designpatterns-php /blob/master/17.memento/source/memento-message.php
<?php class Message { private $content; private $to; private $state; private $time; public function __construct($to, $content) { $this->to = $to; $this->content = $content; $this->state = '未发送'; $this->time = time(); } public function Show() { echo $this->to, '---', $this->content, '---', $this->time, '---', $this->state, PHP_EOL; } public function CreateSaveSate() { $ss = new SaveState(); $ss->SetState($this->state); return $ss; } public function SetSaveState($ss) { if ($this->state != $ss->GetState()) { $this->time = time(); } $this->state = $ss->GetState(); } public function SetState($state) { $this->state = $state; } public function GetState() { return $this->state; } } class SaveState { private $state; public function SetState($state) { $this->state = $state; } public function GetState() { return $this->state; } } class StateContainer { private $ss; public function SetSaveState($ss) { $this->ss = $ss; } public function GetSaveState() { return $this->ss; } } // 模拟短信发送 $mList = []; $scList = []; for ($i = 0; $i < 10; $i++) { $m = new Message('手机号' . $i, '内容' . $i); echo '初始状态:'; $m->Show(); // 保存初始信息 $sc = new StateContainer(); $sc->SetSaveState($m->CreateSaveSate()); $scList[] = $sc; // 模拟短信发送,2发送成功,3发送失败 $pushState = mt_rand(2, 3); $m->SetState($pushState == 2 ? '发送成功' : '发送失败'); echo '发布后状态:'; $m->Show(); $mList[] = $m; } // 模拟另一个线程查找发送失败的并把它们还原到未发送状态 sleep(2); foreach ($mList as $k => $m) { if ($m->GetState() == '发送失败') { // 如果是发送失败的,还原状态 $m->SetSaveState($scList[$k]->GetSaveState()); } echo '查询发布失败后状态:'; $m->Show(); }
说明
- 短信类做为我们的原发器,在发送前就保存了当前的发送状态
- 随机模拟短信发送,只有两个状态,发送成功或者失败,并改变原发器的状态为成功或者失败
- 模拟另一个线程或者脚本对短信的发送状态进行检查,如果发现有失败的,就将它重新改回未发送的状态
- 这里我们只是保存了发送状态这一个字段,其他原发器的内部属性并没有保存
- 真实的场景下我们应该会有一个重试次数的限制,当超过这个次数后,状态改为彻底的发送失败,不再进行重试了
原文地址:https://juejin.cn/post/6844903983555805192
作者:硬核项目经理
推荐学习:《PHP视频教程》
以上是一起聊聊PHP中的備忘錄模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。
