0

我有一门课,我一直在添加。

public class OrderRepository{
    public void Add(IEnumerable<Order> orders){}
    public void Update(IEnumerable<Order> orders){}
    public void Remove(IEnumerable<Order> orders){}
    public void Expedite(IEnumerable<Order> orders){}
    public void GetOrderData(Order order, DateTime start, DateTime end)
    etc...
}

我突然想到这个类不是 Open-Closed,因为所有这些新特性都被添加了。所以我考虑通过将这些函数封装到 Request 对象中来关闭这个类以防止这种变化。我最终得到类似的东西:

public abstract class RequestBase{}

public class AddRequest : RequestBase{}

etc...

public class OrderRepository{
    public void ProcessRequest(RequestBase request){}
}

这使得 OrderRepository 对扩展开放,对修改关闭。但是,我很快遇到了一些问题:

1.) 请求需要操作的数据是用户提供的(运行时)和依赖注入提供的。我显然不能同时满足一个构造函数。我不能这样做:

public class AddRequest{
    public AddRequest(IEnumerable<Order> orders, int UserSuppliedContextArg1, DependencyInjectionArg1, DependencyInjectionArg2);
}

并称之为。我想要一种方法让 DI 框架为我“部分地”构造一个对象,然后让我做剩下的事情。但是,我看不到任何这样做的方法。我看到一个博客把这个概念称为“变量构造函数注入”。

2.) 我想到的下一件事是将它分成 2 个单独的类。用户将创建并填充一个 RequestContext,然后将其传递到存储库中,这将创建一个 RequestProcessor(想不出更好的名称)。我想过这样做:

public abstract class RequestContextBase<T> where T : RequestProcessorBase{}

public class AddRequestContext : RequestContextBase<AddRequestProcessor>

public class OrderRepository{
    public void ProcessRequest<T>(RequestBase<T> request){
        var requestProcessor = IoC.Create<T>();
    }
}

这是一个很好的第一步。但是,请求处理器需要它存储的上下文的确切类型,而我在这里没有。我可以将类型字典用于类型,但这违背了 Open-Closed 的目的。所以我最终不得不做类似的事情:

public class RequestProcessorBase<TRequestContext, TRequestProcessorBase> where TRequestContext : RequestContextBase<TRequestProcessorBase>

这很奇怪,我通常不喜欢奇怪重复出现的模板模式。此外,用户填写上下文并要求我提出请求的想法似乎很奇怪,尽管这可能只是一个命名问题。

3.)我想摆脱以上所有,只拥有:

public AddRequest{
    public AddRequest(DependencyInjectionArg1, DependencyInjectionArg2, ...){}

    public void PackArgs(UserSuppliedContextArg1, UserSuppliedContextArg2, UserSuppliedContextArg3, ...){}
}

这还不错,但是 API 很丑。现在,这个对象的客户需要“构造”两次,就像以前一样。如果他们忘记调用 PackArgs,我必须抛出某种异常。

我可以继续,但这些是我目前遇到的最令人困惑的问题。有任何想法吗?

4

2 回答 2

1

存储库是您的域的一部分。使其通用化,虽然很诱人,但却违背了它作为无处不在的语言运营之家的目的。如果您可以对存储库做任何事情,那么您已经混淆了它的意图。

如果一个类遵循 SRP,则“不断添加”它违反了定义。这表明您正在引入的操作可能由服务更好地解决,或者应该与存储库分离。

编辑以回应评论

您希望将无处不在的语言保持在公开状态,同时确保班级承担最低限度的责任。

从存储库中分离出操作将是第一步。你可以这样做:

public interface IOrderExpeditionService
{
    void Expedite(IEnumerable<Order> orders);
}

public interface IOrderDataService
{
    void GetOrderData(Order order, DateTime start, DateTime end);
}
于 2009-03-04T04:28:43.603 回答
0

Ayende 有几个关于这个主题的帖子。

基本上你想要做的是将你的查询从你的存储库中分离出来,然后把你的查询变成你编写的东西。使用由组合构造的查询,您可以轻松扩展它以添加新的查询方法,而无需向您的存储库添加新方法。你可以得到这样的结果:

public class Repository<T>
{
   T Find(IQueryCriteria queryCriteria);
}

实际上,我还没有在 NHibernate 中完成此操作,但我们使用 LLBLGenPro 完成了此操作,并且效果非常好。我们为查询对象使用了一个流畅的接口,因此我们可以编写如下查询条件:

var query = new EmployeeQuery()
   .WithLastName("Holmes")
   .And()
   .InDepartment("Information Systems");

var employee = repository.Find(query);

扩展存储库的功能就相当于简单地在查询对象上添加新方法。

于 2009-03-04T02:22:26.977 回答