设计模式-责任链模式

125 阅读3分钟

前言

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着一个链条传递,直到有一个对象处理它为止。这个模式的关键是将请求的发送者与接收者解耦,使得多个对象都有机会处理请求,而发送者不需要知道哪个对象最终处理了请求。

在软件开发中,经常会遇到这样的问题:一个请求可能需要多个对象处理,但我们不希望请求者与处理者之间存在强耦合关系。传统的处理方式是通过在发送者中引入条件语句(如if-else)来判断由哪个对象处理请求,但这种方式的缺点在于:

  • 代码的可维护性和可扩展性差。
  • 发送者需要了解所有可能的处理者,这违背了“开闭原则”。

为了解决上述问题,责任链模式应运而生。

责任链模式的核心思想是:将多个处理对象连成一条链,并将请求沿着这条链传递,直到某个对象处理该请求。每个处理对象都包含一个指向下一个处理对象的引用,这样请求在未被处理时,可以沿着链传递到下一个对象。

image.png

场景

现在有个业务场景,针对一个商品需要打不同的标签,例如:商品上架不超过 30天 需要打上 New标签、折扣商品需带上 折扣标签等。

普通版

function createTag(tag) {
  if (tag.tagType === 1) {
    console.log("特性标签");
  } else if (tag.tagType === 2) {
    console.log("New 标签");
  } else if (tag.tagType === 3) {
    console.log("折扣标签");
  } else if (tag.tagType === 4) {
    console.log("属性标签");
  }
}
createTag({ tagType: 1 });

聪明的小伙伴,发现上述代码已经违反了设计模式的 单一职责原则开放封闭原则。下面我们用责任链

责任链0.1版

function porpertypeLabel(tag) {
  if (tag.tagType !== 1) return "next";
  console.log("特性标签");
}

function newLabel(tag) {
  if (tag.tagType !== 2) return "next";
  console.log("New 标签");
}

function discountLabel(tag) {
  if (tag.tagType !== 3) return "next";
  console.log("折扣标签");
}

function attributeLabel(tag) {
  if (tag.tagType !== 4) return "next";
  console.log("属性标签");
}

function chain(tag) {
  let arr = [porpertypeLabel, newLabel, discountLabel, attributeLabel],
    length = arr.length;
  while (length--) {
    if (arr[length](tag) !== "next") {
      break;
    }
  }
}

chain({ tagType: 1 });

责任链0.2版

基于 0.1 版本,如果后期又新增了 XLabelYLabel 等,我们则需要修改 chain 函数, 违背了 开放封闭原则。下面我们将 Chain 抽离成独立的类,提供 add 方法实现动态添加。

function porpertypeLabel(tag) {
  if (tag.tagType !== 1) return "next";
  console.log("特性标签");
}

function newLabel(tag) {
  if (tag.tagType !== 2) return "next";
  console.log("New 标签");
}

function discountLabel(tag) {
  if (tag.tagType !== 3) return "next";
  console.log("折扣标签");
}

function attributeLabel(tag) {
  if (tag.tagType !== 4) return "next";
  console.log("属性标签");
}

function Chain() {
  this.queue = [];
}

Chain.prototype.add = function (handler) {
  this.queue.push(handler);
};

Chain.prototype.end = function (tag) {
  let length = this.queue.length;
  while (length--) {
    if (this.queue[length](tag) !== "next") {
      break;
    }
  }
};

const chain = new Chain();
chain.add(porpertypeLabel);
chain.add(newLabel);
chain.add(discountLabel);
chain.add(attributeLabel);
chain.end({ tagType: 1 });

责任链最终版

0.2 版本的 add 需要逐个添加不同的标签 handler 代码比较冗余,我们通过 链式调用 进行优化。

Chain.prototype.add = function (handler) {
  this.queue.push(handler);
  return this;
};

我们将 Chain 上的 add 方法,返回当前实例对象。使用如下:

const chain = new Chain();
chain.add(porpertypeLabel).add(newLabel).add(discountLabel).add(attributeLabel).end({ tagType: 3 });

总结

在后面看到 if …… else 的语句时,我们在心里需要想一下是不是可以使用 责任链设计模式 来实现。