WEB前端常用的设计模式总结

创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、生成器模式、原型模式 5 种。

结构型模式:用于描述如何将类或对象按某种布局组成更大的结构。包括代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式  7 种。

行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。模板方法、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式 11种。 

【工厂模式】:

  • 工厂模式是创建对象常见的设计模式,又叫做静态工厂模式,它把创建对象的具体逻辑进行封装,由一个工厂对象决定创建某一个类的实例。
  1. 调用者创建对象时只要知道其名称即可
  2. 扩展性高,如果要新增一个产品,直接扩展一个工厂类即可。
  3. 隐藏产品的具体实现,只关心产品的接口。
  • 应用场景:需要依赖具体环境创建不同的实例,这些实例都有相同的行为,这时候我们可以使用工厂模式,简化实现的过程,同时也可以减少每种对象所需的代码量,有利于消除对象间的耦合,提供更大的灵活性;将new操作简单封装,遇到new的时候就应该考虑是否用工厂模式;

【抽象工厂模式】:

  • 希望在不改变原对象的基础上,通过对其拓展功能和属性来实现更复杂的逻辑。
  • 抽象工厂模式包含如下 4 种角色:抽象工厂、具体工厂、抽象产品、具体产品。

  • 使用场景:继承同一父类、实现同一接口的子类对象,由给定的多个类型参数创建具体的对象。

如汽车厂商不但能生产 Car,也可以生产发动机 Engine,这就是两个产品等级结构,一个产品族。再下面例子中,如果我们需要新增一个Audi 厂商,我们只需要新增一个具体的AudiFactory工厂和一个具体产品的AudiCar 和 AudiEngine类,然后进行实例化,便可以实现。

 

【建造者模式】:

  • 建造者模式(Builder)是一种比较复杂使用频率较低的创建型设计模式,也叫生成器模式,主要用于将一个复杂对象的构建与他的表现分离,使得同样的构建过程可以创建不同的表示。
  • 例如生产汽车,分步骤创建安装不同的零件。如果创建逻辑简单则没有拆分的必要。
  • 使用场景:需要将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图;创建时有很多必填参数需要验证;创建时参数求值有先后顺序、相互依赖;创建有很多步骤,全部成功才能创建对象

【单例模式】:

  • 我们无论创建多少次对象都指向同一个
  • 使用场景:线程池、全局缓存、浏览器中的window对象。
  • 例子:当点击页面的登录案例,会弹出登录弹窗,这个弹窗无论点击多少次只能被创建一次,那么这个弹窗就可以用单例模式创建。

  
  

【原型模式】:

  • 原型模式解决了方法或者属性不能共有的问题,在原型模式中,把实例之间相同的属性和方法提取成共有的属性和方法,即:想让谁共有,就把它放在 类.prototype上。
  • 使用场景:原型模式是基于已有的对象克隆数据,而不是修改原型链;创建对象的代价太大,而同类的不同实例对象属性值基本一致,通过原型克隆的方式可以节约资源;不可变对象可以用浅克隆实现,可变对象用深克隆实现,深克隆占用资源多;同一对象不同时间版本,可以对比没变化的浅克隆,变化的深克隆,然后新版本替换旧版本。

 【装饰器模式】:

  • 希望在不改变原对象的基础上,通过对其拓展功能和属性来实现更复杂的逻辑。
  • 使用场景:装饰器类是对原始功能的增强;装饰器类和原始类继承同样的父类,这样我们可以对原始类嵌套多个装饰器类;主要解决继承关系过于复杂的问题,通过组合来替代继承;可以通过对原始类嵌套使用多个装饰器。

【组合模式】:

  • 组合模式作用于将多个部分通过组合变成一个整体
  • 通过对象的多态表现,使得用户对单个对象和组合对象的使用具有一致性。
  • 使用场景:将一组对象组织成树形结构,以表示一种 “部分-整体 ”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性(递归遍历)。

例1、

例2、


【代理模式】:

  • 所谓的的代理模式就是为一个对象找一个替代对象,以便对原对象进行访问。使用代理的原因是我们不愿意或者不想对原对象进行直接操作,我们使用代理就是让它帮原对象进行一系列的操作,等这些东西做完后告诉原对象就行了。

  • 使用场景:给原类添加非功能性需求,为了将代码与原业务解耦;业务系统的非功能性需求开发:监控、统计、鉴权、限流、日志、缓存。


