[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式

2021-10-12  本文已影响0人  woow_wu7

导航

[react] Hooks

[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式

[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[数据结构和算法01] 二分查找和排序

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例

[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson

(一) 前置知识

(1)前置知识

scaffolding 脚手架
cluster 集群
handler 处理
Typography 排版

invoke 调用
maintainer 维护者
fly 苍蝇
flyweight 享元模式
recover 回收

pattern 模式
design pattern 设计模式
strategy 策略模式
singleton 单列模式

expires 过期

(2)layout

xs => extra small 超小
sm => small
md => medium 中等的 中号的
lg => large
xl => extra large 超大

(3) pro-table

Protable => search={false}自定义查找

(4) 本地分支关联远程分支

git branch --set-upstream-to=origin/remote_branch your_branch

(二) 命令模式

// 命令模式

    // Invoker 发布者
    // 1. 发布者中有 命令对象
    // 2. 发布者发布命令,即调用命令对象中的方法,而命令对象又会去调用接收者的方法
    class Invoker {
      constructor(command) {
        this.command = command
      }
      invoke = () => {
        this.command.execute()
      }
    }

    // Command 命令对象
    // 1. 命令对象中有 接收者
    // 2. 命令对象 调用接收者中的方法
    class Command {
      constructor(receiver) {
        this.receiver = receiver
      }
      execute() {
        this.receiver.execute()
      }
    }

    // Receiver 接收者
    // 接收者 执行最终的请求
    class Receiver {
      execute() {
        console.log('接收者执行方法')
      }
    }

    const cook = new Receiver() // 厨师-接收者-做饭
    const shop = new Command(cook) // 商店-命令对象-命令厨师做饭
    const user = new Invoker(shop) // 客户-调用者-发布命令
    user.invoke()

(三) 享元模式 flyweight pattern

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <input type="radio" value="red" name="color" checked> 红色
  <input type="radio" value="yellow" name="color"> 黄色
  <input type="radio" value="blue" name="color"> 蓝色
  <button onclick={draw()}>绘制</button>
  <div id="container">颜色</div>
  <script>
    // 享元模式
    class FlyweightDiv {
      constructor() {
        this.element = document.createElement('div') // ----- 内部属性,不变
      }
      setColor = (color) => { // ---------------------------- 外部外部,变化
        this.element.style=`width: 100px; height: 100px; background-color:${color}`
      }
    }
    const myDiv = new FlyweightDiv()
    const draw = () => {
      const btns = Array.from(document.getElementsByName('color'))
      const checkedBtn = btns.find(btn => btn.checked)
      const color = checkedBtn?checkedBtn.value:'red'
      console.log(`color`, color)
      myDiv.setColor(color)
      document.getElementById('container').appendChild(myDiv.element)
      // 注意:这里每次都是同一个element,所以不会新添加
    }
  </script>
</body>
</html>

(四) 组合模式 compose pattern

image.png
实现一个树形文件结构
- folder 文件夹
- file 文件
- 文件夹下可以创建文件,文件不能再创建文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 组合模式
      // 1. Floder 树对象
      // 2. File 叶对象
      // 3. Folder 和 File 接口统一,都具有 add 和 scan 方法
      class Floder {
        constructor(name) {
          this.name = name;
          this.files = [];
        }
        add = (file) => {
          this.files.push(file);
        };
        scan = () => {
          this.files.forEach((file) => file.scan());
        };
      }

      class File {
        constructor(name) {
          this.name = name
        }
        add = () => {
          throw new Error('文件下面不能添加文件,文能在文件夹下添加文件')
        }
        scan = () => {
          console.log(`正在扫描文件: ${this.name}`)
        }
      }

      const carFolder = new Floder('车')
      const bmwFile = new File('宝马')
      carFolder.add(bmwFile)
      carFolder.scan()
    </script>
  </body>
</html>

(五) 复习 - 代理模式 proxy pattern

实战

(5-1) 代理模式 - ajax请求添加缓存功能

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 代理模式

      // 正常的请求
      const request = (params) => {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            return resolve(`data: ${params}`);
          }, 1000);
        });
      };

      // 代理模式 - 代理请求
      const proxyRequest = (params) => {
        return cache(params);
      };

      // map 用来做参数和返回值的映射
      const mapInstance = new Map();

      // 利用map实现参数和返回值的缓存
      const cache = async (params) => {
        // 参数在map中存在,直接返回参数对应的请求结果
        if (mapInstance.has(params)) {
          return mapInstance.get(params);
        }
        // 参数在map中不存在,则请求并做参数和请求返回值的映射
        // 所以:当参数存在时,直接从map中返回,而不是重新请求
        console.log("在本例中,我只会执行一次");
        const res = await request(params);
        mapInstance.set(params, res);
        return res;
      };

      proxyRequest("参数").then((res) => console.log(`res`, res));
      setTimeout(() => {
        proxyRequest("参数").then((res) => console.log(`res`, res));
      }, 2000);
    </script>
  </body>
</html>
image.png

