前端修炼之路-设计模式

什么是设计模式?

在软件开发程中通用的解决方案。
目前有23中设计模式
设计原则:单一职责原则(SRP)、开放封闭原则(OCP)、最少知识原则(LKP)

1、代理模式

场景1:花店替人送花

//声明女同学
var Girl = function(name){
  this.name = name;
}

//声明男同学
var Boy = function(girl){
  //女同学
  this.girl = girl;
  //送花 行为
  this.sendGirl = function(gift){
    console.log('hi' + this.girl.name + ",送你一个小礼物" + gift);
  }
}

//代理对象 花店员工
var proxyObj = function(girl){
  this.girl = girl;
  this.sendGirl = function(gift){
    (new Boy(this.girl).sendGirl(gift)); //替人送花
  }
}

//调用
var girl = new Girl("小红");
var proxy = new proxyObj(girl);
proxy.sendGirl("999朵玫瑰");

场景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>图片懒加载</title>
</head>
<body>
  <script>
      //解决图片懒加载
      window.onload = function(){
        var myImage = (function(){
          var imgNode = document.createElement("img");
          document.body.appendChild(imgNode);
          var img = new Image(); //代理对象 先展示等待图片 接着负责拉取真实图片
          img.onload = function(){
            setTimeout(()=>{
              imgNode.src = this.src; 
            },2000)
          }
          return {
            setSrc: function(src){
              //先展示等待小图片
              imgNode.src = "./loading.gif";
              img.src = src;
            }
          }
        })()
        //把真实图片给到myImage对象
        myImage.setSrc("./mei.jpg"); //真实图片地址
      }
  </script>
</body>
</html>
  • 使用更高级代理模式写法:
<!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>
      //解决图片懒加载
      window.onload = function(){
        //使用代理模式重构懒加载
        //真实对象 闭包
        var myImage = (function(){
          var imgNode = document.createElement("img");
          document.body.appendChild(imgNode);
          return {
            setSrc: function(src){
              imgNode.src = src;
            }
          }
        })()
        //代理对象
        var proxyImage = (function(){
          var img = new Image();
          img.onload = function(){
            setTimeout(()=>{
              myImage.setSrc(this.src);
            },2000)
          }
          return {
            setSrc: function(src){
              myImage.setSrc("./loading.gif");
              img.src=src;
            }
          }
        })()
        //调用
        proxyImage.setSrc("./mei.jpg");
      }

  </script>
</body>
</html>

2、观察者模式

也叫发布订阅模式,例如:点餐,网上点外卖。
当我们去买鞋,发现看中的款式已经售罄了,售货员告诉你不久后这个款式会进货,到时候打电话通知你。于是你留了个电话,离开了商场,当下周某个时候进货了,售货员拿出小本本,给所有关注这个款式的人打电话。

  • 场景代码:双11购买商品
//观察者设计模式
var event = {
  list:[], //商品列表
  //订阅方法
  listen: function(key, fn){
    if(!this.list[key]){
      this.list[key] = [];
    }
    shopObj.list[key].push(fn)
  },
  //发布消息方法
  publish:function(){
    var key = arguments[0];
    var fns = this.list[key];
    for(var i=0, fn; fn = fns[i++];){
      //执行订阅的fn arguments js内置对象 存储所有实参
      fn.apply(this, arguments); 
    }
  }
}
//初始化
var initEvent = function(obj){
  for(var i in event){
    obj[i] = event[i];
  }
}

//发布者
var shopObj = {};
initEvent(shopObj);
//A添加订阅
shopObj.listen("huawei",function(brand, model){
  console.log("A订阅:"+ brand +" "+ model);
})
//B添加订阅
shopObj.listen("apple",function(brand, model){
  console.log("B订阅:"+brand+" "+ model);
})
//双11,商家发布消息
shopObj.publish("huawei", "P40");
shopObj.publish("apple", "iphone 14");

3、装饰器模式

给新房子装修
允许向一个对象添加新的功能,但不改变原有对象。

  • decorator.js
//使用装饰器给add方法添加日志 并且传入参数。
//运行此文件包需安装babel插件:babel-cli、babel-preset-env、babel-plugin-transform-decorators-legacy,将es6转为es5才能执行
class Math {
  @log(100)
  add(a, b){
    return a + b;
  }
}

function log(num){
  return function log(target, name, decorators){
    //三个参数说明:target 是Math对象、 name 是 "add" 名称 、 decorators 是add方法
    var oldValue = decorators.value; //add方法
    var _num = num;
    //重构add方法
    decorators.value = function(...arg){
      console.log(`调用 ${name} 方法, 参数为:`, arg);
      arg[0] += _num; //让第一个参数增加100
      return oldValue.apply(target, arg);
    }
    return decorators;

  }
}

var math = new Math();
var result = math.add(1,2);
console.log(result);

//运行结果:
//调用 add 方法, 参数为: [ 1, 2 ]
//103
  • .babelrc
{
  "presets": ["env"],
  "plugins": ["transform-decorators-legacy"]
}
  • package.json
{
  "scripts": {
    "build": "babel src/decorator.js -o build/math.js",
    "build-dir": "babel src -d build"
  },
  "dependencies": {
    "babel-cli": "^6.26.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.5",
    "babel-preset-env": "^1.7.0"
  }
}

4、职责链

场景: 击鼓传花、充值抽奖

  • 案例场景:跟领导请假
//领导基类
var Leader = function(){
  this.nextLeader = null;
}
Leader.prototype.setNext = function(next){
  this.nextLeader = next;
  return next; //返回可链式调用setNext
}

/* 小组领导 */
var GroupLeader = new Leader();
GroupLeader.handle = function(duration){
  if (duration <= 0.5) {
    console.log('小组领导经过一番心理斗争:批准了')
  } else{
    this.nextLeader && this.nextLeader.handle(duration)
  }
}

/* 部门领导 */
var DepartmentLeader = new Leader()
DepartmentLeader.handle = function(duration) {
  if (duration <= 1) {
    console.log('部门领导经过一番心理斗争:批准了')
  } else{
    this.nextLeader.handle(duration)
  }
}

/* 总经理 */
var GeneralLeader = new Leader();
GeneralLeader.handle = function (duration) {
  if (duration <= 2) {
    console.log('总经理经过一番心理斗争:批准了')
  } else{
    console.log('总经理:不准请这么长的假')
  }
}

GroupLeader
  .setNext(DepartmentLeader) // 设置小组领导的下一个职责节点为部门领导
  .setNext(GeneralLeader)    // 设置部门领导的下一个职责节点为总经理

GroupLeader.handle(0.5)   // 小组领导经过一番心理斗争:批准了
GroupLeader.handle(1)     // 部门领导经过一番心理斗争:批准了
GroupLeader.handle(2)     // 总经理经过一番心理斗争:批准了
GroupLeader.handle(3)     // 总经理:不准请这么长的假

5、单例模式

Vuex 源码中的store的实现就是单例模式。所以每个 Vue 实例只会拥有一个全局的 Store

  • 优点: 节约资源,保证访问的一致性。

  • 缺点: 扩展性不友好,因为单例模式一般自行实例化,没有接口。

6、工厂模式

根据不同的参数,返回不同类的实例。

posted @ 2022-12-04 13:28  smile_or  阅读(40)  评论(0编辑  收藏  举报