我有以下基本实体:
public class Basket
{
public List<Product> Products {get;set;}
}
public class Product
{
public string Name {get;set;}
public decimal Price {get;set;}
}
我想获得一个低于固定价格的篮子里所有产品的清单。这个逻辑是否应该在Basket
中进行,如下所示:
public class Basket
{
public List<Product> Products {get;set;}
public List<Product> CheapProducts
{
get { return Products.Where(p => p.Price < 5).ToList(); }
}
}
或者它应该放在服务类ProductFilterer
中,它会将整个产品列表作为参数,并返回已过滤的产品列表。或者也许它应该直接进入调用类的方法?
还是其他什么?这是什么最好的做法?
答案 0 :(得分:2)
您可以考虑查看Specification Pattern。该链接具有良好的示例实现,但简而言之,该模式允许您基于简单谓词(或规范)创建复杂的选择标准。
使用委托的这种模式的快速(和不完整)实现可以这样做:
public class Specification<T>
{
Func<T, bool> _spec;
public Specification(Func<T, bool> spec)
{
_spec = spec;
}
public bool IsSatisifedBy(T item)
{
return _spec(T);
}
}
// ...
_cheapProductsSpecification = new Specification<Product>(p => p.Price < 5);
var cheapProducts = Basket.Products.Where(p => _cheapProductsSpecification.IsSatifisifedBy(p));
当然,这是一个简单且可能是多余的示例,但如果您添加And,Or和Not(请参阅链接),则可以将复杂的业务逻辑构建为规范变量。
答案 1 :(得分:2)
如果“廉价产品”的概念是一流的领域概念并且必须以无处不在的语言引入,那么我要做的就是与领域专家见面。
如果是这种情况,Steve的规范解决方案将以优雅的方式解决您的问题。
如果廉价不重要或没有明确定义(例如,如果廉价阈值在整个应用程序中有所不同),我不会为此创建特定实体,只需在需要时过滤具有相关标准的Basket.Products在调用代码。
答案 2 :(得分:0)
是的,我建议将您的DTO与业务逻辑分开。我喜欢将数据对象视为与数据访问,业务和UI层完全独立的层。如果您有一个更通用的ProductBusiness类,我建议将它放在那里,除非有一个单独的过滤器类非常有用。
答案 3 :(得分:0)
你的篮子类不应该知道如何直接过滤,它是正确的,它有一个暴露的功能,允许它按照你的建议从ProductFilter返回结果。代码的外观如下所示:
class ProductFilter
{
filterCheapProducts(Collection<Product> productsToFilter)
{
return Products.Where(p => p.Price < 5).ToList(); //I assume your code is correct
}
}
class Basket
{
Collection<Product> getCheapProducts()
{
return filter.filterCheapProducts(this.products);
}
}