浅浅了解设计模式之三 | 行为型

100 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

image.png 行为型:多个对象之间互相配合,完成单个对象无法独立完成的任务

迭代器模式

迭代器很容易就想到遍历,可以迭代一个对象的内部元素而不暴露底层,for循环、map、foreach都属于迭代器模式

  function each(arr, callback) {
    for(let i in arr) {
      callback(arr[i]);
    }
  }
  each([1,2,3], (res) => {
    console.log(res);
  })

解释器模式

这种模式实现了一个表达式接口,该接口解释一个特定的上下文

大概是:小小的实现了接口的功能

  class Parent {
    constructor(name) {
      this.name = name
    }
    getString() {
      return this.name
    }
  }
  class Son extends Parent {
    constructor(name) {
      super(name);
    }
    getString() {
      return super.getString() + " say hi!";
    }
  }
  let son = new Son('zhangsan');
  console.log(son.getString()); // zhangsan say hi!

基础类实现基础功能,扩展类根据需要的业务对函数进行补充

观察者模式

image.png

  // 餐饮老板 被观察者
  class CateringOwner {
    constructor() {
      this.observers = [];
    }
    add(o) {
      this.observers.push(o);
    }
    remove(o) {
      this.observers.filter(item => item === o);
    }
    notify() {
      console.log('老板炒糊了!!');
      this.observers.forEach(item => {
        item.go();
      })
    }
  }
  // 消费者 观察者
  class Customer {
    go() {
      console.log('不吃啦,快跑!!');
    }
  }
  let owner = new CateringOwner();
  let c1 = new Customer();
  let c2 = new Customer();
  let c3 = new Customer();
  owner.add(c1);
  owner.add(c2);
  owner.add(c3);
  owner.notify();

这里,餐饮老板作为被观察者,为1,消费者作为观察者,为n,一旦餐饮老板把料理做糊了,这一行为就会通知到所有消费者,让消费者产生go的行为

观察者模式跟发布订阅模式类似,发布订阅是观察者模式的升级版

发布/订阅模式使用一个主题/事件频道,这个频道处于想要获取通知的订阅者和发起事件的发布者之间。这个事件系统允许代码定义应用相关的事件,这个事件可以传递特殊的参数,参数中包含有订阅者所需要的值。这种想法是为了避免订阅者和发布者之间的依赖性。

发布订阅者模式

image.png

  // 餐饮老板 观察者
  class CateringOwner {
    constructor(name, takeaway) {
      this.name = name;
      this.takeaway = takeaway;
    }
    // 注册料理
    addDish(dishName) {
      this.takeaway.addDish(dishName);
    }
    // 推送料理
    publishDish(dishName) {
      this.takeaway.publishDish(dishName);
    }
  }
  // 消费者 被观察者
  class Customer {
    constructor(name, takeaway) {
      this.name = name;
      this.takeaway = takeaway;
    }
    // 订阅料理
    subscribeDish(dishName) {
      this.takeaway.subscribeDish(dishName, this);
    }
    // 取消订阅
    unSubscribrDish(dishName) {
      this.takeaway.unSubscribrDish(dishName, this);
    }
    update(dish) {
      console.log(this.name  + '观察到 '+ dish + ' 变化了');
    }
  }
  // 第三方
  class Takeaway {
    constructor() {
      this.dishes = {}
    }
    // 餐厅在外卖平台上注册料理
    addDish(dishName) {
      this.dishes[dishName] = [];
    }
    // 餐厅在外卖平台上注销料理
    removeDish(dishName) {
      delete this.dishes[dishName];
    }
    // 消费者订阅料理
    subscribeDish(dishName, sub) {
      if (this.dishes[dishName]) {
        this.dishes[dishName].push(sub);
      }
    }
    // 消费者取消订阅料理
    unSubscribrDish(dishName, sub) {
      if (this.dishes[dishName]) {
        this.dishes[dishName].forEach((item, index) => {
          if (item === sub) {
            this.dishes[dishName].splice(index, 1);
          }
        });
      }
    }
    // 外卖平台下通知某种料理所有下消费者
    publishDish(dishName) {
      this.dishes[dishName].forEach(item => {
        item.update(dishName);
      })
    }
  }
  let takeaway = new Takeaway();

  // 餐馆
  let xiaomai = new CateringOwner("小麦", takeaway);
  // 餐馆提供这些料理(愿意暴露的属性)
  xiaomai.addDish("汉堡包");
  xiaomai.addDish("炸鸡腿");
  xiaomai.addDish("薯条");
  xiaomai.addDish("蛋挞");
  
  // 顾客
  let customer1 = new Customer('zhangsan', takeaway);
  let customer2 = new Customer('lisi', takeaway);
  let customer3 = new Customer('wangwu', takeaway);
  // 顾客订阅这些料理(愿意观察的属性)
  customer1.subscribeDish("汉堡包");
  customer1.subscribeDish("炸鸡腿");
  customer2.subscribeDish("炸鸡腿");
  customer2.subscribeDish("薯条");
  customer2.subscribeDish("蛋挞");
  customer3.subscribeDish("汉堡包");

  // 一旦餐馆有任意料理发生变动希望 有订阅该料理的顾客 知晓,就调用发布函数
  xiaomai.publishDish("汉堡包");
  xiaomai.publishDish("炸鸡腿");
  xiaomai.publishDish("蛋挞");
  xiaomai.publishDish("薯条");
  
