阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 规格模式(Specification)的业务规则组合

规格模式(Specification)的业务规则组合

作者:陈川 阅读数:27879人阅读 分类: JavaScript

规格模式(Specification)的业务规则组合

规格模式是一种行为设计模式,用于将业务规则封装成可重用的对象,通过逻辑运算组合这些规则。它特别适合处理复杂的业务条件判断场景,使代码更清晰、更易维护。

规格模式的核心概念

规格模式由三个核心部分组成:

  1. 规格接口(Specification Interface):定义验证方法
  2. 具体规格(Concrete Specification):实现具体业务规则
  3. 组合规格(Composite Specification):组合多个规格的逻辑运算
// 规格接口
class Specification {
  isSatisfiedBy(candidate) {
    throw new Error('Method not implemented');
  }

  and(otherSpec) {
    return new AndSpecification(this, otherSpec);
  }

  or(otherSpec) {
    return new OrSpecification(this, otherSpec);
  }

  not() {
    return new NotSpecification(this);
  }
}

具体规格实现

具体规格是实现特定业务规则的类。例如,在电商系统中可能有以下规格:

// 价格规格
class PriceSpecification extends Specification {
  constructor(minPrice, maxPrice) {
    super();
    this.minPrice = minPrice;
    this.maxPrice = maxPrice;
  }

  isSatisfiedBy(item) {
    return item.price >= this.minPrice && item.price <= this.maxPrice;
  }
}

// 类别规格
class CategorySpecification extends Specification {
  constructor(category) {
    super();
    this.category = category;
  }

  isSatisfiedBy(item) {
    return item.category === this.category;
  }
}

// 库存规格
class InStockSpecification extends Specification {
  isSatisfiedBy(item) {
    return item.stock > 0;
  }
}

组合规格实现

组合规格允许将多个规格通过逻辑运算符组合:

// AND组合规格
class AndSpecification extends Specification {
  constructor(left, right) {
    super();
    this.left = left;
    this.right = right;
  }

  isSatisfiedBy(candidate) {
    return this.left.isSatisfiedBy(candidate) && 
           this.right.isSatisfiedBy(candidate);
  }
}

// OR组合规格
class OrSpecification extends Specification {
  constructor(left, right) {
    super();
    this.left = left;
    this.right = right;
  }

  isSatisfiedBy(candidate) {
    return this.left.isSatisfiedBy(candidate) || 
           this.right.isSatisfiedBy(candidate);
  }
}

// NOT组合规格
class NotSpecification extends Specification {
  constructor(spec) {
    super();
    this.spec = spec;
  }

  isSatisfiedBy(candidate) {
    return !this.spec.isSatisfiedBy(candidate);
  }
}

实际应用示例

假设我们有一个电商产品筛选系统:

// 产品数据
const products = [
  { id: 1, name: 'Laptop', category: 'Electronics', price: 999, stock: 5 },
  { id: 2, name: 'Smartphone', category: 'Electronics', price: 699, stock: 0 },
  { id: 3, name: 'Desk', category: 'Furniture', price: 199, stock: 10 },
  { id: 4, name: 'Chair', category: 'Furniture', price: 149, stock: 15 },
  { id: 5, name: 'Monitor', category: 'Electronics', price: 299, stock: 8 }
];

// 创建规格
const electronicsSpec = new CategorySpecification('Electronics');
const priceSpec = new PriceSpecification(100, 500);
const inStockSpec = new InStockSpecification();

// 组合规格:电子类且价格在100-500之间且有库存
const combinedSpec = electronicsSpec
  .and(priceSpec)
  .and(inStockSpec);

// 筛选产品
const filteredProducts = products.filter(p => combinedSpec.isSatisfiedBy(p));
console.log(filteredProducts);
// 输出: [{ id: 5, name: 'Monitor', category: 'Electronics', price: 299, stock: 8 }]

规格模式的进阶用法

参数化规格

规格可以接受参数实现更灵活的条件判断:

class DynamicPriceSpecification extends Specification {
  constructor(getPriceRange) {
    super();
    this.getPriceRange = getPriceRange;
  }

  isSatisfiedBy(item) {
    const { min, max } = this.getPriceRange();
    return item.price >= min && item.price <= max;
  }
}

// 使用
const dynamicPriceSpec = new DynamicPriceSpecification(() => {
  // 可以从配置或API获取价格范围
  return { min: 100, max: 500 };
});

异步规格

对于需要异步验证的场景:

class AsyncStockSpecification extends Specification {
  constructor(stockService) {
    super();
    this.stockService = stockService;
  }

  async isSatisfiedBy(item) {
    const stockInfo = await this.stockService.getStock(item.id);
    return stockInfo.quantity > 0;
  }
}

// 使用
const asyncFilter = async (products, spec) => {
  const results = [];
  for (const product of products) {
    if (await spec.isSatisfiedBy(product)) {
      results.push(product);
    }
  }
  return results;
};

规格模式与领域驱动设计

规格模式在领域驱动设计(DDD)中扮演重要角色:

  1. 仓库模式(Repository):用于查询数据
  2. 规格模式:用于定义查询条件
  3. 领域模型:包含业务逻辑
class ProductRepository {
  constructor(products = []) {
    this.products = products;
  }

