数据访问对象模式(Data access object-DAO):抽象和封装对数据源的访问和存储,DAO通过对数据源链接的管理方便对数据的访问和存储。
假设我们有一个功能需要本地存取数据实现而非调用后台接口,这个功能并不难,但存在以下问题
- 数据很可能会覆盖他人的数据或被其他人员覆盖
- 添加一条数据需要做各个浏览器的兼容
- 无法知道数据存取的状态成功、失败、过期等
- 更多一系列的问题
那么,有什么办法可以让每个人方便的管理自己的本地存储库呢?
我们可以创建一个数据访问对象类DAO,这样每个人在自己的模块就可以通过创建这个DAO类的实例来使用了
数据访问对象类
一个标准的数据访问对象类需要具备以下条件
- 可以对数据进行增、删、改、查
- 可以区分是谁的库(localStorage本地存储没有分库的概念,需要人为去划分)
- 每一项数据存储都需要设置存储时间(用于定期清除等)
包括但不限于以上条件,下面我们开始创建一个DAO类
/*
* @description 本地存储类
* @method BaseLocalStorage
* @param {String} preId 本地存储数据库前缀
* @param {String} timeSign 时间戳与存储数据之间的拼接符
*/
var BaseLocalStorage = function(preId, timeSign) {
// 定义本地存储数据前缀
this.preId = preId;
// 定义时间戳与存储数据之间的拼接符
this.timeSign = timeSign || '-';
}
数据操作状态
我们可以在DAO类内部保存操作需要返回的状态,并提供对数据进行增删改查的接口
BaseLocalStorage.prototype = {
// 操作状态
status: {
SUCCESS: 0, // 成功
FAILURE: 1, // 失败
OVERFLOW: 2, // 溢出
TIMEOUT: 3 // 过期
},
// 保存本地存储链接
storage: localStorage || window.localStorage,
// 获取本地存储数据库数据真实字段
getKey: function(key) {
return this.preId + key;
},
// 添加(修改)数据
set: function(key, value, callback, time) {
},
// 获取数据
get: function(key, callback) {
},
// 删除数据
remove: function(key, callback) {
}
}
增添数据
添加数据时,我们需要在字段中添加时间戳,并且我们向本地存储中添加数据实质上是调用localStorage的setItem方法。最后我们还要执行回调函数并将操作的结果传入回调函数中
// 添加(修改)数据
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 * 31;
}
try {
// 向数据库中添加数据
this.storage.setItem(key, time + this.timeSign + value);
} catch(e) {
// 溢出失败,返回溢出状态
status = this.status.OVERFLOW;
}
// 有回调函数则执行回调函数并传入参数操作状态,真实数据字段标识以及存储数据值
callback && callback.call(this, status, key, value);
}
查找数据
数据查找需要兼容以下四种查询数据情况
- 该字段的数据本身就不存在,返回失败状态
- 操作成功但是没有获取到值,返回失败状态
- 获取到值但是时间已经过期,此时应该删除数据
- 获取成功并将数据返回
get: function(key, callback) {
var status = this.status.SUCCESS, // 默认成功
key = this.getKey(key),
value = null,
timeSignLen = this.timeSign.length, // 时间戳与存储数据之间的拼接符长度
that = this,
index, // 时间戳与存储数据之间的拼接符起始位置
time, // 时间戳
result;
try {
// 获取字段对应的数据字符串
value = that.storage.getItem(key);
} catch(e) {
// 获取失败则返回失败状态,数据结果为null
result = {
status: that.status.FAILURE,
value: null
};
callback && callback.call(this, result.status, result.value);
return result;
}
if (value) {
// 获取时间戳与存储数据之间的拼接符起始位置
index = value.indexOf(that.timeSign);
// 获取时间戳
time = +value.slice(0, index);
// 如果时间为过期
if (new Date(time).getTime() > new Date().getTime() || time == 0) {
// 获取数据结果(拼接符后面的字符串)
value = value.slice(index + timeSignLen);
} else {
value = null;
status = that.status.TIMEOUT;
that.remove(key);
}
} else {
// 未获取数据字符串状态为失败状态
status = that.status.FAILURE;
}
result = {
status: status,
value: value
}
callback && callback.call(this, result.status, result.value);
return result;
}
删除数据
删除方法需要兼容以下三种情况
- 没有该字段,返回失败状态
- 删除时发生异常,返回失败状态
- 删除成功
// 删除数据
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) {
//TODO handle the exception
}
}
value = status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length);
// 执行回调,注意传入回调函数中的数据值:如果操作状态成功则返回真实数据结果,否则返回空
callback && callback.call(this, status, value);
}
测试
var LS = new BaseLocalStorage('LS_');
LS.set('a', 'xiaoming', function() {
console.log(arguments);
});
LS.get('a', function() {
console.log(arguments);
})
LS.remove('a', function() {
console.log(arguments);
})
LS.remove('a', function() {
console.log(arguments);
})
LS.get('a', function() {
console.log(arguments);
})
结果正如我们所预期的那样
最后,附完整代码
/*
* @description 本地存储类
* @method BaseLocalStorage
* @param {String} preId 本地存储数据库前缀
* @param {String} timeSign 时间戳与存储数据之间的拼接符
*/
var BaseLocalStorage = function(preId, timeSign) {
// 定义本地存储数据前缀
this.preId = preId;
// 定义时间戳与存储数据之间的拼接符
this.timeSign = timeSign || '-';
}
BaseLocalStorage.prototype = {
// 操作状态
status: {
SUCCESS: 0, // 成功
FAILURE: 1, // 失败
OVERFLOW: 2, // 溢出
TIMEOUT: 3 // 过期
},
// 保存本地存储链接
storage: localStorage || window.localStorage,
// 获取本地存储数据库数据真实字段
getKey: function(key) {
return this.preId + key;
},
// 添加(修改)数据
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 * 31;
}
try {
// 向数据库中添加数据
this.storage.setItem(key, time + this.timeSign + value);
} catch(e) {
// 溢出失败,返回溢出状态
status = this.status.OVERFLOW;
}
// 有回调函数则执行回调函数并传入参数操作状态,真实数据字段标识以及存储数据值
callback && callback.call(this, status, key, value);
},
// 获取数据
get: function(key, callback) {
var status = this.status.SUCCESS, // 默认成功
key = this.getKey(key),
value = null,
timeSignLen = this.timeSign.length, // 时间戳与存储数据之间的拼接符长度
that = this,
index, // 时间戳与存储数据之间的拼接符起始位置
time, // 时间戳
result;
try {
// 获取字段对应的数据字符串
value = that.storage.getItem(key);
} catch(e) {
// 获取失败则返回失败状态,数据结果为null
result = {
status: that.status.FAILURE,
value: null
};
callback && callback.call(this, result.status, result.value);
return result;
}
if (value) {
// 获取时间戳与存储数据之间的拼接符起始位置
index = value.indexOf(that.timeSign);
// 获取时间戳
time = +value.slice(0, index);
// 如果时间为过期
if (new Date(time).getTime() > new Date().getTime() || time == 0) {
// 获取数据结果(拼接符后面的字符串)
value = value.slice(index + timeSignLen);
} else {
value = null;
status = that.status.TIMEOUT;
that.remove(key);
}
} else {
// 未获取数据字符串状态为失败状态
status = that.status.FAILURE;
}
result = {
status: status,
value: value
}
callback && callback.call(this, result.status, result.value);
return result;
},
// 删除数据
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) {
//TODO handle the exception
}
}
value = status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length);
// 执行回调,注意传入回调函数中的数据值:如果操作状态成功则返回真实数据结果,否则返回空
callback && callback.call(this, status, value);
}
}