跳至主要內容

数据访问对象模式

微信公众号:储凡2023/11/29大约 10 分钟

数据访问对象模式

数据访问对象模式Data Access Object PatternDAO模式,用于把低级的数据访问API 或操作从高级的业务服务中分离出来,数据访问对象模式不属于一般定义的23种设计模式的范畴,而通常将其看作广义上的技巧型设计模式。

基本介绍

数据访问对象模式就是对数据源的访问与存储进行封装,提供一个数据访问对象类负责对存储的数据进行管理和操作,规范数据存储格式,类似于后台的DAO 层。 HTML5提供了两种在客户端存储数据的新方法: localStoragesessionStorage,他们是Web Storage API 提供的两种存储机制,区别在于前者属于永久性存储,而后者是局限于当前会话以及衍生窗口的数据传递。由于WebStorage 采用Key-Value的方式存取数据,而且只能存字符串(任何类型存储的时候都会被转为字符串,读取的时候需要进行类型转换) ,所以我们可以对Key的格式进行规范,比如模块名+Key,开发人员+Key 等,还可以在值中添加一段前缀用来描述数据,如添加数据过期日期的时间戳,用来管理数据的生命周期。具体格式项目组可以自己定义,主要是便于管理,防止出现冲突,在前端方面其实主要是对于本地存储进行了一次封装,用以进行一个规范性约束,约定好规范后就可以开始定义数据访问对象了。

代码实现

/**
 * LocalStorage数据访问类
 * @param {string} prefix Key前缀
 * @param {string} timeSplit 时间戳与存储数据之间的分割符
 */
function DAO(prefix, timeSplit) {
  this.prefix = prefix
  this.timeSplit = timeSplit || '|-|'
}

// 原型方法
DAO.prototype = {
  constructor: DAO,
  // 操作状态
  status: {
    SUCCESS: 0, // 成功
    FAILURE: 1, // 失败
    OVERFLOW: 2, // 溢出
    TIMEOUT: 3 // 过期
  },
  // 本地存储对象
  storage: localStorage || window.localStorage,
  /**
   * 获取带前缀的真实键值
   * @param key 数据字段标识
   */
  getKey(key) {
    return this.prefix + key
  },
  /**
   * 添加(修改)数据
   * @param key 数据字段标识
   * @param value 数据值
   * @param callback 回调函数
   * @param time 过期时间
   */
  set(oriKey, value, callback, time) {
    let status = this.status.SUCCESS // 默认为成功状态
    const key = this.getKey(oriKey)
    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(oriKey, callback) {
    const key = this.getKey(oriKey)
    let status = this.status.SUCCESS // 获取数据状态
    let value = null // 获取数据值
    try {
      value = this.storage.getItem(key) // 从本地存储获取数据
    }
    catch (e) {
      status = this.status.FAILURE // 获取数据失败
      value = null
    }
    // 如果成功获取数据
    if (status !== this.status.FAILURE) {
      const index = value.indexOf(this.timeSplit)
      const timeSplitLen = this.timeSplit.length
      const 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(oriKey, callback) {
    let status = this.status.FAILURE // 设置默认状态为失败
    const key = this.getKey(oriKey)
    let value = null
    try {
      value = this.storage.getItem(key) // 获取数据值
    }
    catch (e) {
      // 数据不存在,不采取操作
    }
    if (value) { // 如果数据存在
      try {
        // 删除数据
        this.storage.removeItem(key)
        status = this.status.SUCCESS
      }
      catch (e) {
        // 数据删除失败,不采取操作
      }
    }
    // 执行回调并传入参数,删除成功则传入被删除的数据值
    const param = status > 0 ? null : value.slice(value.indexOf(this.timeSplit) + this.timeSplit.length)
    callback && callback.call(this, status, param)
  }
};

(function () {
  const dao = new DAO('user-verify')
  dao.set('token', '111', (...args) => console.log(args)) // [0, "user-verifytoken", "111"]
  const value = dao.get('token', (...args) => console.log(args)) // [0, "111"]
  console.log(value) // 111
  dao.remove('token', (...args) => console.log(args)) // [0, "111"]
})()

更新日志

2025/2/24 19:02
查看所有更新日志
  • 16e12-style: eslint code
  • a3cca-refactor: 替换eslint规则,使用antfu/eslint模块 (#138)
  • c0f2d-refactor: 升级vuepress相关版本,优化项目结构 (#137)
  • e6a79-feat: 大幅新增设计模式、Linux命令、ORM框架教程文档