Skip to content

设计模式了解应用,发布订阅模式,单例模式

  • 模式是经历了:大量实际项目验证的优秀的解决方案

适配器

小到参数格式的处理,大到(两个实现方式不同但相同功能的)类的方法,提供一个的类名一样的接口

百度、google 提供一个 show 方法

js
var googleMap = {
  show: function () {
    console.log("开始渲染谷歌地图");
  },
};
var baiduMap = {
  display: function () {
    console.log("开始渲染百度地图");
  },
};
var baiduMapAdapter = {
  show: function () {
    return baiduMap.display();
  },
};
renderMap(googleMap);
renderMap(baiduMapAdapter);

策略模式

js
var strategies = {
  "S": function( salary ){
    return salary * 4;
  },
  "A": function( salary ){
    return salary * 3;
  },
  "B": function( salary ){
    return salary * 2; }
  };
}
var calculateBonus = function( level, salary ){
  return strategies[ level ]( salary ); // 多态在策略模式中的体现:当我 们对这些策略对象发出“计算奖金”的请求时,它们会返回各自不同的计算结果,
};
console.log( calculateBonus( 'S', 20000 ) ); // 输出:80000
console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000

代理模式

js
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(this.src);
  };
  return {
    setSrc: function (src) {
      myImage.setSrc("file:// /C:/Users/svenzeng/Desktop/loading.gif");
      img.src = src;
    },
  };
})();
proxyImage.setSrc("http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg");

单例模式

js
var CreateDiv = function (html) {
  this.html = html;
  this.init();
};
CreateDiv.prototype.init = function () {
  var div = document.createElement("div");
  div.innerHTML = this.html;
  document.body.appendChild(div);
};
var ProxySingletonCreateDiv = function () {
  var instance;
  return function (html) {
    if (!instance) {
      instance = new CreateDiv(html);
    }
    return instance;
  };
};
var a = new ProxySingletonCreateDiv("sven1");
var b = new ProxySingletonCreateDiv("sven2");
a === b;

保证只有给弹窗,当一个弹窗打开的时候,关闭其他弹窗

发布订阅模式

二、发布-订阅模式的通用实现

js
var event = {
  clientList: [],
  listen: function (key, fn) {
    if (!this.clientList[key]) {
      this.clientList[key] = [];
    }
    this, clientList[key].push(fn);
  },
  trigger: function () {
    var key = Array.prototype.shift.call(arguments), // (1);
      fns = this.clientList[key];
    if (!fns || fns.length === 0) {
      // 如果没有绑定对应的消息
      return false;
    }
    for (var i = 0, fn; (fn = fns[i++]); ) {
      fn.apply(this, arguments); // (2) // arguments 是 trigger 时带上的参数
    }
  },
  remove: function (key, fn) {
    var fns = this.clientList[key];
    if (!fns) {
      // 如果 key 对应的消息没有被人订阅,则直接返回
      return false;
    }
    if (!fn) {
      fns && (fns.length = 0);
    } else {
      for (var l = fns.length - 1; l >= 0; l--) {
        var _fn = fns[l];
        if (_fn === fn) {
          fns.splice(l, 1);
        }
      }
    }
  },
};

再定义一个 installEvent 函数,这个函数可以给所有的对象都动态安装发布—订阅功能:

js
var installEvent = function (obj) {
  for (var i in event) {
    obj[i] = event[i];
  }
};

var salesOffices = {};
installEvent(salesOffices);
salesOffices.listen("squareMeter88", function (price) {
  console.log("价格= " + price);
});
salesOffices.listen("squareMeter100", function (price) {
  console.log("价格= " + price);
});
salesOffices.trigger("squareMeter88", 2000000); // 输出:2000000
salesOffices.trigger("squareMeter100", 3000000); // 输出:3000000
js
class myEvent {
  constructor() {
    this.target = [];
  }
  listen(type, fn) {
    this.target[type] = this.target[type] || [];
    this.target[type].push(fn);
  }
  trigger(type, ...args) {
    const fns = this.target[type];
    fns.map((i) => i(...args));
  }
}

const child = new myEvent();

child.listen("type1", function (arg) {
  console.log("type1", arg, "1");
});
child.listen("type2", function () {
  console.log("type2", 2);
});
child.trigger("type1", 2000);
child.trigger("type2", 2000);
  • 观察者模式
js
class Subject {
  // 被观察者的类  被观察者 需要将观察者收集起来
  constructor(name) {
    this.name = name;
    this.state = "非常开心";
    this.observers = [];
  }
  attach(o) {
    // 进行收集
    this.observers.push(o); // on
  }
  setState(newState) {
    this.state = newState;
    this.observers.forEach((o) => o.update(this.name, newState)); // emit
  }
}
class Observer {
  // 观察者
  constructor(name) {
    this.name = name;
  }
  update(s, state) {
    console.log(this.name + ":" + s + "当前" + state);
  }
}
let s = new Subject("小宝宝");
let o1 = new Observer("爸爸");
let o2 = new Observer("妈妈");
s.attach(o1);
s.attach(o2);
s.setState("不开心了");
s.setState("开心了");

发布订阅和观察者区别

实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新

观察者 由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,这种处理方式比较直接粗暴,但是会造成代码的冗余。 发布者 中统一由调度中心进行处理,订阅者和发布者互不干扰,消除了发布者和订阅者之间的依赖。 这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息, 但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。

在 MIT 许可下发布