yaogengzhu / Learning-notes

基础回顾、笔记
1 stars 0 forks source link

设计模式初探 (2021-12-22) #28

Open yaogengzhu opened 2 years ago

yaogengzhu commented 2 years ago

策略模式

策略模式:定义一系列的算法,把她们一个个封装起来 模式目的:就是将算法的使用 和 算法的实现分离开来

/**
 *  策略类 + 环境类  (两个部分组成)
 *
 *  策略类: 封装了具体的算法,并负责具体的计算过程
 *  环境类: 接收客户的请求,随后把请求委托给某一个策略类
 *
 *  环境类中--- 维持着某个策略对象的引用
 */

// 策略类
var performaceS = function () {}
performaceS.prototype.calculate = function (salary) {
    return salary * 4
}

// 定义奖金类   ---环境类
var Bonus = function () {
    this.salary = null // 原始工资
    this.strategy = null // 绩效等级对应的策略对象
}

Bonus.prototype.setSalary = function (salary) {
    this.salary = salary // 设置员工的原始工资
}

Bonus.prototype.setStrategy = function (strategy) {
    this.strategy = strategy // 设置员工绩效等级对应的策略对象
}

Bonus.prototype.getBonus = function () {
    // 取得奖金数额
    if (!this.strategy) {
        throw Error('未设置strateg 属性')
    }

    return this.strategy.calculate(this.salary) // 把计算的奖金委托给对应的策略对象
}

var bonus = new Bonus()
bonus.setSalary(1000)
bonus.setStrategy(new performaceS())
console.log(bonus.getBonus())
yaogengzhu commented 2 years ago

单例模式

定义:保证一个类只有一个实例,并提供一个访问它的全局访问点 原理: 用一个变量那个来标志当前是否为莫个类创建过对象。

/**
 * 对现有的类,实现单例模式
 * 代理类实现单例模式
 */

class SingleMode {
    constructor(name) {
        this.name = name
    }

    getName() {
        return this.name
    }
}

const ProxySingleMode = (function(){
    let instance
    return function () {
        if (!instance) {
            instance = new SingleMode()
        }
        return instance
    }
})()

// 类的实现
// class ProxySingleMode {
//     static instance
//     constructor() {
//         if (!ProxySingleMode.instance) {
//             ProxySingleMode.instance = new SingleMode()
//         }
//         return ProxySingleMode.instance;
//     }
// }
const single = new ProxySingleMode()
const single1 = new ProxySingleMode()
console.log(single === single1) // true

惰性单列 定义: 指的是在需要的时候才创建的对象实例子

const getSingle = function (fn) {
    let instance
    return function () {
        return instance || instance === fn.apply(this, arguments)
    }
}
yaogengzhu commented 2 years ago

代理模式

定义:代理是为一个对象提供一个替代品或者占位符,以便控制对它的访问

优点:对象本身不好处理的事情,交给代理处理

const Flower = function () {}

const xiaoming = {
    sendFlower: function (target) {
        const flower = new Flower()
        target.receiveFlower(flower)
    },
}

const A = {
    receiveFlower: function (flower) {
        console.log('收到花了')
    },
}

const B = {
    receiveFlower: function (flower) {
        A.receiveFlower(flower)
    },
}

xiaoming.sendFlower(B)

衍生 保护代理虚拟代理

yaogengzhu commented 2 years ago

迭代器模式

定义: 是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部状态

迭代器分为两种: 内部迭代外部迭代

内部迭代

const each = function (arr, callback) {
    for (let i = 0; i < arr.length; i++) {
        callback.call(arr[i], i, arr[i])
    }
}

each([1, 2, 4], function (i, v) {
    console.log(i, v)
})

外部迭代

外部迭代虽然调用方式相对复杂,但适用面更广,也能满足更多的需求。 内外迭代模式在生产中,没有优劣之分

const Iterator = function (obj) {
    var current = 0

    var next = function () {
        current += 1
    }

    var isDone = function () {
        return current >= obj.length
    }

    var getCurrentItem = function () {
        return obj[current]
    }

    return {
        next,
        isDone,
        getCurrentItem,
        length: obj.length,
    }
}

// 改写外部迭代模式
const compareOutter = function (iterator1, iterator2) {
    if (iterator1.length !== iterator2.length) {
        return console.log('iterator1 和 iterator2 不相等')
    }

    while (!iterator1.isDone() && !iterator2.isDone()) {
        if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) {
            return console.log('iterator1 和 iterator2 不相等')
        }

        iterator1.next()
        iterator2.next()
    }

    console.log('相等')
}
let iterator1 = Iterator([1, 2, 3])
let iterator2 = Iterator([1, 2, 3, 4])

compareOutter(iterator1, iterator2)

倒叙迭代

const fallEach = function (arr, callback) {
  for (let i = arr.length - 1; i >= 0; i--) {
      callback(i, arr[i])
  }
}

fallEach([1, 2, 3], function (i, k) {
  console.log(i, k)
})

中止迭代

