In depth understanding of JavaScript series (44) : bridge patterns for design patterns

  • 2020-05-10 17:45:54
  • OfStack

introduce

The bridge pattern (Bridge) separates the abstract part from its implementation part so that they can all change independently.

The body of the

The bridge mode is most commonly used for event monitoring. See the following code:


addEvent(element, 'click', getBeerById);
function getBeerById(e) {
var id = this.id;
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// Callback response.
console.log('Requested Beer: ' + resp.responseText);
});
}

The problem with the above code is that getBeerById must have a browser context to use it, because it USES the this.id property internally. Therefore, a slightly more experienced programmer will transform the program into the following form:

function getBeerById(id, callback) {
// through ID Send the request, and then return the data
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// callback response
callback(resp.responseText);
});
}

Much more practical, right? First, ID can be passed in at will, and an callback function is provided for customizing the handler. But what does this have to do with Bridges? This is what the next code is all about:

addEvent(element, 'click', getBeerByIdBridge);
  function getBeerByIdBridge (e) {
    getBeerById(this.id, function(beer) {
      console.log('Requested Beer: '+beer);
  });
}

Here, getBeerByIdBridge is the bridge we defined to connect the abstract click event to getBeerById, and pass in the ID of the event source and the custom call function (console.log output) as parameters to the getBeerById function.

This might seem like a simple example, but let's do a more complicated practical example.

Live XHR connection queue

We're going to build a queue that has a lot of ajax requests in it. The main reason to use queues (queue) is to make sure that requests that are added first are processed first. At any time, we can suspend requests, delete requests, retry requests, and support subscription events for individual requests.

Basic core function
Before we start, let's define a few core encapsulation functions under 1. The first one is the function encapsulation of asynchronous request:


var asyncRequest = (function () {
    function handleReadyState(o, callback) {
        var poll = window.setInterval(
                    function () {
                        if (o && o.readyState == 4) {
                            window.clearInterval(poll);
                            if (callback) {
                                callback(o);
                            }
                        }
                    },
                    50
                    );
    }     var getXHR = function () {
        var http;
        try {
            http = new XMLHttpRequest;
            getXHR = function () {
                return new XMLHttpRequest;
            };
        }         catch (e) {
            var msxml = [
                        'MSXML2.XMLHTTP.3.0',
                        'MSXML2.XMLHTTP',
                        'Microsoft.XMLHTTP'
                        ];             for (var i = 0, len = msxml.length; i < len; ++i) {
                try {
                    http = new ActiveXObject(msxml[i]);
                    getXHR = function () {
                        return new ActiveXObject(msxml[i]);
                    };
                    break;
                }
                catch (e) { }
            }
        }
        return http;
    };     return function (method, uri, callback, postData) {
        var http = getXHR();
        http.open(method, uri, true);
        handleReadyState(http, callback);
        http.send(postData || null);
        return http;
    };
})();

The self-executing function encapsulated above is a generic Ajax request function that anyone who believes in the property Ajax can understand.

Next, we define a generic method to add methods (functions) :


Function.prototype.method = function (name, fn) {
    this.prototype[name] = fn;
    return this;
};

Finally, add two methods about arrays, one for traversal and one for filtering:

if (!Array.prototype.forEach) {
    Array.method('forEach', function (fn, thisObj) {
        var scope = thisObj || window;
        for (var i = 0, len = this.length; i < len; ++i) {
            fn.call(scope, this[i], i, this);
        }
    });
} if (!Array.prototype.filter) {
    Array.method('filter', function (fn, thisObj) {
        var scope = thisObj || window;
        var a = [];
        for (var i = 0, len = this.length; i < len; ++i) {
            if (!fn.call(scope, this[i], i, this)) {
                continue;
            }
            a.push(this[i]);
        }
        return a;
    });
}

Because some new browsers already support both of these capabilities (or some libraries do), it's important to know that if you do, you won't be able to handle them.

Observer system
The observer plays an important role in the process of events in the queue, and can subscribe to events (success, failure, suspension) when the queue is processed:


