前端框架中的设计模式 | 青训营

68 阅读5分钟

什么是设计模式

设计模式是在某种场合下对某个问题的一种解决方案。设计模式是通过概念总结出来的模版,总结出来的固定的东西。每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。

在前端开发中,设计模式是一种被广泛应用的思想。设计模式可以帮助开发者解决常见的问题,并提供可重用的解决方案。本文将会介绍前端常见的设计模式,并通过代码详解它们的实现。

为什么使用设计模式

设计模式是各种业务场景的最佳实践,有助于我们在写日常业务中时,提高自身的思路。例如单例模式,就可以用于登陆框,模态框等场景。每种设计模式,必然有其适合应用的场景,灵活运用设计模式,可以提高代码的可维护性,还可提升自身思维能力。

设计模式六大原则

所有的设计模式都需要遵循下面的六大原则

1、开闭原则

开闭原则的意思是:对扩展开放,对修改关闭,在程序需要进行拓展的时候,不能去修改原有的代码

2、里氏代换原则

里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现,里氏代换原则是对开闭原则的补充,实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范

3、依赖倒转原则

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体 

4、接口隔离原则

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好,它还有另外一个意思是:降低类之间的耦合度,此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合

5、最少知道原则

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立

6、合成复用原则

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承

常用的设计模式

代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问。
    代理模式的生动理解可以类比为一个人找代理去购物。这个人想购买一些物品,但是由于某些原因无法亲自前往商店购买,于是他找到了一个代理人去帮他购买。

    代理模式中的代理对象就像是这个代理人,它可以代替真实对象进行一些操作。

例:HTML元 素事件代理

<ul id="ul">
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<script>
  let ul = document.querySelector('#ul');
  ul.addEventListener('click', event => {
    console.log(event.target);
  });
</script>
  • 优点: 代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。 代理对象可以扩展目标对象的功能;通过修改代理对象就可以了,符合开闭原则;

  • 缺点:处理请求速度可能有差别,非直接访问存在开销。

迭代器模式

    迭代器模式:简单来说是在不暴露数据类型的情况下访问集合中的数据
    应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口
    

例:

(function (a, b, c) {
 const arg = arguments;
  const iterator = arg[Symbol.iterator]();
  
  console.log(iterator.next()); // {value: 1, done: false}
  console.log(iterator.next()); // {value: 2, done: false}
  console.log(iterator.next()); // {value: 3, done: false}
  console.log(iterator.next()); // {value: undefined, done: true}
})(1, 2, 3)
  • 优点:可实现统一遍历接口,符合单一功能和开放封闭原则

  • 缺点:迭代器模式将存储数据遍历数据两个职责拆分 ; 如果新添加一个集合类需要增加该集合类对应的迭代器类,类的个数成对增加。在一定程度上增加了系统复杂性 ;

原型模式

对于前端来说,当新创建的对象和已有对象存在较大共性时,可以通过对象的复制来达到创建新的对象,这就是原型模式。

例:

// Object.create()实现原型模式
const user = {
 name: 'zhangsan',
  age: 18
};
let userOne = Object.create(user);
console.log(userOne.__proto__); // {name: "zhangsan", age: 18}
 
 
// 原型链继承实现原型模式
class User {
 constructor (name) {
   this.name = name;
  }
  getName () {
   return this.name;
  }
}
 
class Admin extends User {
 constructor (name) {
   super(name);
  }
  setName (_name) {
   return this.name = _name;
  }
}
 
const admin = new Admin('zhangsan');
console.log(admin.getName());
console.log(admin.setName('lisi'));
  • 优点:性能高,流程简单
  • 缺点:实现复杂

单例模式

单例模式是一种创建型设计模式
顾名思义,单例模式中Class的实例个数最多为1。当需要一个对象去贯穿整个系统执行某些任务时,单例模式就派上了用场。

例子:


var single = (function(){
    var unique;

    function getInstance(){
    // 如果该实例存在,则直接返回,否则就对其实例化
        if( unique === undefined ){
            unique = new Construct();
        }
        return unique;
    }

    function Construct(){
        // ... 生成单例的构造函数的代码
    }

    return {
        getInstance : getInstance
    }
  • 优点: 划分命名空间,减少全局变量 ;增强模块性,把自己的代码组织在一个全局变量名下,放在单一位置,便于维护; 且只会实例化一次。简化了代码的调试和维护
  • 缺点:由于单例模式提供的是一种单点访问,所以它有可能导致模块间的强耦合从而不利于单元测试;无法单独测试一个调用了来自单例的方法的类,而只能把它与那个单例作为一个单元一起测试。

本文借鉴:

t.csdn.cn/JIwKU
t.csdn.cn/aTdAj
t.csdn.cn/ggwHA