【外观模式】:

  • 为子系统的一组接口提供一个一致的界面,定义了一个高层接口,这个接口使子系统更加容易使用。
  • 外观模式很常见,它其实就是通过一个单独的函数,来简化对一个或多个更大型,更为复杂的函数的访问,是一种对复杂操作的封装,通过把复杂操作封装,调用时直接用方法调用。
  • 使用场景:将多个后端接口请求合并为一个,提高响应速度,解决性能问题;通过封装细粒度接口,提供组合各个细粒度接口的高层次接口,来提高接口的易用性。像Ajax封装。

【桥接模式】:

  • 桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  • 一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,一个类存在两个独立变化的维度,且这两个维度都需要进行扩展,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使他们在抽象层建立一个关联关系。比如,那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统。
  • 使用场景:将抽象和实现解耦,让它们可以独立变化;一个类存在多个独立变化的维度,我们通过组合的方式,让多个维度可以独立进行扩展;非常类似于组合优于继承原则。

【享元模式】:

  • 运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,
  • 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
  • 使用场景:一个程序中使用了大量的相似对象;由于使用了大量对象,造成很大的内存开销;对象的大多数状态都可以变为外部状态;剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。
  • 享元模式要求将对象的属性划分为内部状态和外部状态(状态在这里通常指属性),其目标是尽量减少共享对象的数量。

有个服装厂,生产了男女服装各50种款式,为了推销需要找模特来拍照,正常可能会找男女模特各50个,每个模特分别穿一种服装拍一组照片。但是如果这种,后面款式越来越多,也就意味着模特也越来越多,仔细分析一下,其实不管有多少种类衣服,我们只需要男女各一个模特来穿衣服进行拍照也可实现该需求。


【观察者模式】:

  • 观察者模式又叫发布订阅模式消息模式。这种模式一般会定义一个主体和众多个的个体,这里的主体可以想象成一个消息中心,里面有各种各样的消息,而众多的个体可以订阅里面不同的消息,当未来消息中心发布某个消息的时候,订阅过他的个体就会得到通知。
  • 使用场景:将观察者与被观察者解耦;发布订阅模式有发布订阅调度中心(中间商),观察者模式没有。

例1、

例2:

      售楼处卖房子。很多的想买房子的人来咨询房子的事情,但是现在有些房子还需要等待最终的结果,打算买房子的人就把自己的个人信息都给了售楼处的人,售楼处的人等这些房子有了消息,统一打电话告诉打算房子人他们想要知道的消息,就不用每次都给来一趟售楼处来知道关于当前房子的事。

    // 售楼处
    var saleOffices = {}
    // 保存需要买房的登记的用户(订阅者)
    saleOffices.clientList = []
    // 根据订阅相同类别的房的订阅者分类保存起来
    saleOffices.listen = function (key, fn) {
      if (!this.clientList[key]) {
        this.clientList[key] = []
      }
      this.clientList[key].push(fn)
    }
    // 发布消息给订阅者
    saleOffices.trigger = function () {
      // [].shift.call(arguments) arguments类数组转化为数组
      var key = [].shift.call(arguments)
      fns = this.clientList[key]
      // 不存在要通知的订阅者
      if (!fns || fns.length === 0) {
        return false
      }
      for (var i = 0, fn; fn = fns[i++];) {
        fn.apply(this, arguments)
      }
    }

    saleOffices.listen('squareMeter88', function (price) {
      console.log('价格:' + price);
    })
    saleOffices.listen('1000ping', function (price) {
      console.log('价格:' + price);
    })

    saleOffices.trigger('squareMeter88', 20000)
    saleOffices.trigger('1000ping', 40000)

【策略模式】:

  • 主要用于多种状态或策略需要进行选择的时候,将所有选择都封装在一起,只给外部暴露必要的接口。
  • 使用场景:定义一族算法族,将每个算法分别封装起来,让他们可以相互替换;避免冗长的 if-else 或 switch 分支判断。
 


  请输入:

 例2、

1.公司发奖金,根据不同等级奖金比例也是不同,为了判断每一个人应该根据等级和等级所对应的倍数得到的奖金,写出下面的代码,需要计算的部分封装成了类,Bonus 作为了Context 拥有了执行策略的能力也就是执行这些算法类。
2.定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对 Context发起请求的时候,Context总是把请求委托给这些策略对象中间的某一个进行计算。


【迭代器模式】:

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素(遍历集合对象),而又不暴露该对象的内部表示。

使用场景:

