规格模式(Specification)的业务规则组合
规格模式(Specification)的业务规则组合
规格模式是一种行为设计模式,用于将业务规则封装成可重用的对象,通过逻辑运算组合这些规则。它特别适合处理复杂的业务条件判断场景,使代码更清晰、更易维护。
规格模式的核心概念
规格模式由三个核心部分组成:
- 规格接口(Specification Interface):定义验证方法
- 具体规格(Concrete Specification):实现具体业务规则
- 组合规格(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)中扮演重要角色:
- 仓库模式(Repository):用于查询数据
- 规格模式:用于定义查询条件
- 领域模型:包含业务逻辑
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);
规格模式的性能优化
对于大型数据集,可以考虑以下优化策略:
- 提前终止:在组合规格中实现短路评估
- 缓存结果:对不变的数据缓存验证结果
- 转换为查询:将规格转换为数据库查询条件
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>
);
};
规格模式的变体与扩展
- 解释器规格:将规格转换为可解释的表达式
- 权重规格:为规格添加权重,实现加权评分
- 部分满足规格:返回满足程度而非布尔值
// 加权评分规格
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