设计模式笔记

单例模式

定义

确保一个类仅有一个实例,并提供一个访问它的全局访问点。(多次实例化类也只创建一个实例对象)

实现

// 单例构造函数
function CreateSingleton (name) {
    this.name = name;
    this.getName();
};

// 获取实例的名字
CreateSingleton.prototype.getName = function() {
    console.log(this.name)
};
// 单例对象
var Singleton = (function(){
    var instance;
    return function (name) {
        if(!instance) {
            instance = new CreateSingleton(name);
        }
        return instance;
    }
})();

// 创建实例对象1
var a = new Singleton('a');
// 创建实例对象2
var b = new Singleton('b');

console.log(a===b);

场景

模态框

策略模式

定义

一些列算法,把他们封装起来,并且可以相互替换。(就是把看似毫无联系的代码提取封装、复用,使之更容易被理解和拓展)

实现

1.定义方法(表单校验的方法)

    const strategies = {
      // 非空
      noEmpty: function(value, errMsg){
        if(value === ''){
          return errMsg
        }
      },
      // 最小长度
      minLength: function(value, length, errMsg){
        if(!value || value.length < length){
          return errMsg
        }
      },
      // 最大长度
      maxLength: function(value, length, errMsg){
        if(value.length > length){
          return errMsg
        }
      }
    }

2.设置校验器

    // 创建验证器
    var Validator = function(strategies){
      this.strategies = strategies
      this.cache = [] // 存储校验规则
    }
    // 添加校验规则
    Validator.prototype.add = function(dom, rules){
      rules.forEach(item => {
        this.cache.push(() => {
          let value = dom.value
          let arr = item.rule.split(':')
          let name = arr.shift()
          let params = [value, ...arr, item.errMsg]
          // apply保证上下文一致
          return this.strategies[name].apply(dom, params)
        })
      })
    }
    // 校验结果
    Validator.prototype.validate = function(dom, rules, errMsg){
      // 遍历cache里面的校验函数
      for(let i = 0, validateFun; validateFun = this.cache[i++];){
        const message = validateFun()
        // 返回报错信息,终止验证并抛出异常
        if(message) return message
      }
    }

3.进行校验

 // 校验函数
    function validate(){
      // 实例验证器
      const validator = new Validator(strategies)
      // 添加验证规则
      validator.add(form.username, [
        {
          rule: 'noEmpty',
          errMsg: '用户名不能为空!'
        },
        {
          rule: 'minLength:3',
          errMsg: '用户名长度大于3!'
        }
      ])
      validator.add(form.password, [
        {
          rule: 'minLength:6',
          errMsg: '密码长度大于6!'
        },
        {
          rule: 'maxLength:10',
          errMsg: '密码最大长度为10!'
        }
      ])
      // 进行校验,并返回结果
      return validator.validate()
    }

使用场景

表单校验,多层if-else,switch判断

代理模式

定义

为其他对象提供一种代理以控制对这个对象的访问(拦截,中间层,避免直接修改原对象)

缓存代理实现

const getCacheProxy = (fn, cache = new Map()) => {
  return new Proxy(fn, {
    apply(target, context, args) {
      const argsString = args.join(' ');
      if (cache.has(argsString)) {
        // 如果有缓存,直接返回缓存数据        console.log(`输出${args}的缓存结果: ${cache.get(argsString)}`);
        
        return cache.get(argsString);
      }
      const result = fn(...args);
      cache.set(argsString, result);

      return result;
    }
  })
}

调用

const getFibProxy = getCacheProxy(getFib);
getFibProxy(40);

场景

设置私有属性,网络访问需要设置拦截时,缓存等。

发布订阅

定义

一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。(一对多)

实现思路

  • 在该对象上创建一个缓存列表(调度中心Event Channel)
  • on方法用来把函数添加到缓存列表中(订阅者注册事件到调度中心)
  • emit方法取到argument里第一个当作event,根据event值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
  • off方法可以根据event值取消订阅(取消订阅)
  • once方法只监听一次,调用完毕后删除缓存函数(订阅一次)

代码实现

class EventEmitter {
    constructor() {
        // 缓存列表
        this.listener = {}
    }

    // 订阅
    on(eventName, fn) {
        // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
        // 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
        if(!this.listener[eventName]){
            this.listener[eventName] = [];
        }
        this.listener[eventName].push(fn);
    }

