javaScript设计模式核心原理学习记录(四)

191 阅读5分钟

封装:
单一职责性
开放封闭性

策略模式

//定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
/**
 * 解耦方法
 * 对象映射
 * 优点:开放封闭改造,单一功能
 */
const priceProcessor = {
  pre(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 20;
    }
    return originPrice * 0.9;
  },
  onSale(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 30;
    }
    return originPrice * 0.8;
  },
  back(originPrice) {
    if (originPrice >= 200) {
      return originPrice - 50;
    }
    return originPrice;
  },
  fresh(originPrice) {
    return originPrice * 0.5;
  },
};

class PriceFn {
    askPrice(tag,originPrice){
        return priceProcessor[tag](originPrice)
    }
}

const priceTest = new PriceFn()
console.log(priceTest.askPrice('back',300))
priceProcessor.NewUser = (originPrice) =>{
    if (originPrice >= 500) {
        return originPrice - 100;
      }
      return originPrice * 0.7;
}
console.log(priceTest.askPrice('NewUser',600)) //新拓展

状态模式

状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。

class cofferMack {
  constructor() {
    this.state = "init";
    this.leftMake = "500ml";
    this.stateToProcessor = {
      that: this,
      american() {
        console.log("我只吐黑咖啡", this.that.leftMake);
      },
      latte() {
        this.american();
        console.log("加点奶");
      },
      vanillaLatte() {
        this.latte();
        console.log("再加香草糖浆");
      },
      mocha() {
        this.latte();
        console.log("再加巧克力");
      },
    };
  }

  changeStatus(state) {
    this.state = state;
    if (!this.stateToProcessor[this.state]) {
      return;
    }
    this.stateToProcessor[this.state]();
  }
}

const coffer = new cofferMack();
coffer.changeStatus("latte");

策略模式和状态模式不同之处

策略模式和状态模式确实是相似的,它们都封装行为、都通过委托来实现行为分发。
但策略模式中的行为函数是”潇洒“的行为函数,它们不依赖调用主体、互相平行、各自为政,井水不犯河水。而状态模式中的行为函数,首先是和状态主体之间存在着关联,由状态主体把它们串在一起;另一方面,正因为关联着同样的一个(或一类)主体,所以不同状态对应的行为函数可能并不会特别割裂。

观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。 —— Graphic Design Patterns

在观察者模式里,至少应该有两个关键角色是一定要出现的——发布者和订阅者。用面向对象的方式表达的话,那就是要有两个类

//观察者模式:一对多,目标对象的目标状态发生变化,通知所有观察者模式对象,使它们能自动更新。

//目标对象
class Publisher {
  constructor() {
    //初始化产品
    this.prdState = null;
    //初始化观察者数组
    this.observers = [];
    console.log("Publisher created");
  }
  //增加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }
  //移除观察者
  removeObserver() {
    this.observers.splice(i, 1);
  }
  //通知观察者
  notify() {
    this.observers.forEach((observer) => {
      observer.update(this);
    });
  }
}

//观察者
class Observer {
  constructor() {
    console.log("Observer created");
  }
  //通知更新
  update() {
    console.log("Observer.update invoked");
  }
}

//继承Pulisher --具体需求产品
class PrdPublisher extends Publisher {
  constructor() {
    super();
  }
  //改变产品,通知观察者
  setState(state) {
    this.prdState = state;
    this.notify();
  }
  //获取产品
  getState() {
    return this.prdState;
  }
}
//继承Observer --开发者职责
class DeveloperObserver extends Observer {
  constructor() {
    super();
    //需求文档一开始不存在
    this.prdState = {};
    console.log("DeveloperObserver created");
  }
  //更新产品
  update(publisher) {
    console.log("DeveloperObserver.update invoked");
    this.prdState = publisher.getState();
    this.work();
  }
  //开始工作
  work() {
    const prd = this.prdState;
    console.log("996 bigins。。。");
  }
}

const lilei = new DeveloperObserver();
const zhangsan = new DeveloperObserver();
const hanMei = new PrdPublisher();

const prd = {
  //具体产品
  work:'制作咖啡机'
};

hanMei.addObserver(lilei);
hanMei.addObserver(zhangsan);
hanMei.setState(prd);

订阅-发布模式

vue的中央处理事件bus:

把它理解为一个事件中心,我们所有事件的订阅/发布都不能由订阅方和发布方“私下沟通”,必须要委托这个事件中心帮我们实现。

const EventBus = new Vue()
export default EventBus
import bus from 'EventBus的文件路径'
Vue.prototype.bus = bus

实现vue的bus:

//先通过on存储事件,再emit触发事件
class EventEmitter {
  constructor() {
    // handlers是一个map,用于存储事件与回调之间的对应关系
    this.handlers = {}
  }

  // on方法用于安装事件监听器,它接受目标事件名和回调函数作为参数
  on(eventName, cb) {
    // 先检查一下目标事件名有没有对应的监听函数队列
    if (!this.handlers[eventName]) {
      // 如果没有,那么首先初始化一个监听函数队列
      this.handlers[eventName] = []
    }

    // 把回调函数推入目标事件的监听函数队列里去
    this.handlers[eventName].push(cb)
  }

  // emit方法用于触发目标事件,它接受事件名和监听函数入参作为参数
  emit(eventName, ...args) {
    // 检查目标事件是否有监听函数队列
    if (this.handlers[eventName]) {
      // 如果有,则逐个调用队列里的回调函数
      this.handlers[eventName].forEach((callback) => {
        callback(...args)
      })
    }
  }

  // 移除某个事件回调队列里的指定回调函数
  off(eventName, cb) {
    const callbacks = this.handlers[eventName]
    const index = callbacks.indexOf(cb)
    if (index !== -1) {
      callbacks.splice(index, 1)
    }
  }

  // 为事件注册单次监听器
  once(eventName, cb) {
    // 对回调函数进行包装,使其执行完毕自动被移除
    const wrapper = (...args) => {
      cb(...args)
      this.off(eventName, wrapper)
    }
    this.on(eventName, wrapper)
  }
}

const bus = new EventEmitter()
bus.on('test',(val)=>{
    console.log(val)
})
bus.emit('test','111')

观察者模式和订阅-发布模式的区别在于:
发布者不直接触及到订阅者、而是由统一的第三方来完成实际的通信的操作,叫做发布-订阅模式

迭代模式

在ES6中,针对Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象这些原生的数据结构都可以通过for...of...进行遍历

for...of...是借助数组的Symbol.iterator生成了它对应的迭代器对象,通过反复调用迭代器对象的next方法访问了数组成员。

const arr = [1, 2, 3]
// 通过调用iterator,拿到迭代器对象
const iterator = arr[Symbol.iterator]()

// 对迭代器对象执行next,就能逐个访问集合的成员
iterator.next()
iterator.next()
iterator.next()

等价于

// 通过调用iterator,拿到迭代器对象
const iterator = arr[Symbol.iterator]()

// 初始化一个迭代结果
let now = { done: false }

// 循环往外迭代成员
while(!now.done) {
    now = iterator.next()
    if(!now.done) {
        console.log(`现在遍历到了${now.value}`)
    }
}

//实现iterator
function iteratorGenerator(list){
    var idx = 0;
    var len = list.length;
    return {
        next:function(){
            var done = idx >= len;
            var value = !done ? list[idx++] : undefined
            return {
                done,
                value
            }
        }
    }
}
const iterator = iteratorGenerator([1,2,3])
iterator.next()
iterator.next()
iterator.next()