const breakEach = function (arr, callback) {
    for (let i = 0; i < arr.length; i++) {
        if (callback(i, arr[i]) === false) {
            break
        }
    }
}

breakEach([1, 2, 3, 4], function (i, n) {
    if (n > 3) return false
    console.log(n)
})

迭代器的应用举例

根据浏览器环境获取不同的上传方式

const getActiveUploadObj = function() {
    try {
        retrun new ActiveXObject('IE上传')
    } catch(e) {
        return false
    }
}

const getFlashUploadObj = function() {
    if (supportFlash()) { // 省略该方法
        var str  = `<object type='xx'></object>` // 省写
        return $(str).appendTo($('body'))
    }
    return false
}

const getFormUploadObj = function() {
    var str = `<input type='file' />`
    return $(str).appendTo($('body'))
}

const iteratorUploadObj = function() {
    for (let i = 0; fn = arguments[i++]) {
        const uploadObj = fn()
        if (uploadObj !== false) {
            return uploadObj
        }
    }
}

const uploadObj = iteratorUploadObj(getActiveUploadObj, getFlashUploadObj, getFormUploadObj)
yaogengzhu commented 2 years ago

命令模式

用途: 用于执行一个特定事情的指令

const setCommad = function (button, command) {
    button.click = function () {
        command.execute()
    }
}

const MenuBar = {
    refresh: function () {
        console.log('刷新')
    },
}

const SubMenu = {
    add: function () {
        console.log('新增子菜单')
    },

    del: function () {
        console.log('删除子菜单')
    },
}

// 封装命令类
const RefreshMenuCommad = function (recevicer) {
    this.recevicer = recevicer
}

RefreshMenuCommad.prototype.execute = function () {
    this.recevicer.refresh()
}

const AddSubMenuCommand = function (recevicer) {
    this.recevicer = recevicer
}

AddSubMenuCommand.prototype.execute = function () {
    this.recevicer.add()
}

const DelSubMenuCommand = function (recevicer) {
    this.recevicer = recevicer
}

DelSubMenuCommand.prototype.execute = function () {
    // console.log('删除子菜单')
    this.recevicer.del()
}

// 将命令接受者传入到command对象中,并且将command对象安装到button 上

const refreshMenuBarCommand = new RefreshMenuCommad(MenuBar)
const addSubMenuCommand = new AddSubMenuCommand(SubMenu)
const delSubMenuCommand = new DelSubMenuCommand(SubMenu)

setCommad(button1, refreshMenuBarCommand)
setCommad(button2, addSubMenuCommand)
setCommad(button3, delSubMenuCommand)

命令模式应用

// 游戏
const Ryu = {
    attack: function () {
        console.log('攻击')
    },

    defense: function () {
        console.log('防御')
    },

    jump: function () {
        console.log('跳跃')
    },

    crouch: function () {
        console.log('蹲下')
    },
}

// 创建命令
const makeCommand = function (recevicer, state) {
    return function () {
        recevicer[state]()
    }
}

const commands = {
    119: 'jump',
    115: 'crouch',
    97: ' defense',
    100: 'attach',
}

const commandStack = [] // 保存命令栈

document.onKeypress = function (ev) {
    const keyCode = ev.keyCode,
        command = makeCommand(Ryu, commands[keyCode])

    if (command) {
        command()
        commandStack.push(command) // 将刚执行过的命令推到栈里
    }
}

// 点击播放录像
document.getElementById('replay').onclick = function () {
    let command
    while (command === commandStack.shift()) {
        // 从堆栈里一次取出命令并执行
        command()
    }
}

宏命令

宏命令是一组命令的集合,通过执行宏命令,可以支持一次执行一批命令

// 依次创建好各种command

const closeDoorCommand = {
    execute: function () {
        console.log('关门')
    },
}

const openPcCommand = {
    execute: function () {
        console.log('开电脑')
    },
}

const openQQCommand = {
    execute: function () {
        console.log('登QQ')
    },
}

const MaroCommand = function () {
    return {
        commandList: [],
        add: function (command) {
            this.commandList.push(command)
        },
        execute: function () {
            this.commandList.forEach((fn) => fn.execute())
        },
    }
}

const macroCommand = new MaroCommand()

macroCommand.add(closeDoorCommand)
macroCommand.add(openPcCommand)
macroCommand.add(openQQCommand)
macroCommand.execute()
yaogengzhu commented 2 years ago

职责链模式

定义:使用多个对象处理请求,从而避免请求发送者和接收者之间的耦合关联,将这些对象连成一条链子,并沿着链子传递请求,直到有一个对象处理它为止

/**
 * 普通函数写法
 * @param {*} orderType  订单类型
 * @param {*} pay   是否已支付
 * @param {*} stock 库存数量
 */
const order = function (orderType, pay, stock) {
    if (orderType === 1) {
        if (pay === true) {
            console.log('已付款500元定金,可以得到100优惠券')
        } else {
            if (stock > 0) {
                console.log('普通购买,无优惠券')
            } else {
                console.log('库存不足')
            }
        }
    } else if (orderType === 2) {
        if (pay === true) {
            console.log('已付款200元定金,可以得到50优惠券')
        } else {
            if (stock > 0) {
                console.log('普通购买,无优惠券')
            } else {
                console.log('库存不足')
            }
        }
    } else if (orderType === 3) {
        if (stock > 0) {
            console.log('普通购买,无优惠券')
        } else {
            console.log('库存不足')
        }
    }
}

