JS设计模式(三) 数据访问对象模式

引言
HTML5 提供了两种在客户端存储数据的新方法:localStorage、sessionStorage,他们是Web Storage API 提供的两种存储机制,区别在于前者属于永久性存储,而后者是局限于当前窗口的数据传递,存储在其中的数据会在当前会话结束时被删除。localStorage、sessionStorage的具体内容在这里就不多做介绍了,我们主要探讨一下在实际开发中怎样合理使用他们。
问题
大部分网站会将一些数据(如:用户Token)存储在前端,用来实现页面间的传值,对于一些大型Web应用来说,其存储的数据可能会非常多,数据的管理会变得复杂,并且一个大型项目是由多位程序员共同开发的,这时就会遇到一个问题:怎样确保自己的数据不会覆盖掉其他人的呢?因为在一个页面中大家都是使用同一个WebStorage对象,总不能把大家使用过的Key记录下来吧。这时候就可以使用数据访问对象模式来解决了。
数据访问对象模式(DAO)
数据访问对象模式就是对数据源的访问与存储进行封装,提供一个数据访问对象类负责对存储的数据进行管理和操作,规范数据存储格式,类似于后台的DAO层。
由于WebStorage采用Key-Value的方式存取数据,而且只能存字符串(任何类型存储的时候都会被转为字符串,读取的时候需要进行类型转换),所以我们可以对Key的格式进行规范,比如模块名+Key,开发人员+Key等,还可以在值中添加一段前缀用来描述数据,如添加数据过期日期的时间戳,用来管理数据的生命周期。具体格式项目组可以自己定义,主要是便于管理,防止出现冲突,约定好规范后就可以开始定义数据访问对象了。
下面以localStorage为例,介绍一下数据访问对象类的定义和使用。
代码示例
DAO类基本结构
数据访问对象类的基本结构如下,我们给键值添加了一段前缀用来避免键值冲突,并且在值中加入数据过期时间戳以及分隔符,获取值的时候再进行判断是否过期,这样可以更灵活地管理存储数据的生命周期。这里还用到了回调的方式,方便获取数据访问过程的具体结果,以及在必要时执行相关操作。
/**
 * LocalStorage数据访问类
 * @param {string} prefix Key前缀
 * @param {string} timeSplit 时间戳与存储数据之间的分割符
 */
var Dao = function (prefix, timeSplit) {
    this.prefix = prefix;
    this.timeSplit = timeSplit || '|-|';
}
// LocalStorage数据访问类原型方法
Dao.prototype = {
    // 操作状态
    status: {
        SUCCESS: 0,     // 成功
        FAILURE: 1,     // 失败
        OVERFLOW: 2,    // 溢出
        TIMEOUT: 3      // 过期
    },
    // 本地存储对象
    storage: localStorage || window.localStorage,
    // 获取带前缀的真实键值
    getKey: function (key) {
        return this.prefix + key;
    },
    // 添加(修改)数据
    set: function (key, value, callback, time) {
       ...
    },
    // 获取数据
    get: function (key, callback) {
        ...
    },
    // 删除数据
    remove: function (key, callback) {
        ...
    }
}
添加(修改)数据
/**
    * 添加(修改)数据
    * @param key 数据字段标识
    * @param value 数据值
    * @param callback 回调函数
    * @param time 过期时间
    */
    set: function (key, value, callback, time) {
        // 默认为成功状态
        var status = this.status.SUCCESS,
            key = this.getKey(key);
        try {
            // 获取过期时间戳
            time = new Date(time).getTime() || time.getTime();
        } catch (e) {
            // 未设置过期时间时默认为一个月
            time = new Date().getTime() + 1000 * 60 * 60 * 24 * 30;
        }
        try {
            // 向本地存储中添加(修改)数据
            this.storage.setItem(key, time + this.timeSplit + value);
        } catch (e) {
            // 发生溢出
            status = this.status.OVERFLOW;
        }
        // 执行回调并传入参数
        callback && callback.call(this, status, key, value);
    }
获取数据
/**
    * 获取数据
    * @param key 数据字段标识
    * @param callback 回调函数
    */
    get: function (key, callback) {
        var key = this.getKey(key),
            status = this.status.SUCCESS,    // 获取数据状态
            value = null;    // 获取数据值
        try {
            // 从本地存储获取数据
            value = this.storage.getItem(key);
        } catch (e) {
            // 获取数据失败
            status = this.status.FAILURE;
            value = null;
        }
        // 如果成功获取数据
        if (status !== this.status.FAILURE) {
            var index = value.indexOf(this.timeSplit),
                timeSplitLen = this.timeSplit.length,
                // 获取时间戳
                time = value.slice(0, index);
            // 判断数据是否未过期
            if (new Date(1*time).getTime() > new Date().getTime() || time == 0) {
                // 获取数据值
                value = value.slice(index + timeSplitLen);
            } else {
                // 数据已过期,删除数据
                value = null;
                status = this.status.TIMEOUT;
                this.remove(key);
            }
        }
        // 执行回调
        callback && callback.call(this, status, value);
        // 返回结果值
        return value;
    }
删除数据
/**
    * 删除数据
    * @param key 数据字段标识
    * @param callback 回调函数
    */
    remove: function (key, callback) {
        // 设置默认状态为失败
        var status = this.status.FAILURE,
            key = this.getKey(key),
            value = null;
        try {
            // 获取数据值
            value = this.storage.getItem(key);
        } catch (e) {
            // 数据不存在,不采取操作
        }
        // 如果数据存在
        if (value) {
            try {
                // 删除数据
                this.storage.removeItem(key);
                status = this.status.SUCCESS;
            } catch (e) {
                // 数据删除失败,不采取操作
            }
        }
        // 执行回调并传入参数,删除成功则传入被删除的数据值
        callback && callback.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSplit) + this.timeSplit.length));
    }
