规格模式 Specification Pattern

Posted GoldenaArcher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了规格模式 Specification Pattern相关的知识,希望对你有一定的参考价值。

Specification Pattern

不了解 SOLID 的可以扫一眼这个:SOLID,面向对象设计五大基本原则

今天扫 SOLID JS 的时候,授课者对 OCP 的一个实现方式利用了 Specification Pattern,所以这里也进行一下补充学习。

不过讲道理来说,因为 javascript 是一个弱类型的语言,所以很难强制让子类去实现其他语言中 interface/abstract 的函数,这也是一个局限了。

使用案例为,假设需要实现一个过滤器(filter),最初的实现方式如下:

const COLOR = Object.freeze(
  RED: 'red',
  GREEN: 'green',
  BLUE: 'blue',
);

const SIZE = Object.freeze(
  SMALL: 'small',
  MEDIUM: 'medium',
  LARGE: 'large',
);

class Product 
  constructor(name, color, size) 
    this.name = name;
    this.color = color;
    this.size = size;
  


class ProductFilter 
  filterByColor(products, color) 
    return products.filter((p) => p.color === color);
  

  filterBySize(products, size) 
    return products.filter((p) => p.size === size);
  


const apple = new Product('Apple', COLOR.GREEN, SIZE.SMALL);
const tree = new Product('Tree', COLOR.GREEN, SIZE.LARGE);
const house = new Product('House', COLOR.BLUE, SIZE.LARGE);

const products = [apple, tree, house];

const pf = new ProductFilter();

console.log('Green products:');
for (const p of pf.filterByColor(products, COLOR.GREEN)) 
  console.log(` * $p.name is green.`);

可以看到之后如果要写其他的 filter,也是可以继续在 ProductFilter 实现的,不过这里有几个问题:

  1. 继续实现其他的 filter 就要修改现有的代码
  2. 每一次新的需求来了,都需要额外实现新需求,这会引起 state space explosion 的问题,毕竟 3 个变量的 AND 操作,就需要 7 个方法去进行实现(A, B, C, A+B, B+C, A+C, A+B+C)。

一个解决这个问题的方法是使用 specification pattern,修改代码如下:

// should be done through inheritance, but js doesn't reenforce inheritance
// class Specification 

class ColorSpecification 
  constructor(color) 
    this.color = color;
  

  isSatisfied(item) 
    return item.color === this.color;
  


class SizeSpecification 
  constructor(size) 
    this.size = size;
  

  isSatisfied(item) 
    return item.size === this.size;
  


class FilterSpecs 
  filter(items, spec) 
    return items.filter((x) => spec.isSatisfied(x));
  


const fs = new FilterSpecs();

console.log('Green products with specs:');
for (const p of fs.filter(products, new ColorSpecification(COLOR.GREEN))) 
  console.log(` * $p.name is green.`);

对于 AND 操作的实现补充如下:

class AndSpecifications 
  constructor(...specs) 
    this.specs = specs;
  

  isSatisfied(item) 
    return this.specs.every((x) => x.isSatisfied(item));
  


console.log('Large and green products:');
const specs = new AndSpecifications(
  new ColorSpecification(COLOR.GREEN),
  new SizeSpecification(SIZE.LARGE)
);

for (const p of fs.filter(products, specs)) 
  console.log(` * $p.name is green and large.`);

使用 specification pattern 的优势就在于极大地减少了函数的实现,原本对于 AND 的操作,3 个条件就需要实现 7 个方法,而后如果有更多的需求,就需要些更多的方法去满足这些需求,对于 n n n 个需求来说,就会有 n + n − 1 , + . . . + 1 n + n-1, + ... + 1 n+n1,+...+1,也就是 n 2 n^2 n2 个方法。。使用了 specification pattern 现在只需要实现 3+1 个,并且如果之后有其他的需求了,在实现对应的基类后,只需要调用 AndSpecifications 并将对应的 specs 传进去就行,换言之,只需要实现 n + 1 n+1 n+1 个方法。

同样的,如果对 OR,XOR 操作也有需求,那么 specification pattern 就能省下大量的代码和开发时间。

Reference

以上是关于规格模式 Specification Pattern的主要内容,如果未能解决你的问题,请参考以下文章

规格模式 Specification Pattern

specification一般有哪几种翻译

英语Device specification怎么翻译?

Specification Testing 是啥意思

ADT and OOP 设计规范(Designing Specification)

BUAA OO 第三单元总结