// order(1, true, 500)

/**
 * 职责链重构
 */
const order500 = function (orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log('已付款500元定金,可以得到100优惠券')
    } else {
        order200(orderType, pay, stock)
    }
}

const order200 = function (orderType, pay, stock) {
    if (orderType === 2 && pay === true) {
        console.log('已付款200元定金,可以得到50优惠券')
    } else {
        orderNormal(orderType, pay, stock)
    }
}

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

// order500(3, true, 500)

/**
 * 升级写法
 */

const Chain = function (fn) {
    this.fn = fn
    this.successor = null // 表示职责链的下一个节点
}

/**
 * 指定在链中的下一个节点
 * @param {*} successor
 * @returns
 */
Chain.prototype.setNextSuccessor = function (successor) {
    return (this.successor = successor)
}

/**
 * 传递请求给某一个节点
 * @returns
 */
Chain.prototype.passRequest = function () {
    let ret = this.fn.apply(this, arguments)
    if (ret === 'nextSuccessor') {
        return (
            this.successor &&
            this.successor.passRequest.apply(this.successor, arguments)
        )
    }

    return ret
}

const upgradeOrder500 = function (orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log('已付款500元定金,可以得到100优惠券')
    } else {
        return 'nextSuccessor'
    }
}

const upgradeOrder200 = function (orderType, pay, stock) {
    if (orderType === 2 && pay === true) {
        console.log('已付款200元定金,可以得到50优惠券')
    } else {
        return 'nextSuccessor'
    }
}

const upgradeOrderNormal = function (orderType, pay, stock) {
    if (stock > 0) {
        console.log('普通购买,无优惠券')
    } else {
        console.log('库存不足')
    }
}

// 将3个订单的函数分别包装成职责链的节点
const chainOrder500 = new Chain(upgradeOrder500)
const chainOrder200 = new Chain(upgradeOrder200)
const chainNormal = new Chain(upgradeOrderNormal)

// 指定节点在职责链的顺序
chainOrder500.setNextSuccessor(chainOrder200)
chainOrder200.setNextSuccessor(chainNormal)

// 将请求传递给第一个节点
chainOrder500.passRequest(2, true, 500)
yaogengzhu commented 2 years ago

补充链表结合职责链模式实现

地址

yaogengzhu commented 2 years ago

适配器模式

用来解决两个软件实体间接口不兼容的问题。

const google = {
  show: function () {
    console.log('渲染goggle地图')
  },
}

const baidu = {
  show: function () {
    console.log('渲染baidu地图')
  },
}

const renderMap = function (map) {
  if (map.show instanceof Function) {
    map.show()
  }
}

renderMap(google)
renderMap(baidu)

/**
 * 异常,不是show
 */
const newBaidu = {
  display: function () {
    console.log('渲染新百度地图')
  },
}

const newBaiduAdapter = {
  // 重新一个写一个方法来适配现有的结构
  show: function () {
    return newBaidu.display()
  },
}

renderMap(newBaiduAdapter)
yaogengzhu commented 1 year ago

什么是多态

同一操作作用不同的对象上, 可以产生不同的解释和不同的执行结果

function renderMap(map) {
  if (map.show instanceof Function) {
     map.show()
  }
}

class GoogleMap {
  show() {
     console.log('渲染Google地图')
  }
}

class SogoMap {
  show() {
    console.log('渲染Sogo地图')
  }
}

console.log(renderMap(new GoogleMap()))
console.log(renderMap(new SogoMap()))
yaogengzhu commented 1 year ago

柯里化前置知识

实现一个计算cost(10)、cost(20)、cost(30)、最后通过cost() 得到最终的结果

思路:参数为空时,返回最终结果 可将参数存起来,判断参数为空时 在统计返回结果,需要利用到闭包的知识点;

const cost = (function () {
  const args = []; // 存放参数
  return function () {
    // 判断参数是否为空, 参数为空,则表示需要直接返回最终结果
    if (arguments.length === 0) {
      let momeny = 0;
      for (let i = 0; args.length === 0; i++) {
        momeny += args[i];
      }
      return momeny;
    } else {
      // 如果参数存在,则将参数全部存到args 中
      [].push.apply(args, arguments);
    }
  };
})();

通用方案

const cost = (function () {
  let momeny = 0;
  return function () {
    for (let i = 0; i < arguments.length; i++) {
      momeny += arguments[i];
    }
    return momeny;
  };
})();

function currying(fn) {
  const args = [];
  return function () {
    if (arguments.length == 0) {
      return fn.apply(this, args);
    } else {
      [].push.apply(args, arguments);
      return arguments.callee;
    }
  };
}
const fn = currying(cost);
fn(1)(2);
console.log(fn());