1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。 
3、为遍历不同的聚合结构提供一个统一的接口。

    // hasNext():判断迭代是否结束,返回Boolean
    // next():查找并返回下一个元素
    const arr = [1, 'red', false, 3.14]

    function Iterator(item) {
      this.item = item
      this.index = 0
    }
    Iterator.prototype = {
      hasNext: function () {
        return this.index < this.item.length
      },
      next: function () {
        return this.item[this.index++]
      }
    }
    const iterator = new Iterator(arr)
    while (iterator.hasNext()) {
      console.log(iterator.next());
    }

    // 数字区间进行迭代
    function Range(start, end) {
      return {
        [Symbol.iterator]: function () {
          return {
            next() {
              if (start < end) {
                return {
                  value: start++,
                  done: false
                }
                return {
                  done: true,
                  value: end
                }
              }
            }
          }
        }
      }
    }
    for (num of Range(1, 6)) {
      console.log(num);
    }
  

【命令模式】:

  •  命令模式:将请求以命令的形式包裹在对象中,这个对象传递给调用者或理解为调用者寻找可以处理该命令的合适对象,这个对象会处理传过来命令。
  • 使用场景:命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等;将命令的发起者和执行者解耦。

例子:比如电视它具有开和关的功能,用代码来表示的话就是一个'电视类'有两个方法'开电视,'关电视'。一般执行的时候'电视.开电视',对应上面说的'主要解决来讲'开电视这个行为和开电视这个实现都是电视来做的两者是在一起。
在定义上:这时候遥控器出现了,'开/关电视'的行为请求。可以看做两个命令,并且将这两个命令单独写成对象现在就多了两个对象'开电视' 和'关电视'他们是一组命令,多了个'遥控器'可以调用这些命令,并且把这些命令给了电视,电视依旧还是有'开电视', '关电视'的方法,不过不是直接请求调用而是遥控器间接调用了。

以去饭店吃饭为例,我们只需关心菜单上的菜,而不会必须要知道是那个厨师做的菜,因为服务员会帮我们将菜单上的'指令' 和厨师进行匹配。


  
  
  
  

【适配器模式】:

  • 适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。
  • 使用场景:适配器模式用于补救设计上的缺陷,将不兼容的接口变得兼容;封装有缺陷的接口设计;统一多个类的接口设计;替换依赖的外部系统;兼容老版本接口;适配不同格式的数据。

百度地图和谷歌地图的渲染:


【职责链模式】:

  • 链模式是实现链式调用的主要方法,通过在自身方法中返回自身的方式,在一个对象连续多次调用自身的方法时可以简化。

【模板方法模式】:

  • 模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法和封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
  • 在模板方法模式中,子类实现中的相同部分被上移到父类中,而将不同的部分留待子类来实现。
  • 使用场景:一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;子类中公共的行为应被提取出来并集中到一个公共父类中的避免代码重复。
 

【访问者模式】:

  • 访问者模式(数据访问对象模式)主要用来抽象和封装一个对象来对数据源进行访问和存储,这样可以方便对数据的管理,以及避免数据间的重复,覆盖等问题出现。

【等待者模式】:

  • 通过对多个异步进程的监听,对未来事件进行统一管理

【备忘录模式】:

  • 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
  • 使用场景:分页控件;撤销组件。

【状态模式】:

  • 状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

  • 通常我们谈到封装,一般都会优先封装对象的行为,而不是对象的状态。但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部,所以 button 被按下的的时候,只需要在上下文中,把这个请求委托给当前的状态对象即可,该状态对象会负责渲染它自身的行为。

有一个电灯,电灯上面只有一个开关。当电灯开着的时候,此时按下开关,电灯会切换到关闭状态;再按一次开关,电灯又将被打开。同一个开关按钮,在不同的状态下,表现出来的行为是不一样的。


【中介者模式】:

  • 中介者模式的主要作用是解除对象之间的强耦合关系,通过增加一个中介者,让所有的对象通过中介者通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者对象即可。
  • 中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(依赖关系)转换成一对多(星状关系)。原本一个对象要跟n个对象交互,现在只需要跟一个中介对象交互,从而最小化对象间的交互关系,降低了代码复杂度,提高了代码的可读性和可维护性。
  • 使用场景;系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象;想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

【解释器模式】:

  • 给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
  • 这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式常被用在 babel、sass、less 解析、符号处理引擎等。
  • 使用场景:可以将一个需要解释执行的语言中的句子,表示为一个抽象语法树;一些重复出现的问题可以用一种简单的语言来进行表达;一个简单语法需要解释的场景。

你可能感兴趣的:(前端)