js design pattern-agent pattern

Keywords: Javascript

Preface

This series of articles is mainly based on "JavaScript Design Patterns and Development Practices", which will add some of their own thinking. I hope it will be helpful to you all.

concept

The proxy mode is to provide a substitute or placeholder for an object in order to control its access.

UML class diagram

scene

For example, stars are represented by brokers. If you want to invite a star to a commercial show, you can only contact his agent. The broker will negotiate the details and remuneration of the commercial performance before handing over the contract to the star for signature.

classification

Protection agent

To control access to target objects by objects with different privileges, such as the above example of star brokers

Virtual agent

Delay the creation of expensive objects until they are really needed.
If many http requests are initiated in a short time, we can use virtual proxy to send requests uniformly in a certain time.

Advantages and disadvantages

Advantage

1. Objects can be protected
 2. Optimize performance and reduce overhead objects
 3. Caching results

Example

Picture preloading

Load a picture

  var myImage = (function () {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
      setSrc: function (src) {
        imgNode.src = src;
      }
    }
  })();
  myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');

Imagine if we had a big picture, users would see that the page was blank for a long time.
The improvement we can think of is to show loading pictures before they are loaded.

Add a loading picture

var myImage = (function () {
  var imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  var img = new Image()
  img.onload = () => {
    // Analog Picture Loading
    setTimeout(() => {
      imgNode.src = img.src
    }, 1000)
  }
  return {
    setSrc: function (src) {
      img.src = src
      imgNode.src = 'https://content.igola.com/static/WEB/images/other/loading-searching.gif';
    }
  }
})();
myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');

This code violates the single responsibility principle. This object is responsible for loading and preloading images at the same time.
At the same time, it violates the principle of open and closed. If we don't need to pre-load pictures in the future, we have to modify the whole object.

Improvement with Virtual Agent


var myImage = (function () {
  var imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  return {
    setSrc: function (src) {
      imgNode.src = src
    }
  }
})();

var proxyImage = (function() {
  var img = new Image()
  img.onload = function() {
    myImage.setSrc(img.src)
  }
  return {
    setSrc: function (src) {
      img.src = src
      myImage.setSrc('https://content.igola.com/static/WEB/images/other/loading-searching.gif')
  }
  }
})()


proxyImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');

Note: Our proxy and ontology interfaces should be consistent. For example, both proxy Image and myImage above return an object containing setSrc methods. In this regard, we also have some traces to follow when we write agents.

Virtual proxy merges HTTP requests

Simple implementation

<body>
  <div id="wrapper">
    <input type="checkbox" id="1"></input>1
    <input type="checkbox" id="2"></input>2
    <input type="checkbox" id="3"></input>3
    <input type="checkbox" id="4"></input>4
    <input type="checkbox" id="5"></input>5
    <input type="checkbox" id="6"></input>6
    <input type="checkbox" id="7"></input>7
    <input type="checkbox" id="8"></input>8
    <input type="checkbox" id="9"></input>9
  </div>
</body>

<script type="text/javascript">
  // Simulate http requests
  var synchronousFile = function (id) {
    console.log('Start synchronizing files. id by: ' + id);
  };

  var inputs = document.getElementsByTagName('input')
  var wrapper = document.getElementById('wrapper')
  wrapper.onclick = function (e) {
    if (e.target.tagName === 'INPUT') {
      synchronousFile(e.target.id)
    }
  }
</script>

The drawback is obvious: an http request is sent once at every point

Improvement


<body>
  <div id="wrapper">
    <input type="checkbox" id="1"></input>1
    <input type="checkbox" id="2"></input>2
    <input type="checkbox" id="3"></input>3
    <input type="checkbox" id="4"></input>4
    <input type="checkbox" id="5"></input>5
    <input type="checkbox" id="6"></input>6
    <input type="checkbox" id="7"></input>7
    <input type="checkbox" id="8"></input>8
    <input type="checkbox" id="9"></input>9
  </div>
</body>