(5-2) 代理模式 - 代理缓存,处理缓存过期时间

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      class Storage {
        constructor(type, expires) {
          this.storage = window[type];
          this.expires = expires;
        }

        // 存
        // 代理原生的 setItem() 方法,添加缓存过期时间
        setItem = (key, value) => {
          this.storage.setItem(
            key,
            JSON.stringify({
              value,
              setTime: +new Date(), // --------------------- ( 存 ) 的时间戳
            })
          );
        };

        // 取
        // 代理原生的 getItem() 方法,根据传入的过期时间,来判断数据是否过期
        getItem = (key) => {
          const now = +new Date(); // --------------------- ( 取 ) 的时间戳
          const { value, setTime } = JSON.parse(this.storage.getItem(key));
          if (now - setTime > this.expires) {
            this.storage.removeItem(key); // -------------- 过期删除
          }
          return this.storage.getItem(key);
        };
      }

      const localStorageInstance = new Storage("localStorage", 5000);
      localStorageInstance.setItem("name", "woow_wu7");

      console.log("未过期", localStorageInstance.getItem("name"));

      setTimeout(() => {
        console.log("过期", localStorageInstance.getItem("name"));
      }, 6000);
  
    </script>
  </body>
</html>
image.png

(六) 复习 - 策略模式 strategy pattern

实战

(6-1) 计算奖金

(1) 不做任何优化的写法
- 缺点
  - 有很多if...else
  - 缺乏扩展性:如果要添加C绩效,就得修改内部的函数实现,违反了开放封闭原则即可扩展但 ( 不可修改 )
  - 复用性差
- bonus 奖金
- performance 绩效
- salary 工资
const getBonus = (performance, salary) => {
    if (performance === 'S') return 4 * salary;
    if (performance === 'A') return 3 * salary;
    if (performance === 'B') return 2 * salary;
}
getBonus('A', 1000) // 输出 3000



-------
(2) 使用 策略模式 strategy pattern 重构代码
- 优点
  - 避免多个if...esle
  - 符合开放/封闭原则,扩展不需要修改原来的逻辑,也不会影响原来的逻辑
  - 所有计算奖金bonus的逻辑都不在getBonus函数即环境类context中,而是分布在各个策略对象strategyPattern中
  - context环境类不负责计算,只是负责将请求委托给strategyPattern策略类
const strategyPattern = {
  S: (salary) => 4 * salary,
  A: (salary) => 3 * salary,
  B: (salary) => 1 * salary,
} 
const getBonus = (performance, salary) => strategyPattern[performance](salary)
getBonus('A', 1000) // 输出3000

(6-2) 表单验证

(1) 不做任何优化

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <form action="javascript:void(0)" method="post">
    <input type="text" name="username">
    <input type="text" name="password">
    <button>提交表单</button>
  </form>
  <script>
    const form = document.querySelector('form')
    form.onsubmit = (e) => {
      if (form.username.value === '') {
        console.log('用户名不能为空')
        return false
      }
      if (form.password.value.length < 6) {
        console.log('密码不能少于6位')
        return false
      }
      console.log('提交成功')
      return true
    }
  </script>
</body>
</html>
image.png
(2) 使用策略模式优化 - 表单验证

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <form action="javascript:void(0)" method="post">
      <input type="text" name="username" />
      <input type="text" name="password" />
      <button>提交表单</button>
    </form>
    <script>
      const form = document.querySelector("form");

      // strategy 策略对象
      const strategy = {
        notEmpty: (value, err) => { if (!value) return err },
        minLength: (value, length, err) => { if (value.length < 6) return err}
      }

      // Validator 环境类
      class Validator {
        constructor() {
          this.rules = [];
        }
        add = (value, rule, err) => { // --------------- add()方法的返回值是 err | undefined
          const ruleArr = rule.split(":"); // ---------- [notEmpty] 或 [minLength, 6]
          this.rules.push(() => { // ------------------- shift unshift push 改变原数组
            const strategyKey = ruleArr.shift(); // ---- 'notEmpty' 或 'minLength'
            ruleArr.unshift(value) // ------------------ 头部添加
            ruleArr.push(err) // ----------------------- 尾部添加
            // 1. ruleArr = [value, err] 或 [value, minLength, err]
            // 2. 这里 apply 用得很秒,因为 ruleArr 是一个数组
            return strategy[strategyKey].apply(null, ruleArr)
          });
        };
        start = () => this.rules.map(rule => rule()) // start()返回一个err数组
      }

      form.onsubmit = (e) => {
        e.preventDefault();
        const validator = new Validator();
        validator.add(form.username.value, "notEmpty", "用户名不能为空"); //  add()返回 err | undefined
        validator.add(form.password.value, "minLength:6", "密码不能少于6位"); // add()返回 err | undefined
        const errArr = validator.start() // start()返回err数组,成员可能是undefined表示通过验证
        const filterErrArr = errArr.filter(err => err) // 过滤掉通过验证的成员
        filterErrArr.forEach(err => console.log(err)) // 打印错误
        if (filterErrArr.length>0) return false // 如果过滤掉通过验证成员后,数组长度大于0,则表示有未通过验证的错误,返回false
      };
    </script>
  </body>
</html>
image.png

资料

命令模式(精简) https://juejin.cn/post/6844903889217519624
享元模式 http://techblog.sishuxuefu.com/atricle.html?5bcf34ef808ca40072a6fec2
享元模式 https://juejin.cn/post/6844903743901663246
组合模式 https://juejin.cn/post/6844903890375147527
代理模式[我的掘金] https://juejin.cn/post/6918744081460002824#heading-6

上一篇 下一篇

猜你喜欢

热点阅读