    // 取消订阅
    off(eventName, fn) {
        let callbacks = this.listener[eventName];
        // 缓存列表中没有对应的fn,返回false
        if(!callbacks){
            return false;
        }
        if(!fn){
            // 如果未传入fn,则将缓存列表中对应的fn都清空
            callbacks && (callbacks.length = 0);
        } else {
            let cb;
            // 遍历所对应的fn,判断和那个fn相同,相同则删除
            for (let i = 0, cbLen = callbacks.length; i < cbLen; i++) {
                cb = callbacks[i];
                if (cb == fn || cb.fn == fn) {
                    callbacks.splice(i, 1);
                    break
                }
            }
        }
    }

    // 监听一次
    once(eventName, fn) {
        // 先绑定,运行时删除对应的值
        let on = () => {
            this.off(eventName, on);
            fn.apply(this, arguments);
        }

        on.fn = fn;
        this.on(eventName, on);

    }

    // 发布
    emit(eventName, data) {
        const callbacks = this.listener[eventName];
        if(callbacks) {
            callbacks.forEach((c) => {
                c(data);
            })
        }
    }
}

let a = new EventEmitter();
function aa(x) {
    console.log(x);
}
a.on("kak", aa)
a.on("kak", (data) => {
    console.log("1", data);
})


a.emit('kak', 'hahahah');
a.off('kak',aa);
a.emit('kak', 'hahahah');

场景

权限状态,一对多的场景。

中介者模式

定义

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。(是对象与对象之间)

实现

  // 创建中介者
  var Mediator = function() {
    var _msg = {}
    return {
      register: function(type, action) {
        if (!_msg[type]) _msg[type] = []
        _msg[type].push(action)
      },
      send: function(type) {
        if(_msg[type]) {
          for (var i = 0; i < _msg[type].length; i++) {
            _msg[type][i] && _msg[type][i]()
          }
        }
      }
    }
  }()

  Mediator.register('demo', function () {
    console.log('first')
  })
  Mediator.register('demo', function () {
    console.log('second')
  })
  Mediator.send('demo')

场景

商品选择不同颜色,尺码(等不同维度)时显示库存

组合模式

定义

又叫 “部分整体” 模式,将对象组合成树形结构,以表示 “部分-整体” 的层次结构。通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性。(文件访问目录,tree结构)。对外暴露统一接口,访问顶层对象时会自上而下遍历子对象。

实现

// 树对象 - 文件目录
class CFolder {
    constructor(name) {
        this.name = name;
        this.files = [];
    }

    add(file) {
        this.files.push(file);
    }

    scan() {
        for (let file of this.files) {
            file.scan();
        }
    }
}

// 叶对象 - 文件
class CFile {
    constructor(name) {
        this.name = name;
    }

    add(file) {
        throw new Error('文件下面不能再添加文件');
    }

    scan() {
        console.log(`开始扫描文件:${this.name}`);
    }
}

let mediaFolder = new CFolder('娱乐');
let movieFolder = new CFolder('电影');
let musicFolder = new CFolder('音乐');

let file1 = new CFile('钢铁侠.mp4');
let file2 = new CFile('再谈记忆.mp3');
movieFolder.add(file1);
musicFolder.add(file2);
mediaFolder.add(movieFolder);
mediaFolder.add(musicFolder);
mediaFolder.scan();


/* 输出:
开始扫描文件:钢铁侠.mp4
开始扫描文件:再谈记忆.mp3
*/

场景

树形结构查找(地区级联显示

责任链模式

定义

使多个对象都有机会处理请求,从而避免了请求的发送者与多个接收者直接的耦合关系,将这些接收者连接成一条链,顺着这条链传递该请求,直到找到能处理该请求的对象。

实现

function order500 (orderType, pay, stock) {
  if (orderType === 1 && pay === true) {
    console.log('500元定金预购,得到100元优惠券')
  } else {
    return 'nextSuccessor'
  }
}

function order200 (orderType, pay, stock) {
  if (orderType === 2 && pay === true) {
    console.log('200元定金预购,得到50元优惠券')
  } else {
    return 'nextSuccessor'
  }
}

function orderNormal (orderType, pay, stock) {
  if (stock > 0) {
    console.log('普通用户购买,无优惠券')
  } else {
    console.log('手机库存不足')
  }
}

使用AOP创建职责链

Function.prototype.after = function (fn) {
  const self = this
  return function () {
    const res = self.apply(this, arguments)
    if (res === 'nextSuccessor') {
      return fn.apply(this, arguments)
    }
    return res
  }
}

const order = order500.after(order200).after(orderNormal)
order(1, true, 500)   // 500元定金预购,得到100元优惠券
order(2, true, 500)   // 200元定金预购,得到50元优惠券
order(3, true, 500)   // 普通用户购买,无优惠券
order(1, false, 0)    // 手机库存不足

使用场景

商品促销不同顾客优惠力度不同的购买

你可能感兴趣的:(前端)