前端常用设计模式

什么是设计模式?

​  设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,设计模式并不是一种固定的公式,而是一种思想,是一种解决问题的思路;使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码可维护性。

  设计模式不区分编程语言,设计模式是解决通用问题和提效的解决方案;通常在我们解决问题的时候,很多时候不是只有一种方式,我们通常有多种方式来解决;但是肯定会有一种通用且高效的解决方案,这种解决方案在软件开发中我们称它为设计模式;

  项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的场景及其原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

什么是设计原则?

  设计原则是基于设计模式产生的一套方法论;通常在做很多事情的时候,都会有一定的规范制约;在软件开发的过程中,我们可以将设计原则视为一种约定俗成的开发规范,但不是必须要遵循的;

  • 单一职责原则(Single Responsibility Principle)
  • 开闭原则(Open Closed Principle)
  • 里氏替换原则(Liskov Substitution Principle)
  • 迪米特法则(Law of Demeter)
  • 接口隔离原则(Interface Segregation Principle)
  • 依赖倒置原则(Dependence Inversion Principle)

  6 个原则的首字母(里氏替换原则和迪米特法则的首字母重复,只取一个)联合起来就是:SOLID(稳定的),其代表的含义也就是把这 6 个原则结合使用的好处:建立稳定、灵活、健壮的设计;

设计原则

  • 单一职责原则(Single Responsibility Principle)

    一个类只做一件事;一个类应该只有一个引起它修改的原因,应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。导致脆弱的设计。例如:要实现逻辑和界面的分离

  • 开闭原则(Open Closed Principle)

   一个软件实体如类,模块和函数应该对扩展开放,对修改封闭,即在程序需要进行拓展的时候,不能去修改原有的代码。

  • 里氏替换原则(Liskov Substitution Principle)

   不要破坏继承体系;程序中的子类应该可以替换父类出现的任何地方并保持预期不变。所以子类尽量不要改变父类方法的预期行为。

  • 迪米特法则(Law of Demeter)

   降低耦合度,一个类或对象应该对其它对象保持最少的了解。只与直接的朋友(耦合)通信,不与朋友的朋友通信。

  • 接口隔离原则(Interface Segregation Principle)

   设计接口的时候要精简单一;当类 A 只需要接口 B 中的部分方法时,因为实现接口需要实现其所有的方法,于是就造成了类 A 多出了部分不需要的代码。这时应该将 B 接口拆分,将类A需要和不需要的方法隔离开来。

  • 依赖倒置原则(Dependence Inversion Principle)

   细节应该依赖于抽象 ,抽象不依赖于细节;把抽象层放在程序设计的最高层,并保持稳定,程序的细节变化由低层的实现层来完成。(例如实现一个组件基类,存放组件的id、apperance等信息,具体的组件类继承基类,实现具体的UI及功能 )

为什么要使用设计模式?

  1. 设计模式是前人根据经验总结出来的,使用设计模式,就相当于是站在了前人的肩膀上。
  2. 设计模式使程序易读。熟悉设计模式的人应该能够很容易读懂运用设计模式编写的程序。
  3. 设计模式能使编写的程序具有良好的可扩展性,满足系统设计的开闭原则。
  4. 设计模式能降低系统中类与类(或者function与function)之间的耦合度。
  5. 设计模式能提高代码的重用度。
  6. 设计模式能为常见的一些问题提供现成的解决方案。
  7. 设计模式增加了重用代码的方式。比如装饰器模式,在不使用继承的前提下重用系统中已存在的代码。

设计模式分类

  •  5种创建型
  •  7种结构型
  •  11种行为型
  • 创建型:抽工单建原型

   抽象工厂、工厂、单例、建造者、原型

  •  结构型:桥代理装饰适配器,享元组合成门面

      桥接、代理、装饰器、适配器、享元、组合、门面(外观)

  •  行为型:观察模板迭代的状态,命令中介解释职责链,访问策略备忘录

   观察者、模板、迭代、状态、命令、中介者、解释器、职责链、访问者、策略、备忘录

