php设计模式(二十一):备忘录模式(Memento)

2023-05-24 14:28:24 浏览数 (1)

备忘录模式

备忘录模式又称为快照、Snapshot、Memento,备忘录模式是一种行为设计模式,允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

问题

很多时候我们是按照产品的需求,对代码多次改版,可能因为某次疏忽(未保存就关机了),就导致代码不翼而飞,这会使我们非常懊恼,少则加班几小时,多则几个通宵。如果有”后悔药”可吃,那一定不会说:“曾经有一份写好的代码摆在我的面前我没有好好去保存,等我失去的时候才追悔莫及,人间最痛苦的事莫过于此,如果上天能给我一次再来一次的机会,我一定狂按 Ctrl s”。

上述情况非常常见,而不常见的却是没有“后悔药”可吃。那我们在编程中有没有“后悔药”呢?如开发一款游戏,我们打 BOSS 失败之后,想要吃“后悔药”回到打 BOSS 之前的状态。

解决方法

在编程中我们通过一个备忘录类(Memento)专门存储对象状态,我们可以提供历史类(History)存储大量的备忘录对象,当用户类触发撤销操作时,历史类将从栈中取回最近的备忘录,并将其传递给使用类以请求进行回滚。

也就是说我们打 BOSS 之前我们保存一个状态,我们打 BOSS 失败之后,使用备忘录类回滚到 BOSS 之前的状态。

结构

Origin:原型类 需要创建快照的类 Snapshot:快照类 存原型快照的地方 History:历史类 存储的多个快照 以便能回滚和撤销

代码示例

原型类

代码语言:javascript复制
/**
 * 原型类
 */
class Origin
{

    /**
     * 血量值
     * @var
     */
    private $hp;

    /**
     * 体力值
     * @var
     */
    private $lp;

    /**
     * 力量
     * @var
     */
    private $power;

    public function __construct()
    {
        // 初始化时 HP:100、LP:100、power:50
        $this->hp = 100;
        $this->lp = 100;
        $this->power = 50;
    }

    /**
     * 设置 HP
     * @param $hp
     * @return void
     * @author chendashengpc
     */
    public function setHp($hp)
    {
        $this->hp = $hp;
    }

    /**
     * 设置 LP
     * @param $lp
     * @return void
     * @author chendashengpc
     */
    public function setLp($lp)
    {
        $this->lp = $lp;
    }

    /**
     * 设置 Power
     * @param $power
     * @return void
     * @author chendashengpc
     */
    public function setPower($power)
    {
        $this->power = $power;
    }

    /**
     * 制作备份
     * @return void
     * @author chendashengpc
     */
    public function makeSnapshot()
    {
        return new Snapshot($this->hp, $this->lp, $this->power);
    }

    /**
     * 回滚备份
     * @return void
     * @author chendashengpc
     */
    public function restore(Snapshot $snapshot)
    {
        $this->hp = $snapshot->getHp();
        $this->lp = $snapshot->getLp();
        $this->power = $snapshot->getPower();
    }

    /**
     * 魔术方法设置字符串
     * @return string
     * @author chendashengpc
     */
    public function __toString()
    {
        return 'HP:' . $this->hp . PHP_EOL . 'LP:' . $this->lp . PHP_EOL . 'POWERL:' . $this->power . PHP_EOL;
    }
}

快照类

代码语言:javascript复制
/**
 * 快照
 */
class Snapshot
{

    /**
     * 血量值
     * @var
     */
    private $hp;

    /**
     * 体力值
     * @var
     */
    private $lp;

    /**
     * 力量
     * @var
     */
    private $power;

    /**
     * 初始化备忘录
     * @param $hp
     * @param $lp
     * @param $power
     */
    public function __construct($hp, $lp, $power)
    {
        $this->hp = $hp;
        $this->lp = $lp;
        $this->power = $power;
    }

    /**
     * 获取备忘录HP
     * @return mixed
     * @author chendashengpc
     */
    public function getHp()
    {
        return $this->hp;
    }

    /**
     * 获取备忘录LP
     * @return mixed
     * @author chendashengpc
     */
    public function getLp()
    {
        return $this->hp;
    }
    /**
     * 获取备忘录POWER
     * @return mixed
     * @author chendashengpc
     */
    public function getPower()
    {
        return $this->power;
    }

}

历史类

代码语言:javascript复制
/**
 * 存历史类
 */
class History
{
    public $list = [

    ];

    /**
     * 设置快照
     * @param Snapshot $snapshot
     * @return void
     * @author chendashengpc
     */
    public function makeBackup(Snapshot $snapshot)
    {
        $this->list[] = $snapshot;
    }

    /**
     * 返回备忘录
     * @return mixed|null
     * @author chendashengpc
     */
    public function undo()
    {
        if (count($this->list)) {
            $snapshot = array_pop($this->list);
            return $snapshot;
        }
        return null;
    }

}

客户端使用

代码语言:javascript复制
/**
 * 初始化
 */
$origin = new Origin();
echo $origin . PHP_EOL;
/**
 * 提升力量
 */
$origin->setHp(200);
$origin->setLp(200);;
$origin->setPower(100);
echo $origin . PHP_EOL;

/**
 * 存进版本库
 */
$history = new History();
/**
 * 设置快照
 */
$history->makeBackup($origin->makeSnapshot());

$origin->setHp(120);
$origin->setLp(120);;
$origin->setPower(60);
echo $origin . PHP_EOL;
/**
 * 回滚
 */
$snapshot = $history->undo();
$origin->restore($snapshot);
echo $origin . PHP_EOL;

输出

代码语言:javascript复制
HP:100
LP:100
POWERL:50

HP:200
LP:200
POWERL:100

HP:120
LP:120
POWERL:60

HP:200
LP:200
POWERL:100

UML

优缺点

优点

  • 可以在不破坏对象封装情况的前提下创建对象状态快照。
  • 可以通过让负责人维护原发器状态历史记录来简化原发器代码。

缺点

  • 如果客户端过于频繁地创建备忘录, 程序将消耗大量内存。
  • 负责人必须完整跟踪原发器的生命周期, 这样才能销毁弃用的备忘录。
  • 绝大部分动态编程语言 (例如 PHP、 Python 和 JavaScript)不能确保备忘录中的状态不被修改。

0 人点赞