首頁  >  文章  >  後端開發  >  一起聊聊PHP中的備忘錄模式

一起聊聊PHP中的備忘錄模式

青灯夜游
青灯夜游轉載
2021-07-20 19:25:152199瀏覽

在先前的文章《深入淺析PHP中的建造者模式》中我們介紹了PHP中的建造者模式,以下這篇文章帶大家了解一下PHP設計模式中的備忘錄模式。

一起聊聊PHP中的備忘錄模式

備忘錄,這個名字其實已經很形象的解釋了它的作用。典型的例子就是我們原來玩硬碟遊戲時的存檔功能。當你對即將面對的大BOSS有所顧慮時,一般都會先保存一次進度存檔。如果挑戰失敗了,直接讀取存檔就可以恢復到挑戰BOSS前的狀態,然後你就開開心心的再去練一會級回來解決這個大BOSS就好了。不過,為了以防萬一,在挑戰BOSS之前就先存檔總是好的。另外一個例子就是我們碼農們天天要用到的程式碼管理工具Git或Svn了。每次的提交都像是存檔備份,當新程式碼出現問題的時候,直接回溯恢復就行了。這些,都是備忘錄模式的典型應用,以下就一起來看看這個模式吧。

Gof類別圖及解釋

GoF定義:在不破壞封裝性的前提下,捕捉一個物件的內部狀態,並在該物件之外保存這個狀態。這樣以後就可將該物件還原到原先儲存的狀態

GoF類別圖:

一起聊聊PHP中的備忘錄模式

程式碼實作:

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

#實例

這次又回到簡訊發送的範例。通常我們做簡訊或郵件發送這些功能時,會有一個佇列從資料庫或快取中讀取要傳送的內容進行傳送,如果成功了就不管了,如果失敗了會將簡訊的狀態改成失敗或重發。在這裡,我們直接將它改回到之前未發送的狀態然後等待下次發送的佇列再次執行發送。

簡訊發送類別圖

一起聊聊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 = &#39;未发送&#39;;
        $this->time = time();
    }

    public function Show()
    {
        echo $this->to, &#39;---&#39;, $this->content, &#39;---&#39;, $this->time, &#39;---&#39;, $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(&#39;手机号&#39; . $i, &#39;内容&#39; . $i);
    echo &#39;初始状态:&#39;;
    $m->Show();

    // 保存初始信息
    $sc = new StateContainer();
    $sc->SetSaveState($m->CreateSaveSate());
    $scList[] = $sc;

    // 模拟短信发送,2发送成功,3发送失败
    $pushState = mt_rand(2, 3);
    $m->SetState($pushState == 2 ? &#39;发送成功&#39; : &#39;发送失败&#39;);
    echo &#39;发布后状态:&#39;;
    $m->Show();

    $mList[] = $m;
}

// 模拟另一个线程查找发送失败的并把它们还原到未发送状态
sleep(2);
foreach ($mList as $k => $m) {
    if ($m->GetState() == &#39;发送失败&#39;) { // 如果是发送失败的,还原状态
        $m->SetSaveState($scList[$k]->GetSaveState());
    }
    echo &#39;查询发布失败后状态:&#39;;
    $m->Show();
}

说明

  • 短信类做为我们的原发器,在发送前就保存了当前的发送状态
  • 随机模拟短信发送,只有两个状态,发送成功或者失败,并改变原发器的状态为成功或者失败
  • 模拟另一个线程或者脚本对短信的发送状态进行检查,如果发现有失败的,就将它重新改回未发送的状态
  • 这里我们只是保存了发送状态这一个字段,其他原发器的内部属性并没有保存
  • 真实的场景下我们应该会有一个重试次数的限制,当超过这个次数后,状态改为彻底的发送失败,不再进行重试了

原文地址:https://juejin.cn/post/6844903983555805192

作者:硬核项目经理

推荐学习:《PHP视频教程

以上是一起聊聊PHP中的備忘錄模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除