日常工作常用的设计模式

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

  定义:

    发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知;发布订阅模式有发布订阅调度中心(中间商),观察者模式没有!

  应用场景:

  DOM元素节点事件实时触发方法
  vue的响应式原理

  大白话解释(生活中的场景):

  假设你在淘宝上看上了某件商品,临近双11,但是价格有点高;于是你订阅了某种类型某种规格的商品降价通知,到双11降价的时候会系统推送相关消息给对应的用户;这时发布-订阅模式就产生了;用户是订阅者,淘宝是调度中心,商家是发布者;商家降价之后,会通过发布中心推送相关的消息给指定的订阅者;

 1 // 定义一个发布者对象
 2 var pub = {
 3      缓存列表,存放订阅者回调函数
 4     list: {}, 5     subscribe: function (key,fn) {
 6          如果没有该消息的缓存列表,就创建一个空数组
 7         if (!this.list[key]) {
 8             this.list[key] = [];
 9         }
10          将回调函数推入该消息的缓存列表
11         .list[key].push(fn);
12     },1)">13 
14      取消订阅方法
15     unsubscribe: 16          如果有该消息的缓存列表
17         if (18              遍历缓存列表
19             for (var i = this.list[key].length - 1; i >= 0; i--) {
20                  如果存在该回调函数,就从缓存列表中删除
21                 this.list[key][i] === fn) {
22                     this.list[key].splice(i,1);
23                 }
24             }
25 26 27      发布方法
28     publish:  () {
29          获取消息类型
30         var key = Array.prototype.shift.call(arguments);
31          获取该消息的缓存列表
32         var fns = .list[key];
33          如果没有订阅消息,就返回
34         if (!fns || fns.length === 035             return;
36 37          遍历缓存列表,执行回调函数
38         var i = 0; i < fns.length; i++39             fns[i].apply(,arguments);
40 41     }
42 }
43 
44  定义一个订阅者对象A
45 var subA =  (name) {
46     console.log('A收到了消息:' + name);
47 48 
49  定义一个订阅者对象B
50 var subB = 51     console.log('B收到了消息:' +52 53 
54  A订阅了test消息
55 pub.subscribe('test'56  B订阅了test消息
57 pub.subscribe('test'58  发布了test消息,传递了参数'hello'
59 pub.publish('test','hello'60  A取消订阅了test消息
61 pub.unsubscribe('test'62  发布了test消息,传递了参数'world'
63 pub.publish('test','world'64 
65  输出:
66  A收到了消息: hello
67  B收到了消息: hello
68  A取消订阅了test消息

  代理模式:
   定义:
    为一个对象提供一个代用品或占位符,以便控制对它的访问!
  应用场景:
    Proxy代理对象;
     图片预加载,占位loading图片就是代理;

  大白话解释(生活中的场景):

  明星往往拥有很多粉丝,也会接收到很多粉丝送的礼物;但是这些礼物一般都是通过 粉丝 ->  经纪人  ->  明星 这个流程才到达明星的手里;这里的经纪人就是明星的代理对象,有些无用或者恶作剧之类的礼物,可以直接在代理对象(经纪人)这一层直接拦截或者过滤掉;
var fans =    flower() {
 3         agent.reception('花' 5  6 
 7 var agent = 8     reception:  (gift) {
 9         console.log('粉丝送的:' + gift);  粉丝送的:花
if (gift !== '花'11             star.reception('花'13 14 15 
16 var star =17     reception: 18         console.log('收到粉丝的:' + gift);
19 20 21 
22 fans.flower();

  策略模式:
  定义:
    定义一系列算法,并将这些算法各自封装成策略类(方法),然后将不变的部分和变化的部分分离开来,并且这些算法可以相互替换
  应用场景:
    主要用来消除或减少逻辑分支判断,避免冗长的if-else或switch分支判断

  大白话解释(生活中的场景):

  政府事务办理中心,是近几年来提升居民、市民办事的一项策略调整;在政务中心没出现之前,市民办事都需要去对应的政府机关窗口分流办理相应手续;政务大厅的出现将公安、财务、劳动、税务、供电等多个部门事务办理手续集中到政务中心来办理,这样提高了各级机关单位的办事效率,减少了维护办事人群的秩序,也提升了市民的用户体验;市民只需要记住办理政务相关手续去政务大厅即可;

/** 策略模式改造前 */
 2      calculateBonus(level,salary){
 3         if(level === 'S'){
 4             return salary*4 6         
if(level === 'A'return salary*3
10 
if(level === 'B'12             return salary*2
16     console.log(calculateBonus("S",14000));  56000
17     console.log(calculateBonus("A",10000)); 30000
18     console.log(calculateBonus("B",5000));  10000
* 策略模式改造后var strategies  = 3         "S":(salary){
return salary*4
        },1)"> 6         "A": 7             return salary*3 8  9         "B":10             11 var calculateBonus =(level,1)">15          strategies[level](salary);
    } 
17     console.log(calculateBonus("S",1)">18     console.log(calculateBonus("A",10000));  19     console.log(calculateBonus("B",5000));   10000

  单例模式
   定义:
    保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  应用场景:
    弹窗组件;
     加载某个script资源或者只能存在一个全局变量;
     React状态管理工具Redux
  大白话解释(生活中的场景):

  假设甲去当地派出所办理身份证相关手续,这时公安机关会对甲的身份进行验证查询,如果甲已经有过身份登记信息,就直接办理挂失补办手续;否则走新增户籍人手续;一个合法公民不可能同时存在两张合法身份证件;

 1 let Singleton = this.name = name;
this.instance = null 5 
 6 Singleton.prototype.getName =  7     console.log(.name);
 9  
10 Singleton.getInstance =  (name) { 
11     .instance) {
12         return .instance;
new Singleton(name);
15 16 
17 let Winner = Singleton.getInstance('Winner'18 let looser = Singleton.getInstance('looser'19 
20 console.log(Winner === looser);  true
21 console.log(Winner.getName());  'Winner'
22 console.log(looser.getName());   'Winner'

  总结

  1. 在日常开发中,应该尽可能的遵守设计原则和合理选用设计模式;
  2. 合理使用设计模式便于我们写出可维护性高、拓展性强的代码;
  3. 设计模式一共分为三大类,分别是 :5种创建型、7种结构型、11种行为型;
  4. 设计模式不区分编程语言,设计模式是解决通用问题和提效的解决方案;
  5. 编程的目的是解决现实问题,同样每一种设计模式的原理都能在现实社会中找到对应的场景,这也是它能被广泛应用的根本原因;

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件举报,一经查实,本站将立刻删除。

本文链接:https://www.f2er.com/3188585.html

大家都在看

  • 飞码LowCode前端技术系列:如何便捷快速验证实现投产及飞码探索

    本篇文章从数据中心,事件中心如何协议工作、不依赖环境对vue2.x、vue3.x都可以支持、投产页面问题定位三个方面进行分析。
    2023-11-16 博文
  • 如何优雅使用 vuex

    大纲 本文内容更多的是讲讲使用 vuex 的一些心得想法,所以大概会讲述下面这些点: Q1:我为什么会想使用 vuex 来管理数据状态交互? Q2:使用 vuex 框架有哪些缺点或者说副作用? Q3:我是如何在项目里使用 vuex 的? 初识 vuex 对于 vuex,有人喜欢,有人反感 喜欢的人觉
    2023-11-16 博文
  • 第三方组件及计算属性传参的问题解决方式

    1. 前言 唉,好想玩滋嘣。 2. 计算属性直接传参接收不到 表格数据某一列需要用的计算属性时,模板中使用计算属性 fullName 就会直接调用 fullName 函数,而在模板中 fullName(item) 相当于fullName()(item),此处为函数柯里化。 &lt;el-table-
    2023-11-16 博文
  • 记录--Vue3基于Grid布局简单实现一个瀑布流组件

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 在学习Grid布局之时,我发现其是CSS中的一种强大的布局方案,它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,在刷某书和某宝首页时,我们发现其展示方式就是一种瀑布流,是一种流行的网站页面布局,视觉表……
    2023-11-16 博文
  • 用强数据类型保护你的表单数据-基于antd表单的类型约束

    接口数据类型与表单提交数据类型,在大多数情况下,大部分属性的类型是相同的,但很少能做到完全统一。我在之前的工作中经常为了方便,直接将接口数据类型复用为表单内数据类型,在遇到属性类型不一致的情况时会使用any强制忽略类型错误。后来经过自省与思考,这种工作模式会引起各种隐藏bug,一定有更……
    2023-11-16 博文
  • pinia的使用

    前言 最近新开了个项目,以前老项目都是vue2+vuex开发的,都说用vue3+pinia爽得多,那新项目就vue3+pinia吧。这里记录一下pinia的使用。 使用方法 安装pinia: npm i pinia main.js中引入pinia: //main.js import { create
    2023-11-16 博文
  • 记录--让我们来深入了解一下前端“三清”是什么

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前端“三清” 在前端开发中,我们经常听到关于“三清”的说法,即 window、document、Object。这三者分别代表了 BOM(浏览器对象模型)、DOM(文档对象模型)以及 JS 的顶层对象。在这个体系中,我们通过 JavaScr
    2023-11-16 博文
  • 记录--啊?Vue是有三种路由模式的?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 众所周知,vue路由模式常见的有 history 和 hash 模式,但其实还有一种方式-abstract模式(了解一哈~) 别急,本文我们将重点逐步了解: 路由 + 几种路由模式 + 使用场景 + 思考 + freestyle 路由概念
    2023-11-16 博文