设计模式在vue中的应用(七)

5,286 阅读7分钟

前言

目录整理:
设计模式在vue中的应用(一)
设计模式在vue中的应用(二)
设计模式在vue中的应用(三)
设计模式在vue中的应用(四)
设计模式在vue中的应用(五)
设计模式在vue中的应用(六)
设计模式在vue中的应用(七)

为什么要写这些文章呢。正如设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结(来自百度百科)一样,也是想通过分享一些工作中的积累与大家探讨设计模式的魅力所在。
在这个系列文章中为了辅助说明引入的应用场景都是工作中真实的应用场景,当然无法覆盖全面,但以此类推也覆盖到了常见的业务场景



一,适配器模式

适配器相当于和事佬的存在,生活中适配器常见情景:

  • 两口插座转三口
  • android充电线接口转iphone

通过上面两个例子应该很容易发现适配器应用于存在不兼容的场景,在代码中的表现就是多个对象每个对象要执行的方法不同,这时就要通过适配器对要调用的方法进行统一
定义(来自百度百科):

适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中

场景

假设在项目中我们原本使用了Swiper这个组件,使用方式如下:

<swiper :prop-x="px" :prop-y="py" :prop-z="pz" />

后来又有了一个功能更强大,性能更好的滑动组件——NbSwiper,使用方式如下:

<nb-swiper :prop-x="px" :prop-yy="pz" :prop-z="pw" />

对比可以发现

Swiper NbSwiper 组件data
prop-x prop-x px
prop-y py
prop-z prop-yy pz
prop-z pw

现在需求是要把项目中所有的Swiper组件替换为NbSwiper如上表:

两个组件处理除了`prop-x`使用相同,另外几个属性`prop-y`、`prop-yy`、`prop-z`都有一定的区别。

如果直接在代码中一个一个的更改,工作量有点大,而且很难保证在处理一些兼容问题上不出现bug
适配器模式上场

要解决这个问题我们可以选择将Swiper作为一个装饰组件

// Swiper.vue
// 项目原本使用的Swiper组件会被替换掉,我们自己封装一个Swiper组件
<template>
  <!-- 进行转换 -->
  <nb-swiper :prop-x="propX" :prop-yy="propZ" :prop-z="propW" />
</tempalte>
<script>
  export default {
    props: {
      // 接受原本Swiper的props和NbSwiper支持的props
      propX: String,
      propY: String,
      propYy: String,
      propZ: String,
      propW: String,
    }
    ...
  }
</script>

现在我们的工作只需在代码中增加对NbSwiper的新属性值的兼容

二,装饰者模式

定义(来自百度百科):

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象

场景 1

<input />

浏览器原生Input没有验证功能或者验证功能比较弱,我们现在要赋予它比较强的验证功能怎么办——装饰者模式

设计实现

element-ui、iView等各类UI框架大家都有用过,这些框架提供的Input组件应该也不会陌生吧,交互友好功能强大。今天我们的设计会与他们有点与众不同

// 原生input需要验证功能,我们用带有验证能力的valid-input包裹
<valid-input>
  <input v-model="username" type="text" />
</valid-input>

简单思考下
valid-input要做验证需要:

  • 对那个值进行验证
  • 用什么规则进行验证
//input 与username进行绑定
<valid-input field="username" options="[{ rule: required, message: '用户名必须' }]">
  <input v-model="username" type="text" />
</valid-input>

如果哪天我不需要验证功能只需删掉valid-input,这样很灵活的实现功能的扩展

具体实现过程(之前写过一篇相关主题的文章,链接找不到了,代码实现如果需要后期补上

场景 2

checkbox、radio浏览器原生长的丑不说而且在不同的浏览器上还不一样,这对于我们优秀的产品来说是不能忍的

// 常规解决方案,自己通过CSS模式checkbox的交互
<label class="checkbox-wrapper">
  <span class="checkbox">
    <span class="checkbox-inner"></span>
    <input type="checkbox" class="checkbox-input">
  </span>
  Checkbox
</label>

这样的场景实现相信大家也不会陌生,现在的问题的是:

我原本使用checkbox只需
<input type="checkbox" class="checkbox-input">

而现在多了这么多层标签嵌套,还有很多css样式,在使用中很难保证不出错
装饰者模式实现
// stage.vue
// decoratorCheckbox的实现简化每次使用要写很多标签和注明class

<tempalte>
  <div>
    <decorator-checkbox>
      <input type="checkbox" class="checkbox-input">
    </decorator-checkbox>
  </div>
</template>

具体实现过程(之前写过一篇相关主题的文章,链接找不到了,代码实现如果需要后期补上

三,代理模式

先看定义(来自百度百科):

所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。

代理模式和装饰者模式的UML图看起来很相似,有什么区别呢?

懵懂骚年喜欢一位漂亮姑凉

装饰者模式:不管这位姑凉怎么化妆,穿长袖还是短袖(装饰)骚年每次远观看到的终究是姑凉本人

代理模式:骚年按奈不住躁动的心要开始行动了,骚年想到了一个方法首先策反姑凉的好友闺蜜作
        为自己的代理达传达一些小纸条什么的,小纸条最终有没有送到姑凉手中骚年并不能确认
        ,可是少年依旧通宵写着纸条。参见电影《你好,之华》年轻之华
          

情景

我们从后端拿到了一堆数据

  • 数据不为空——进行列表显示
  • 数据为空——显示数据为空的提示
// list.vue
<template>
 <div>
   <div v-if="isEmpty">数据为空</div>
   <div v-else>
     <div v-for="item in data">
       ... do something  
     </div>
   </div>
 </div>
</tempalte>
<script>
  export default{
    props: {
      data: Array
    },
    computed: {
      isEmpty() {
        return this.data.length < 1
      }
    }
  }
</script>

可以发现List组件做了两件事:数据为空的处理、数据不为空的处理,这种设计是不太友好的

Proxy

// ProxyList.vue
<template>
 <div>
   <empty v-if="isEmpty" />
   <list v-else :data="data" />
 </div>
</tempalte>
<script>
  import Empty from './Empty'
  import List from './List'
  
  export default{
    props: {
      data: Array
    },
    computed: {
      isEmpty() {
        return this.data.length < 1
      }
    },
    components:{
      Empty,
      List
    }
  }
</script>

对于数据data的使用者来说,只需关心拿数据渲染列表,数据为空是什么样的完全不关心

结尾

这篇一次介绍了三种设计模式:适配器模式、装饰者模式、代理模式,大多时候我们很容易将他们搞混淆,这也是将他们放在一起的原因。结合上面的例子再来看一下他们的不同点

适配器模式:将多个对象的不同方法调用(objA.methodA(),objB.methodB()),适配成统一调
            用(objA.methodA(),adapterObjB.methodA())

装饰者模式:在不修改原对象的基础上动态的添加功能
            功能:扩展功能
            要求:装饰者要实现和被装饰对象相同的方法

代理模式:为其他对象提供一种代理以控制对这个对象的访问
          功能:控制被代理对象的访问
          要求:与装饰者模式一样要实现和被代理对象相同的方法

本文实现同样适用于react,为什么文章以vue做题?vue的template让我们在理解一些概念的时候可能会有点不适应,而react的jsx可以看做就是在写JavaScript对各种概念实现更灵活
友情提示:设计模式在vue中的应用应该会写一个系列,喜欢的同学记得关注下