window.DED = window.DED || {};
DED.util = DED.util || {};
DED.util.Observer = function () {
    this.fns = [];
} DED.util.Observer.prototype = {
    subscribe: function (fn) {
        this.fns.push(fn);
    },     unsubscribe: function (fn) {
        this.fns = this.fns.filter(
            function (el) {
                if (el !== fn) {
                    return el;
                }
            }
            );
            },
    fire: function (o) {
        this.fns.forEach(
            function (el) {
                el(o);
            }
            );
    }
};

Queues are the main implementation code
First, the main properties and event delegates of the queue are subscribed:


DED.Queue = function () {
    // The queue containing the request .
 this.queue = [];
    // use Observable Objects in the 3 So that you can subscribe to events at any time
 this.onComplete = new DED.util.Observer;
    this.onFailure = new DED.util.Observer;
    this.onFlush = new DED.util.Observer;     // Core properties that can be set at the time of an external call
 this.retryCount = 3;
    this.currentRetry = 0;
    this.paused = false;
    this.timeout = 5000;
    this.conn = {};
    this.timer = {};
};

Then through the chain call of DED.Queue.method, a number of available methods are added to the queue:


DED.Queue.
    method('flush', function () {
        // flush methods
 if (!this.queue.length > 0) {
            return;
        }         if (this.paused) {
            this.paused = false;
            return;
        }         var that = this;
        this.currentRetry++;
        var abort = function () {
            that.conn.abort();
            if (that.currentRetry == that.retryCount) {
                that.onFailure.fire();
                that.currentRetry = 0;
            } else {
                that.flush();
            }
        };         this.timer = window.setTimeout(abort, this.timeout);
        var callback = function (o) {
            window.clearTimeout(that.timer);
            that.currentRetry = 0;
            that.queue.shift();
            that.onFlush.fire(o.responseText);
            if (that.queue.length == 0) {
                that.onComplete.fire();
                return;
            }             // recursive call to flush
 that.flush();         };         this.conn = asyncRequest(
            this.queue[0]['method'],
            this.queue[0]['uri'],
            callback,
            this.queue[0]['params']
            );
    }).
    method('setRetryCount', function (count) {
        this.retryCount = count;
    }).
    method('setTimeout', function (time) {
        this.timeout = time;
    }).
    method('add', function (o) {
        this.queue.push(o);
    }).
    method('pause', function () {
        this.paused = true;
    }).
    method('dequeue', function () {
        this.queue.pop();
    }).
    method('clear', function () {
        this.queue = [];
    });

It looks like a lot of code, but when you fold it, you can see that it actually defines flush, setRetryCount, setTimeout, add, pause, dequeue, and clear methods on the queue.

A simple call


var q = new DED.Queue;
// Set the number of retries high 1 Point to deal with slow connections
q.setRetryCount(5);
// Set up the timeout time
q.setTimeout(1000);
// add 2 A request .
q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true'
}); q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true&woe=me'
}); // flush The queue
q.flush();
// Pause the queue and save the rest
q.pause();
// empty .
q.clear();
// add 2 A request .
q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true'
}); q.add({
    method: 'GET',
    uri: '/path/to/file.php?ajax=true&woe=me'
}); // Delete the last from the queue 1 A request .
q.dequeue();
// Once again, Flush
q.flush();

The bridge?

There is no bridge in the calling code above. What about Bridges? Take a look at the full example below and you'll see Bridges everywhere:


function getBeerById(id, callback) {
// through ID Send the request, and then return the data
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// callback response
callback(resp.responseText);
});
}
0
In this example, you can do flush queues, suspend queues, delete requests from queues, empty queues, and so on, and you can see the power of bridging.

conclusion

The advantages of the bridge model are also obvious, so let's just list the main advantages:

Part 1. The separation of interface and implementation, an implementation is not necessarily the same binding on an interface, the realization of the abstract class (function) can be configured at run time, an object can change its implementation at run time, even with the abstract and the implementation is also fully decoupled, also is advantageous to the layered, resulting in a better structured system.
2. Improve scalability
3. The implementation details are transparent to customers and can be hidden from customers.

At the same time, the bridge model has its own disadvantages:

The large number of classes will lead to an increase in development costs and possibly a reduction in performance.


Related articles: