Data Access Object Pattern

The Data Access Object Pattern, also known as DAO pattern, is used to separate low-level data access APIs or operations from high-level business services. The Data Access Object Pattern does not belong to the categories of the 23 commonly defined design patterns, but it is generally regarded as a broad-spectrum technical design pattern.

Description

The Data Access Object Pattern encapsulates access to and storage of data sources, providing a data access object class responsible for managing and manipulating the stored data, and standardizing the data storage format, similar to the backend's DAO layer. HTML5 provides two new methods for client-side data storage: localStorage and sessionStorage. They are two storage mechanisms provided by the Web Storage API, with the former being permanent storage and the latter being limited to the current session and data transfer in related windows. Since WebStorage uses a key-value approach to access data and can only store strings (any type of data will be converted to strings when stored and requires type conversion when read), we can standardize the format of the key, such as module name + Key, developer + Key, and also add a prefix to describe the data in the value, such as adding a timestamp for the expiration date of the data to manage the data lifecycle. The specific format can be defined by the project team itself, mainly for ease of management and prevention of conflicts. In the frontend, it mainly involves encapsulating local storage to standardize and define the data access object after the conventions are set.

Implementation

/**
 * LocalStorage data access class
 * @param {string} prefix Key prefix
 * @param {string} timeSplit Separator between timestamp and stored data
 */
var DAO = function (prefix, timeSplit) {
    this.prefix = prefix;
    this.timeSplit = timeSplit || "|-|";
}


```javascript
// Prototype method
DAO.prototype = {
    constructor: DAO,
    // Operation status
    status: {
        SUCCESS: 0,     // Success
        FAILURE: 1,     // Failure
        OVERFLOW: 2,    // Overflow
        TIMEOUT: 3      // Expired
    },
    // Local storage object
    storage: localStorage || window.localStorage,
    /**
     * Get the real key value with prefix
     * @param key Data field identifier
     */
    getKey: function (key) {
        return this.prefix + key;
    },
    /**
     * Add (or update) data
     * @param key Data field identifier
     * @param value Data value
     * @param callback Callback function
     * @param time Expiration time
     */
    set: function (oriKey, value, callback, time) {
        let status = this.status.SUCCESS; // Default to success
        let key = this.getKey(oriKey);
        try{
            time = new Date(time).getTime() || time.getTime(); // Get expiration timestamp
        }catch(e){
            time = new Date().getTime() + 1000 * 60 * 60 * 24 * 30; // Default to one month if no expiration time is set
        }
        try{
            this.storage.setItem(key, time + this.timeSplit + value); // Add (or update) data to local storage
        }catch(e){
            status = this.status.OVERFLOW; // Overflow occurred
        }
        callback && callback.call(this, status, key, value); // Execute callback and pass in parameters
    },
    /**
     * Get data
     * @param key Data field identifier
     * @param callback Callback function
     */
    get: function (oriKey, callback) {
        let key = this.getKey(oriKey);
        let status = this.status.SUCCESS;    // Get data status
        let value = null;    // Get data value
        try{
            value = this.storage.getItem(key); // Get data from local storage
        }catch(e){
            status = this.status.FAILURE; // Failed to get data
            value = null;
        }
        // If data is successfully retrieved
        if (status !== this.status.FAILURE) {
            let index = value.indexOf(this.timeSplit);
            let timeSplitLen = this.timeSplit.length;
            let time = value.slice(0, index); // Get the timestamp
            // Check if the data is not expired
            if(new Date(1 * time).getTime() > new Date().getTime() || time === 0) {
                value = value.slice(index + timeSplitLen); // Get the data value
            }else {
                value = null; // Data has expired, delete the data
                status = this.status.TIMEOUT;
                this.remove(key);
            }
        }
        callback && callback.call(this, status, value); // Execute callback
        return value; // Return the result value
    },
    /**
    * Delete data
    * @param key Data field identifier
    * @param callback Callback function
    */
    remove: function (oriKey, callback) {
        let status = this.status.FAILURE; // Set the default status to failure
        let key = this.getKey(oriKey);
        let value = null;
        try {
            value = this.storage.getItem(key); // Get the data value
        }catch(e){
            // Data does not exist, no operation is taken
        }
        if(value){ // If the data exists
            try{
                // Delete the data
                this.storage.removeItem(key);
                status = this.status.SUCCESS;
            }catch(e) {
                // Data deletion failed, no operation is taken
            }
        }
        // Execute callback and pass in parameters, if successful, pass in the deleted data value
        let param = status > 0 ? null : value.slice(value.indexOf(this.timeSplit) + this.timeSplit.length);
        callback && callback.call(this, status, param);
    }
};
(function(){
    let dao = new DAO("user-verify");
    dao.set("token", "111", (...args) => console.log(args)); // [0, "user-verifytoken", "111"]
    let value = dao.get("token", (...args) => console.log(args)); // [0, "111"]
    console.log(value); // 111
    dao.remove("token", (...args) => console.log(args)); // [0, "111"]
})();

Daily Challenge

https://github.com/WindrunnerMax/EveryDay

Reference

https://zhuanlan.zhihu.com/p/235136284 https://zkhdev.github.io/2017/07/31/js-dao/ https://www.runoob.com/design-pattern/data-access-object-pattern.html