对几种常见的设计模式的理解 (javascript实现)

设计模式的核心原则:

  1. 单一职责原则(SRP)
    • 一个对象(方法)只做一件事情
  1. 开放-封闭原则(OCP)
    • 软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改。
  1. 最少知识原则(LKP)
    • 一个软件实体应当尽可能少地与其他实体发生相互作用。(调用链不要太长和过于复杂)
      • 比如(情景:将军需要让(士兵 function)挖散兵坑)
      • (复杂的情况)将军通知上校让他叫来少校,然后让少校找来上尉,并让上尉通知一个军士,最后军士唤来一个士兵,然后将军命令士兵挖掘一些散兵坑。
      • (简单情况)将军有个调度部,命令调度部去挖掘一些散兵坑就行了,调度部会安排士兵去

几种常见的设计模式:

  1. 单例模式
  2. 迭代器模式
  3. 代理(中介)模式
  4. 发布-订阅模式
  5. 装饰器模式

1. 单例模式

定义:确保一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等

在JavaScript开发中,单例模式的用途非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

单个单例参考:

  1. // 单例构造函数
  2. function CreateSingleton (name) {
  3. this.name = name;
  4. this.getName();
  5. };
  6. // 获取实例的名字
  7. CreateSingleton.prototype.getName = function() {
  8. console.log(this.name)
  9. };
  10. // 单例对象
  11. var Singleton = (function(){ // 用闭包特性,保存唯一的单例
  12. var instance; // 保存唯一的单例
  13. return function (name) {
  14. if(!instance) {
  15. instance = new CreateSingleton(name);
  16. }
  17. return instance;
  18. }
  19. })();
  20. // 创建实例对象1
  21. var a = new Singleton('a');
  22. // 创建实例对象2
  23. var b = new Singleton('b');
  24. console.log(a === b); // true

通用单例参考:

  1. var singleton = function(fn) {
  2. var instance;
  3. return function() {
  4. return instance || (instance = fn.apply(this, arguments));
  5. }
  6. };
  7. // 创建遮罩层
  8. var createMask = function(){
  9. // 创建div元素
  10. var mask = document.createElement('div');
  11. // 设置样式
  12. mask.style = 'xxx'
  13. document.body.appendChild(mask);
  14. // 单击隐藏遮罩层
  15. mask.onclick = function(){
  16. this.style.display = 'none';
  17. }
  18. return mask;
  19. };
  20. // 创建登陆窗口
  21. var createLogin = function() {
  22. // 创建div元素
  23. var login = document.createElement('div');
  24. // 设置样式
  25. login.style = 'xxx'
  26. login.innerHTML = 'login it';
  27. document.body.appendChild(login);
  28. return login;
  29. };
  30. document.getElementById('btn').onclick = function() {
  31. singleton(createMask)();
  32. singleton(createLogin)();
  33. }

总结:需要有一个变量去保存单例

2. 迭代器模式

可以很简便的实现遍历

  • js也内置了迭代器(some、every、forEach、map、filter)等
  1. // 手动实现一个迭代器
  2. var each = function(ary, callback){
  3. for (var i = 0, l = ary.length; i < l; i++){
  4. if (callback.call(ary[i], i, ary[i]) === false){ // callback 的执行结果返回false,提前终止迭代
  5. break;
  6. }
  7. }
  8. };
  9. each([1, 2, 3, 4, 5], function(i, n){
  10. if (n > 3){ // n 大于3 的时候终止循环
  11. return false;
  12. }
  13. console.log(n); // 分别输出:1, 2, 3
  14. });

3. 代理(中介)模式

为一个对象提供一个代用品或占位符,以便控制对它的访问

代理模式是一种非常有意义的模式,在生活中可以找到很多代理模式的场景。

  • 比如,明星都有经纪人作为代理。如果想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演出的细节和报酬都谈好之后,再把合同交给明星签。

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象

对几种常见的设计模式的理解 (javascript实现) - 图1

举个实际例子:

  • 通过增加虚拟代理的方式,把预加载图片的职责放到代理对象中,而本体仅仅负责往页面中添加img标签,这也是它最原始的职责。

此例子是:我们需要加载一个大图,大图比较慢,在大图未加载好之前,先用菊花图loading.gif替代

  1. // myImage负责往页面中添加img标签:
  2. var myImage = (function(){
  3. var imgNode = document.createElement( 'img' );
  4. document.body.appendChild( imgNode );
  5. return {
  6. setSrc: function( src ) {
  7. imgNode.src = src;
  8. }
  9. }
  10. })();
  11. // proxyImage负责预加载图片,并在预加载完成之后把请求交给本体myImage:
  12. var proxyImage = (function() {
  13. var img = new Image;
  14. img.onload = function() {
  15. myImage.setSrc( this.src );
  16. }
  17. return {
  18. setSrc: function( src ) {
  19. myImage.setSrc( './loading.gif' );
  20. img.src = src;
  21. }
  22. }
  23. })();
  24. proxyImage.setSrc( 'http://imgcache.qq.com/music/photo/xxxxxxbig.png' );

4. 发布-订阅模式(观察者模式)

博主姊妹篇:发布-订阅模式详解(观察者模式)

5. 装饰器模式

其他伙伴写的:https://segmentfault.com/a/1190000013664124


个人整理,有误可留言。
部分参考丛书 《JavaScript设计模式与开发实践》曾探