<script type="text/javascript">
  // Simulate http requests
  var synchronousFile = function (id) {
    console.log('Start synchronizing files. id by: ' + id);
  };

  var inputs = document.getElementsByTagName('input')
  var wrapper = document.getElementById('wrapper')
  wrapper.onclick = function (e) {
    if (e.target.tagName === 'INPUT' && e.target.checked) {
      proxySynchronousFile(e.target.id)
    }
  }

  var proxySynchronousFile = (function () {
    var cacheIds = [],
      timeId = 0
    return function (id) {
      if (cacheIds.indexOf(id) < 0) {
        cacheIds.push(id)
      }
      clearTimeout(timeId)
      timeId = setTimeout(() => {
        synchronousFile(cacheIds.join(','))
        cacheIds = []
      }, 1000)
    }
  })()
</script>

Cache Proxy-Computational Product

Rough Realization

var mult = function () {
    console.log('Start calculating the product');
    var a = 1;
    for (var i = 0, l = arguments.length; i < l; i++) {
      a = a * arguments[i];
    }
    return a;
  };
  mult(2, 3); // Output: 6
  mult(2, 3, 4); // Output: 24

Improvement


var mult = function () {
  console.log('Start calculating the product');
  var a = 1;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }
  return a;
};
// mult(2, 3); / / Output: 6
// mult(2, 3, 4); / / Output: 24

var proxyMult = (function() {
  var cache = {}
  return function () {
    let id = Array.prototype.join.call(arguments, ',')
    if (cache[id]) {
      return cache[id]
    } else {
      return cache[id] = mult.apply(this, arguments)
    }
  }
})()

proxyMult(2, 3); // Output: 6
proxyMult(2, 3); // Output: 6

We now hope that additions can also be cached

Further improvement


var mult = function () {
  console.log('Start calculating the product');
  var a = 1;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }
  return a;
};

var plus = function () {
  console.log('Start calculating and');
  var a = 0;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a + arguments[i];
  }
  return a;
};


// mult(2, 3); / / Output: 6
// mult(2, 3, 4); / / Output: 24

var createProxyFactory = function (fn) {
  var cache = {}
  return function () {
    let id = Array.prototype.join.call(arguments, ',')
    if (cache[id]) {
      return cache[id]
    } else {
      return cache[id] = fn.apply(this, arguments)
    }
  }
}

var proxyMult = createProxyFactory(mult),
  proxyPlus = createProxyFactory(plus);
proxyMult(1, 2, 3, 4) // Output: 24
proxyMult(1, 2, 3, 4) // Output: 24
proxyPlus(1, 2, 3, 4) // Output: 10
proxyPlus(1, 2, 3, 4) // Output: 10

Agent mode of es6

Class-based Implementation

class Car {
    drive() {
        return "driving";
    };
}

class CarProxy {
    constructor(driver) {
        this.driver = driver;
    }
    drive() {
        return  ( this.driver.age < 18) ? "too young to drive" : new Car().drive();
    };
}

class Driver {
    constructor(age) {
        this.age = age;
    }
}

Implementation Based on Proxy


// Star
let star = {
    name: 'Zhang XX',
    age: 25,
    phone: '13910733521'
}

// Agent
let agent = new Proxy(star, {
    get: function (target, key) {
        if (key === 'phone') {
            // Return to the broker's cell phone number
            return '18611112222'
        }
        if (key === 'price') {
            // Stars do not quote, brokers quote
            return 120000
        }
        return target[key]
    },
    set: function (target, key, val) {
        if (key === 'customPrice') {
            if (val < 100000) {
                // Minimum 10w
                throw new Error('The price is too low.')
            } else {
                target[key] = val
                return true
            }
        }
    }
})

// Sponsor?
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)

// Want to offer your own quotation (bargaining, or scrambling for high prices)
agent.customPrice = 150000
// agent.customPrice = 90000 // / Error Reporting: Price is too low
console.log('customPrice', agent.customPrice)

Posted by thelinx on Sat, 04 May 2019 01:30:37 -0700