JavaScript设计模式———备忘录模式

定义

备忘录模式,在不破坏对象封装性的前提下,在对象之外捕获并保存该对象的内部状态,以便日后在该对象使用时恢复到之前的某个状态。

问题引入

// 事件:下一页
$('#next_page_btn').click(function() {
    // 获取新闻列表容器
    var $news = $('#news_content');
    // 获取当前页号
    var page = $news.data('page');
    // 从服务端获取下一页的列表数据并显示
    getPageDataFromServer(page, function() {
        $news.data('page', page + 1);
    })
});
// 事件:上一页
$('#pre_page_btn').click(function() {
    // 获取新闻列表容器
    var $news = $('#news_content');
    // 获取当前页号
    var page = $news.data('page');
    // 从服务端获取上一页的列表数据并显示
    getPageDataFromServer(page, function() {
        $news.data('page', page - 1);
    });
});

这段代码即是我们常见的上一页、下一页的翻页功能。实现逻辑当然是没有问题的,但存在性能问题,尤其当在移动端时,当用户不断地切换上一页和下一页时,由于数据没有缓存,会不断地向服务端请求数据并渲染页面,从而导致糟糕的用户体验,并且给移动用户造成不必要的流量浪费。

事实上,我们可以使用备忘录模式来缓存已经请求过的数据,从而解决这个性能问题。当用户频繁切换上一页、下一页时,先从缓存中查找指定页的数据,如果不存在时再向服务端发起请求。如此便大大地提升了数据加载的速度,并且节省了用户的手机流量。

备忘录模式:新闻翻页缓存器

使用备忘录模式,定义一个新闻缓存器,用于把用户已经请求过的列表数据缓存起来,以供下次使用,以减少从服务端的频繁请求。代码实现如下:

// Page备忘录
var Page = function() {
    // 缓存器
    var cache = {};
    return function(page, fn) {
        if (cache[page]) {
            // 1-如果缓存中存在指定页的数据,直接使用
            showPage(page, cache[page]);
            fn && fn();
        } else {
            // 2-如果缓存中不存在指定页的数据,则从服务端获取,并将其缓存下来
            $.post('server_api_url', { page: page }, function(res) {
                if (res.success) {
                    showPage(page, res.data);
                    cache[page] = res.data;
                    fn && fn();
                } else {
                    // ajax error
                }
            });
        }
    }
}();

现在我们使用上述定义的新闻缓存器,来优化“上一页”“下一页”事件。在翻页事件中,我们先从缓存器中查询指定页的数据,若存在则直接读取并渲染页面;若不存在,则从服务端请求,并将其加入到缓存器中供下次使用。优化代码如下:

// 事件:下一页
$('#next_page_btn').click(function() {
  // 获取新闻列表容器
  var $news = $('#news_content');
  // 获取当前页号
  var page = $news.data('page');
  Page(page, function() {
    $news.data('page', page+1);
  });
});
// 事件:上一页
$('#pre_page_btn').click(function() {
  // 获取新闻列表容器
  var $news = $('#news_content');
  // 获取当前页号
  var page = $news.data('page');
  Page(page, function() {
    $news.data('page', page-1);
  });
});

例子:
使用js实现一个简单的状态机,用于状态的保存,回退。实现下备忘录模式。

var Memento = function(state){
  var _state = state;
  this.getState = function(){
      return _state;
  }
}

var Originator = function(){
  var _state;
  this.setState = function(state){
      _state = state;
  }
  this.getState = function(){
      return _state;
  }
  this.saveStateToMemento = function(){
      return new Memento(_state)
  }
  this.getStateFromMemento = function(memento){
      _state = memento.getState();
  }
}

var CareTaker = function(){
  var _mementoList = [];
  this.add = function(memento){
      _mementoList.push(memento);
  }
  this.get = function(index){
      return _mementoList[index];
  }
}

var originator = new Originator();
var careTaker = new CareTaker();
originator.setState("State 1");
originator.setState("State 2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State 3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State 4");

console.log("当前状态: " + originator.getState());
// 当前状态: State 4
originator.getStateFromMemento(careTaker.get(0));
console.log("恢复第一次保存状态: " + originator.getState());
// 恢复第一次保存状态: State 2
originator.getStateFromMemento(careTaker.get(1));
console.log("恢复第二次保存: " + originator.getState());
// 恢复第二次保存: State 3

这个例子里面Originator称为原发器,可以通过saveStateToMemento创建一个备忘录,存储当前状态。Memento是一个备忘录对象,只供原发器使用,提供状态提取方法。CareTaker称之为负责人也可以叫管理者,它负责保存备忘录,但是不能对备忘录内容进行操作或检查

总结

事实上,备忘录模式的应用是非常广泛的。比如当你打开页面中的换肤设置时,第一次打开时要向服务端请求若干数据,但是在第二次打开时就不需要再次请求了,我们只需要把第一次获取到的数据缓存下来即可。

再者,在MVC架构中,我们常常把数据缓存在 M 中,以供视图和控制器来使用。这些思想,都是基于备忘录模式来设计的。

优点

  • 提供了一种状态恢复的时间机制,使得用户可以方便的回退到一个特定的历史步骤。
  • 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。
    缺点:
  • 备忘录模式的主要缺点是资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免的需要占用大量的存储空间。

适用场景

  • 保存一个对象在某一时刻的全部或部分状态
posted @ 2020-05-12 00:54  CD、小月  阅读(9)  评论(0编辑  收藏  举报  来源