学习设计模式之前首先要了解五大设计原则:
  • S:单一职责原则,对象应该仅具有单一功能,如果功能负责就进行拆分;
  • O:开放封闭原则,对于扩展开放,对于修改关闭。增加需求时,扩展新代码,不是更改老代码;
  • L:里式替换原则,对象的方法应该可以在不改变运行正确的前提下被它所有的子类替换;
  • I:接口隔离原则,将功能宽泛的接口拆分成多个相对于单一的接口;
  • D:依赖反转原则,面向接口编程,依赖抽象而不依赖具体的实现,使用方知关注暴露的接口,不关注具体实现。

    一、单例模式

    单例模式的定义是:保证一个类仅仅存在一个实例,并提供一个访问它的全局访问点。实现的方法是先判断实例是否存在,如果存在直接返回,如果不存在就先创建在返回,这样确保一个类只存在一个实例。

    1、以下是普通单例代码的实现:
    1. class Singleton() {
    2. static getInstance() {
    3. if (!this.instance) {
    4. this.instance = new Singleton();
    5. }
    6. return this.instance;
    7. }
    8. }

    2、以下是透明的单例模式:
    1. var Singleton = (function() {
    2. var instance
    3. return function() {
    4. if (!instance) instance = this;
    5. return instance;
    6. }
    7. })()

    3、单例模式的缺点:
  • 不适合用于变化的对象,如果这个实例在不同场景下使用,因为实例会进行内部数据共享,这样会导致数据错乱;

  • 如果是透明的单例模式就返回了单一职责原则,因为这个类就实现了两个功能:单例和创建对象;

    为了拆分单例和创建对象,保持单一职责原则,可以采用代理的方式,将创建单例和创建对象分离:
    1. var Person = function(name) { // 创建对象
    2. this.name = name;
    3. }
    4. var Singleton = function(obj) { // 实现单例
    5. var instance
    6. return function() {
    7. if (!instance) instance = new obj();
    8. return instance;
    9. }
    10. }
    11. var PersonSingleton = Singleton(Person);
    12. var a = new PersonSingleton('a');
    13. var b = new PersonSingleton('b');

    二、策略模式

    策略模式的定义:定义一系列的算法,把他们一个一个封装起来,并且使它们之间能相互替换。并且封装的算法具有独立性,不会因为需求的变化而该变。并且策略模式能避免大量的if else和switch case的嵌套。

    一个基于策略模式的程序至少存在两个组成部分。第一个是一组策略类(可变的),策略类封装了具有具体的算法,主要负责具体的计算过程。第二部分就是环境类,环境类接受请求,随后委托给某一个策略类。

    以下是代码实现:
    1. class Customer {
    2. constructor() {
    3. this.kinds = {
    4. normal: p => p,
    5. member: p => p * 0.9,
    6. vip: p => p * 0.5
    7. }
    8. }
    9. cost(kind, m) {
    10. return this.kinds[kind](m);
    11. }
    12. }
    13. const c = new Customer();
    14. console.log(c.cost('normal',100));
    15. console.log(c.cost('member',100));
    16. console.log(c.cost('vip',100));

    三、代理模式

    代理模式的定义:由于一个对象不能直接使用另一个对象,所以需要通过一个代理对象在这两个对象之间起到中介者的作用。在使用者和目标之间加一个代理对象,通过代理对象进行控制。

    代理模式的优点:
  • 代理模式可以代替本体被实例化,并暴露供其他类使用;

  • 代理模式可以把本体实例化推迟到真正需要的时候,对于一些比较费事的本体,可以推迟本体的实例化;

    代理模式的应用最常见的就是图片的懒加载功能,当网络不顺畅的时候,我们需要一个loading图片进行占位,当图片加载完成后进行src的替换。

    以下是代码实现:
    1. var imgFun = (function() {
    2. var imgNode = document.createElement('img');
    3. document.body.appendChild(img);
    4. return {
    5. setSrc: function(src) {
    6. imgNode.src = src;
    7. }
    8. }
    9. })()
    10. var proxyImg = (function() {
    11. var img = new Image();
    12. img.onload = function() {
    13. imgFun.setSrc(this.src);
    14. }
    15. return {
    16. set: function(src) {
    17. imgFun.setSrc('./loading,gif');
    18. img.src = src;
    19. }
    20. }
    21. })()
    22. proxyImg.set('./pic.png');

    以上就是使用代理模式来进行图片的懒加载功能,其中imgFun函数只负责创建img元素,内部的方法将元素插入到body中;proxyImg方法来进行加载loading图片,并且目标图片加载完成后通知imgFun函数的方法。如果不使用代理函数也可以直接调用本体函数的方法。

    四、工厂模式

    工厂模式的定义:提供创建对象的接口,把成员对象的创建工作都交给外部暴露的对象,内部成员对外部属于黑盒,这样能消除对象之间的耦合。

    以下是代码实现:
    1. class Plant{
    2. constructor(name) {
    3. this.name = name;
    4. }
    5. grow() {
    6. console.log('growing~~~~~~');
    7. }
    8. }
    9. class Apple extends Plant{
    10. constructor(name) {
    11. super(name);
    12. this.taste = '甜';
    13. }
    14. }
    15. class Orange extends Plant{
    16. constructor(name) {
    17. super(name);
    18. this.taste = '酸';
    19. }
    20. }
    21. class Factory{
    22. static create(name) {
    23. switch (name) {
    24. case 'apple':
    25. return new Apple('苹果');
    26. case 'orange':
    27. return new Orange('橘子');
    28. }
    29. }
    30. }
    31. let apple = Factory.create('apple');
    32. let orange = Factory.create('orange');

    五、装饰器模式

    装饰器模式的定义:在不改变对象原有自身的属性和方式的基础上,在程序运行的过程中动态的给对象添加方法。常见于react的高阶组件、react-redux的connect组件等。

    装饰器模式适用的场景:
  • 原有的方法保持不变,在原来的方法上挂载其他的方法来满足现在的需求;

  • 函数的结耦,将函数拆分成多个可复用的函数,在将拆分出来的函数挂载在某个待执行函数上,不仅实现了同样的效果还达到了结藕的效果。

    AOP面向切片变成,就是装饰器模式的一种体现。
    1. Function.prototype.before = function(beforefn) {
    2. var self = this; //保存原函数引用
    3. return function() { //返回包含了原函数和新函数的 '代理函数'
    4. beforefn.apply(this, arguments); //执行新函数,修正this
    5. return self.apply(this,arguments); //执行原函数
    6. }
    7. }
    8. var func = function() {
    9. console.log('2');
    10. }
    11. var func1 = function() {
    12. console.log('1');
    13. }
    14. func = func.before(func1)
    15. func();

    六、责任链模式

    责任链模式的定义:责任链模式就是某个请求需要多个对象中的某一个进行处理,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一个链子,并沿着这条链子依次传递请求,直到有对象处理请求为止。

    责任链模式的流程:
  • 发送者知道链条中的第一个接受着,发送者向它发送请求

  • 每一个接受者都对请求进行分析,要么处理它,要么往下传递
  • 每个接受者只知道一个其他对象,就是它下家对象
  • 可以将责任链想象成一个链表就行处理
    以下是代码实现:
    1. class Assign { // 任务类(发送者)
    2. constructor(task) {
    3. this.task = task;
    4. }
    5. }
    6. class WorkFlow { // 接受任务的类
    7. constrcutor(assign) {
    8. this.assign = assign;
    9. }
    10. handler(executor) {
    11. if (this.assign.task === executor.task) {
    12. return executor.start();
    13. } else {
    14. this.handler(executor.successtor);
    15. }
    16. }
    17. }
    18. class Executor { // 处理者
    19. constrcutor(name, task, successtor) {
    20. this.name = name;
    21. this.task = task;
    22. this.successtor = successtor;
    23. }
    24. start() {
    25. console.log(this.name);
    26. }
    27. }
    28. // 实例化处理对象
    29. var c = new Executor('1', '1');
    30. var b = new Executor('2', '2', c);
    31. var a = new Executor('3', '3', b);
    32. // 分配一个任务
    33. var assign = new Assign('2');
    34. // 实例化接受任务类,传递任务
    35. var workFlow = new WorkFlow(assign);
    36. workFlow.handler(a);