  findAll(specification) {
    return this.products.filter(p => specification.isSatisfiedBy(p));
  }

  findOne(specification) {
    return this.products.find(p => specification.isSatisfiedBy(p));
  }
}

// 使用
const repo = new ProductRepository(products);
const expensiveElectronicsSpec = new CategorySpecification('Electronics')
  .and(new PriceSpecification(500, Infinity));

const results = repo.findAll(expensiveElectronicsSpec);

规格模式的性能优化

对于大型数据集,可以考虑以下优化策略:

  1. 提前终止:在组合规格中实现短路评估
  2. 缓存结果:对不变的数据缓存验证结果
  3. 转换为查询:将规格转换为数据库查询条件
class OptimizedAndSpecification extends Specification {
  constructor(left, right) {
    super();
    this.left = left;
    this.right = right;
  }

  isSatisfiedBy(candidate) {
    // 短路评估:如果左边不满足就不评估右边
    return this.left.isSatisfiedBy(candidate) && 
           this.right.isSatisfiedBy(candidate);
  }

  // 转换为SQL WHERE条件
  toSQL() {
    return `${this.left.toSQL()} AND ${this.right.toSQL()}`;
  }
}

// 使用
const sqlCondition = combinedSpec.toSQL();
// 输出: "category = 'Electronics' AND price BETWEEN 100 AND 500 AND stock > 0"

规格模式的测试策略

测试规格模式时可以采用分层测试方法:

// 单元测试具体规格
describe('PriceSpecification', () => {
  it('should satisfy when price in range', () => {
    const spec = new PriceSpecification(100, 200);
    const item = { price: 150 };
    expect(spec.isSatisfiedBy(item)).toBe(true);
  });
});

// 测试组合规格
describe('AndSpecification', () => {
  it('should satisfy when both specs are satisfied', () => {
    const spec1 = new MockSpecification(true);
    const spec2 = new MockSpecification(true);
    const andSpec = new AndSpecification(spec1, spec2);
    expect(andSpec.isSatisfiedBy({})).toBe(true);
  });
});

// 集成测试
describe('ProductFilter', () => {
  it('should filter products correctly', () => {
    const spec = /* 组合规格 */;
    const filtered = productFilter.filter(products, spec);
    expect(filtered.length).toBe(2);
  });
});

规格模式在前端状态管理中的应用

规格模式可以用于复杂的状态筛选:

// Redux选择器中的规格模式
const createTodoSelector = (filterSpec) => (state) => {
  return state.todos.filter(todo => filterSpec.isSatisfiedBy(todo));
};

// 定义规格
const activeTodoSpec = new AndSpecification(
  new NotSpecification(new IsCompletedSpecification()),
  new NotSpecification(new IsArchivedSpecification())
);

// 使用选择器
const selectActiveTodos = createTodoSelector(activeTodoSpec);
const activeTodos = selectActiveTodos(store.getState());

规格模式与函数式编程

规格模式与函数式编程理念高度契合:

// 函数式实现
const createSpec = (predicate) => ({
  isSatisfiedBy: predicate,
  and: (other) => createSpec(c => predicate(c) && other.isSatisfiedBy(c)),
  or: (other) => createSpec(c => predicate(c) || other.isSatisfiedBy(c)),
  not: () => createSpec(c => !predicate(c))
});

// 使用
const isEven = createSpec(n => n % 2 === 0);
const isPositive = createSpec(n => n > 0);
const spec = isEven.and(isPositive);

[1, 2, 3, 4].filter(n => spec.isSatisfiedBy(n)); // [2, 4]

规格模式在UI组件中的应用

在React组件中使用规格模式控制渲染:

const ProductList = ({ products }) => {
  const [priceRange, setPriceRange] = useState({ min: 0, max: 1000 });
  const [category, setCategory] = useState('all');
  
  const filterSpec = new AndSpecification(
    category === 'all' 
      ? new AlwaysTrueSpecification()
      : new CategorySpecification(category),
    new PriceSpecification(priceRange.min, priceRange.max)
  );

  const filteredProducts = products.filter(p => filterSpec.isSatisfiedBy(p));

  return (
    <div>
      {/* 筛选控件 */}
      {filteredProducts.map(product => (
        <ProductItem key={product.id} product={product} />
      ))}
    </div>
  );
};

规格模式的变体与扩展

  1. 解释器规格:将规格转换为可解释的表达式
  2. 权重规格:为规格添加权重,实现加权评分
  3. 部分满足规格:返回满足程度而非布尔值
// 加权评分规格
class WeightedSpecification extends Specification {
  constructor(spec, weight) {
    super();
    this.spec = spec;
    this.weight = weight;
  }

  isSatisfiedBy(candidate) {
    return this.spec.isSatisfiedBy(candidate);
  }

  getScore(candidate) {
    return this.isSatisfiedBy(candidate) ? this.weight : 0;
  }
}

// 使用
const scoringSpec = new WeightedSpecification(
  new CategorySpecification('Electronics'), 0.6)
  .and(new WeightedSpecification(
    new PriceSpecification(100, 500), 0.4));

function sortByScore(items, spec) {
  return items
    .map(item => ({
      item,
      score: spec.getScore(item)
    }))
    .sort((a, b) => b.score - a.score)
    .map(({ item }) => item);
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