规范模式比较,Func谓词和管道过滤器
我正在做一些研发工作,因此正在探索设计模式。我最近一直在阅读规范模式并被引用 这篇很棒的文章。
我对代码的简单性和简洁性很感兴趣,但我开始对使用其他技术实现相同的简洁性进行一些比较。
考虑服务层的以下接口契约:
public interface IFooDataService
{
ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}
因此,一些初始点:
- 所有三个都返回 Foo 对象的集合
- 所有三个都采用一个参数
- 规范方法限制对特定需求的访问
- 谓词方法基本上没有限制
- 搜索参数方法限制对特定需求的访问需求
现在,进入实现:
public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
return fooDataRepository
.Find()
.Where(f => specification.IsSatisfiedBy(f))
.ToList();
}
public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
return fooDataRepository
.Find()
.Where(predicate)
.ToList();
}
public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
return fooDataRepository
.Find()
.WhereMeetsSearchCriteria(searchArgs)
.ToList();
}
实现要点:
- 所有这三个实现都非常简单(一行链式代码)
- 规范和搜索参数过滤在外部实现。
- Search args 方法只是使用 IEnumerable 扩展方法来检查 args
那么,话虽如此,在什么条件下您会使用上述 3 种技术之一?
我对规范模式的看法:
- 很好,因为它将业务/领域需求隔离到可重用的组件中
- 非常易于阅读,使代码说英语
- 涉及相当多的代码(接口、抽象类)。如果我要使用它,我会将抽象放在一个公共程序集中(所以我的解决方案中没有一堆静态文件)。
- 只需更改规范即可轻松更改需求,而不需要更改服务层。
- 领域逻辑的最高可测试性(规范)
我对扩展方法(管道和过滤器)的想法:
- 逻辑上的“重量”,但仍然导致相同的简单性。
- 将查询逻辑从服务层隔离到静态方法
- 仍然需要排序的“反射”(检查提供的搜索参数并构建查询)
- 允许您首先对架构(存储库、服务层)进行编码,而无需考虑特定的业务需求(这在 我对谓词
方法的想法:
- 可以在需要对查询进行粗粒度控制的地方使用。
- 适合小型项目,因为规范可能会过度。
我的最终思想逻辑是,如果您正在开发一个复杂的业务应用程序,其中业务需求是预先已知的,但可能会随着时间的推移而变化,那么我会使用规范模式。
但对于“启动”应用程序,即需求将随着时间的推移而发展,并且有多种方法来检索数据而无需复杂的验证,我将使用管道和过滤器方法。
你有什么想法?你们中有人在使用上述方法时遇到过问题吗?有什么建议吗?
即将开始一个新项目,因此这些类型的考虑因素至关重要。
感谢您的帮助。
编辑以澄清规范模式
这是规范模式的相同用法。
Specification<Foo> someSpec; // Specification is an abstract class, implementing ISpecification<TEntity> members (And, Or, Not, IsSatisfiedBy).
someSpec = new AllFoosMustHaveABarSpecification(); // Simple class which inherits from Specification<Foo> class, overriding abstract method "IsSatisfiedBy" - which provides the actual business logic.
ICollection<Foo> foos = fooDataService.GetFoosBySpecification(someSpec);
I'm doing some R&D work, and as such am exploring design patterns. I have recently been reading up on the Specification pattern and was referred to this great article.
I was intrigued by the simplicity and cleanliness of the code, but i started to draw some comparisons to implementing the same cleanliness using other techniques.
Consider the following interface contract for a service layer:
public interface IFooDataService
{
ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}
So, some initial points:
- All three return a collection of Foo objects
- All three take one single argument
- Specification method restricts access to specific requirements
- Predicate method has basically no restriction
- Search args method restricts access to specific requirements
Now, onto the implementation:
public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
return fooDataRepository
.Find()
.Where(f => specification.IsSatisfiedBy(f))
.ToList();
}
public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
return fooDataRepository
.Find()
.Where(predicate)
.ToList();
}
public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
return fooDataRepository
.Find()
.WhereMeetsSearchCriteria(searchArgs)
.ToList();
}
Points on the implementation:
- All three are extremely simple in implementation (one line of chained code)
- Specification and Search Args filtered implemented externally.
- Search args method simply uses IEnumerable extension method to inspect args
So, that being said, under what conditions would you use one of above 3 techniques?
My thoughts on Specification Pattern:
- Nice in that it isolates business/domain requirements into reusable components
- Extremely easy to read, makes code speak english
- Fair bit of code involved (interfaces, abstract classes). If i were to use this, i would put the abstractions in a common assembly (so i dont have a bunch of static files in my solution).
- Easy to change requirements by only changing specification, and not service layer.
- Supreme testability of domain logic (specifications)
My thoughts on Extension Methods (Pipes & Filters):
- 'Weighty' in logic, but still result in the same simplicity.
- Isolate query logic from service layer to static methods
- Still requires "reflection" of sort (inspecting supplied search args and building up query)
- Allows you to code architecture (repository, service layer) first, without thinking about specific business requirements (which is handy in certain scenarios)
My thoughts on Predicate Method:
- Could be used where you need coarse grained control over the queries.
- Good for small projects, where specifications may be overdoing it
My final thought logic is that if you are working on a complex business application where business requirements are known up front but may change over time, then i would use Specification pattern.
But for a application that is a "startup", ie requirements will evolve over time and has a multitude of ways to retrieve data without complex validation, i would use the Pipes and Filters methods.
What are your thoughts? Have any of you run into problems with any of the above methods? Any recommendations?
About to start a new project so these types of considerations are critical.
Thanks for the help.
EDIT for Clarification on Specification pattern
Here is same usage of the Specification pattern.
Specification<Foo> someSpec; // Specification is an abstract class, implementing ISpecification<TEntity> members (And, Or, Not, IsSatisfiedBy).
someSpec = new AllFoosMustHaveABarSpecification(); // Simple class which inherits from Specification<Foo> class, overriding abstract method "IsSatisfiedBy" - which provides the actual business logic.
ICollection<Foo> foos = fooDataService.GetFoosBySpecification(someSpec);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
根据我的一点经验:
在一个晴朗的一天,我的朋友告诉我,他写了一个类,使我们能够计算 32 种计算 X 的方法。他已经实现了所有这些。呵呵,我认为这真是一个英雄般的编程。他花了几个星期的时间在半夜做这件事。他相信自己是一个优秀的程序员,所以他坚持每个人都使用他的杰作。
我们当时不关心单元测试,所以我们使用他的杰作。然后发生了什么?代码总是崩溃。嗯,从那时起我就意识到单元测试和模块化是多么重要。
From my little experience :
In a one fine day, my mate told me that he wrote a class which enabled us to calculate 32 kinds of way of calculating X. And he already implemented all that. Hoho, that was such a heroic programming i think. He spent several weeks doing that in the middle of the night. He believed that he was a good programmer so he insisted everybody to use his masterpiece.
We, at that time, didn't care about unit testing so we used his masterpiece. What happened then? The code crashed all the time. Well, from that time i realized how important unit test and modularity are.
好吧,首先我要编写 Predicate 方法,即使它仅用作其他两个方法的私有实现细节:
搜索参数函数将是类似的单行函数。
除此之外,我真的不能说任何抽象的话。我必须更多地了解数据结构才能决定寻找它们的最佳方式。
Well, first I'd write the Predicate method, even if it's only used as a private implementation detail for the other two:
The search argument function would be a similar one-liner.
Beyond that, I really can't say anything in the abstract. I'd have to know more about the data structure to decide the best way to seek them.