用法
var dao = new Dao('myModule_');
// 添加/修改数据
dao.set('token', 'abc', function () { console.log(arguments); });
// 获取数据
var value = dao.get('token', function () { console.log(arguments); });
console.log(value);
// 删除数据
dao.remove('token', function () { console.log(arguments); });
写在最后
其实数据访问对象模式更适合与服务器端的数据库操作,比如在nodejs中操作MongoDB,通过对数据库增删改查操作的封装,可以方便我们对前端存储的管理,不必为操作数据库感到烦恼,DAO已经为我们提供了便捷统一的接口,这样在团队开发中就不用担心影响到其他人的数据了。
本文为作者kMacro原创,转载请注明来源:http://www.jianshu.com/p/ffdaa99dbde8。
JS设计模式(三) 数据访问对象模式的更多相关文章
- PHP设计模式系列 - 数据访问对象模式
		数据访问对象模式 数据访问对象模式描述了如何创建透明访问数据源的对象. 场景设计 设计一个BaseDao基类,实现数据库操作基本的一些query,insert,update方法 在实际使用的过程中,继 ... 
- Java数据访问对象模式
		数据访问对象模式或DAO模式用于将低级数据访问API或操作与高级业务服务分离. 以下是数据访问对象模式的参与者. 数据访问对象接口 - 此接口定义要对模型对象执行的标准操作. 数据访问对象具体类 - ... 
- JAVAWEB基础模块开发顺序与数据访问对象实现类步骤
		一.模块的开发的顺序 1. 定义数据表 2. 新建模型类 3. 新建"add.jsp" 4. 实现AddServlet中的doGet()方法 5. 定义Dao.Service接口 ... 
- Yii的学习(2)--数据访问对象 (DAO)
		摘自Yii官网:http://www.yiiframework.com/doc/guide/1.1/zh_cn/database.dao Yii提供了强大的数据库编程支持.Yii数据访问对象(DAO) ... 
- winform中利用反射实现泛型数据访问对象基类(1)
		考虑到软件使用在客户端,同时想简化代码的实现,就写了一个泛型的数据访问对象基类,并不是特别健全,按道理应该参数化的方式实现insert和update,暂未使用参数化,抽时间改进. /// <su ... 
- 解析大型.NET ERP系统数据访问 对象关系映射框架LLBL Gen Pro
		LLBL Gen Pro是一个为.NET开发人员设计的的对象关系映射(ORM)框架,与NHibernate,Entity Framework等框架一样,通过实体与数据表的映射,实现关系数据库持久化. ... 
- Php设计模式(三):行为型模式part2
		原文详见:http://www.ucai.cn/blogdetail/7023?mid=1&f=5 可以在线运行查看效果哦! <接上文> 5.中介者模式(Mediator) : 用 ... 
- 设计模式のNullObjectPattern(空对象模式)----行为模式
		一.产生背景 在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查.Null 对象不是检查空值,而是反应一个不做任何动作的关系.这样的 Null 对象也可 ... 
- JS设计模式(3)代理模式
		什么是代理模式? 情景:小明追女生 A 非代理模式:小明 =花=> 女生A 代理模式:小明 =花=> 让女生A的好友B帮忙 =花=> 女生A 定义:为其他对象提供一种代理以控制对这个 ... 
随机推荐
- MySQL(三)之SQL语句分类、基本操作、三大范式
			一.SQL语句的分类 DML(Data Manipulation Langauge,数据操纵/管理语言) (insert,delete,update,select) DDL(Data ... 
- java性能真的差吗
			详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt275 我见过java运行在手机上,包括很廉价的山寨手机,但是却暂时没发现.n ... 
- SQL Server 常用操作XML
			--修改FunctionNo节点值,@OperateFunctionNo为参数 set @DataXml.modify('replace value of (/CrudData/FunctionNo/ ... 
- MFS文件系统的组成
			1. 元数据服务器.在整个体系中负责管理管理文件系统,目前MFS只支持一个元数据服务器master,这是一个单点故障,需要一个性能稳定的服务器来充当.希望今后MFS能支持多个master服务器,进一 ... 
- 【集美大学1411_助教博客】团队作业2——需求分析&原型设计 成绩
			首先要向各位同学道歉,最近助教的工作较多,并且伴随着频繁的出差,评论博客和评分都不及时,以致于同学们都没有得到反馈,在此我要表示歉意.其次,对于第二次团队作业,有两个团队没有提交到班级博客中但按时完成 ... 
- 团队作业4--第一次项目冲刺(Alpha版本)预备工作
			小组说明 我们组是从周一开始对项目进行研究讨论并编程的,因为我们看截止日期是周日,就从周一才开始,起步晚了,是我们认识上的失误,导致我们周一周二的步伐没有协调好,项目进展的不稳定,但是我们在上周末并不 ... 
- 作业2——需求分析&原型设计
			需求分析: 软件的最终目的是用来解决用户的某些问题,需求分析就是要理解要解决的问题,真正明确用户需求.下面是我们初步的需求分析: 1.访问软件项目的真实用户(至少10个),确保软件真正体现用户的需求, ... 
- 201521123066《Java程序设计》第五周学习总结
			1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪 ... 
- 201521123087 《Java程序设计》第3周学习总结
			1.本周学习总结 2. 书面作业 代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; publ ... 
- 杭电acm-2007平方和立方和
			#include<stdio.h>int main(){ int t,m,n,x,y,i; while(scanf("%d%d",&n, ... 
