规格模式(Specification)
规格模式(Specification)
需求如下:需要从数据库的Cargo表中取出商品名称为“钉子”的货物。
可以这样做:
public class CargoRepository
{
public List SelectByPartName(string partName){}
}
当需求发生变化了,它要从数据库的Cargo表中取出商品编码为“0001”的货物。
可以这样做:
public class CargoRepository
{
public List SelectByPartCode(string partCode){}
}
当需求又一次变化了,它要从数据库的Cargo表中取出商品名称为“钉子”并且商品编码为“0001”的货物。
这时候问题严重了,看下上面的方法都是一些特定的查询,可能还有很多这样的查询。
他们单个的查询通过组合又形成了新的查询。这就是组合爆炸。
有两种模式可以解决这样的问题:Specification-[Evans的DDD]和Query Object[Fowler,PEAA]
这里只讨论Specification
在对DDD一书中提到:
(原文如下:业务规则不适于放在任何已个实体或值对象中,而且规则的变化和组合会掩盖那些领域对象的基本含义。)
有时经常造成组合爆炸。规格是模型的一部分,将它们从实体或值对象中独立出来有助于使模型更加清晰,它表达的是业务的规则。规格是值对象,它用来判断对象是否满足标准的谓词。谓词是指计算结果未true或false的函数,可以用and、or、not操作符连接。
对规格的接口定义:
public interface ISpecification
{
bool isSatisfiedBy(object candidate);

ISpecification and(ISpecification other);
ISpecification or(ISpecification other);
ISpecification not();
}
我们希望具体的规格只实现isSatisfiedBy方法,所以定义抽象类:
public abstract class AbstractSpecification : ISpecification
{
ISpecification 成员
定义操作符谓词:
internal class AndSpecification : AbstractSpecification
{
ISpecification one;
ISpecification other;
public void SetAndSpecification(ISpecification x, ISpecification y)
{
one = x;
other = y;
}
public override bool isSatisfiedBy(object candidate)
{
return one.isSatisfiedBy(candidate) && other.isSatisfiedBy(candidate);
}
}
internal class OrSpecification : AbstractSpecification
{
ISpecification one;
ISpecification other;
public void SetOrSpecification(ISpecification x, ISpecification y)
{
one = x;
other = y;
}
public override bool isSatisfiedBy(object candidate)
{
return one.isSatisfiedBy(candidate) || other.isSatisfiedBy(candidate);
}
}
internal class NotSpecification : AbstractSpecification
{
ISpecification wrapped;
public void SetNotSpecification(ISpecification notSpec)
{
wrapped = notSpec;
}
public override bool isSatisfiedBy(object candidate)
{
return !wrapped.isSatisfiedBy(candidate);
}
}
如果使用规格模式,系统中会存在许多小颗粒的规格类。性能会有影响。这里使用享元模式获取规格。
public class SpecificationFactory
{
private static readonly SpecificationFactory instance = new SpecificationFactory();
private Dictionary<string, ISpecification> m_SpecList;

private SpecificationFactory() { }

public static SpecificationFactory Instance()
{
return instance;
}

public ISpecification GetSepcification(string assembly,string type)
{
ISpecification spec;
if (m_SpecList==null) { m_SpecList = new Dictionary<string, ISpecification>(); }
string key = assembly + type;
if (!m_SpecList.ContainsKey(key))
{
spec = Assembly.Load(assembly).CreateInstance(type) as ISpecification;
if (spec!=null) { m_SpecList.Add(key, spec); return spec; }
throw new Exception(type + "规格不存在,请确保存" + assembly + "中存在" + type + "类");
}
return m_SpecList[key];
}
}
这里简单的测试:
[TestMethod]
public void TestSpec()
{
SpecificationFactory sf = SpecificationFactory.Instance();
Cargo cargo=null;
ISpecification spec = sf.GetSepcification("Bmrxntfj", "Bmrxntfj.Specification.NullSpecification");
Assert.AreEqual(true, spec.isSatisfiedBy(cargo));
spec=spec.or(new EqualSpecification());
Assert.AreEqual(true, spec.isSatisfiedBy(cargo));
cargo = new Cargo();
cargo.PartCode = 2;
cargo.PartName = "2";
Assert.AreEqual(false, spec.isSatisfiedBy(cargo));
}
然而我们却不常这样做。因为有些ORM框架采用对象的属性来查询。在基础结构层中生成SQL,这样我们就不需要这样麻烦了。
需求如下:需要从数据库的Cargo表中取出商品名称为“钉子”的货物。
可以这样做:
public class CargoRepository
{
public List SelectByPartName(string partName){}
}
当需求发生变化了,它要从数据库的Cargo表中取出商品编码为“0001”的货物。
可以这样做:
public class CargoRepository
{
public List SelectByPartCode(string partCode){}
}
当需求又一次变化了,它要从数据库的Cargo表中取出商品名称为“钉子”并且商品编码为“0001”的货物。
这时候问题严重了,看下上面的方法都是一些特定的查询,可能还有很多这样的查询。
他们单个的查询通过组合又形成了新的查询。这就是组合爆炸。
有两种模式可以解决这样的问题:Specification-[Evans的DDD]和Query Object[Fowler,PEAA]
这里只讨论Specification
在对DDD一书中提到:
(原文如下:业务规则不适于放在任何已个实体或值对象中,而且规则的变化和组合会掩盖那些领域对象的基本含义。)
有时经常造成组合爆炸。规格是模型的一部分,将它们从实体或值对象中独立出来有助于使模型更加清晰,它表达的是业务的规则。规格是值对象,它用来判断对象是否满足标准的谓词。谓词是指计算结果未true或false的函数,可以用and、or、not操作符连接。
对规格的接口定义:



























































































然而我们却不常这样做。因为有些ORM框架采用对象的属性来查询。在基础结构层中生成SQL,这样我们就不需要这样麻烦了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探