// zhangsan观察到 汉堡包 变化了
// wangwu观察到 汉堡包 变化了
// zhangsan观察到 炸鸡腿 变化了
// lisi观察到 炸鸡腿 变化了
// lisi观察到 蛋挞 变化了
// lisi观察到 薯条 变化了

虽然这个例子有点离谱,但是这个意思就是这个意思,更好的例子可以阅读文尾的参考文章。

相当于中间商捆绑了订阅者和发布者的行为,发布者可以自由选择发布自己想要让别人知道的属性,订阅者也可以自由选择想要知道别人那些属性变化了。中间商也可以有更多的发布者。

中介模式

收集不同对象的信息,对这些信息进行处理

用一个中介对象来封装一系列的对象交互

  let meditaor = (() => {
    let sizes = ['s', 'm', 'l'];
    let colors = ['red', 'yellow', 'blue'];
    return {
      validate: function(obj) {
        if (sizes.indexOf(obj.size) < 0){
          console.log('请选择尺码');
          return;
        }
        if (colors.indexOf(obj.color) < 0){
          console.log('请选择颜色');
          return;
        }
        if (!obj.count){
          console.log('数量要大于0');
          return;
        }
        console.log('加入购物车');
      }
    }
  })()
  let form = {
    validate: () => {
      let obj = {
        color: 'blue',
        size: 'l',
        count: 0
      }
      meditaor.validate(obj);
    }
  }
  form.validate(); // 数量要大于0

访问者模式

访问者模式是将对数据的操作和数据结构进行分离,将对数据中各元素的操作封装成独立的类,使其在不改变数据结构的前提下可以拓展对数据新的操作。

五种结构:抽象访问者,具体访问者,抽象元素,具体元素,对象结构

抽象访问者:定义具体访问者的接口,具体访问者实现函数,确定在访问具体元素时需要做什么

抽象元素:定义具体元素的接口,具体元素实现函数,实现包含接收操作

对象结构:包含具体元素的数组,提供访问者去访问的接口

这里可以直接看文尾的文章举得例子(这个实在是太麻烦了叭)

状态模式

当一个对象的内部状态发生改变时,会导致其行为的改变,这看起来像是改变了对象。

  let weatherFun  = (() => {
    let weather = null;
    return {
      change: (w) => {
        weather = w;
        weather.operate();
      }
    }
  })();
  function Sunny () {} 
  Sunny.prototype.operate = () => {
    console.log('放风筝啦!');
  }
  function Rainy () {} 
  Rainy.prototype.operate = () => {
    console.log('天要下雨,娘要嫁人');
  }
  function Snowy () {} 
  Snowy.prototype.operate = () => {
    console.log('鹅毛大雪');
  }
  weatherFun.change(new Sunny())
  weatherFun.change(new Rainy())
  weatherFun.change(new Snowy())
  
  // 放风筝啦!
  // 天要下雨,娘要嫁人
  // 鹅毛大雪

天气的状态变化会导致weatherFun产生不同的行为,看起来像是改变了对象

参考:

# js设计模式【详解】总目录——最常用的20种设计模式

# js设计模式之(解释器模式)

# JS设计模式之观察者模式

# JavaScript 23 种设计模式之 21 访问者模式