我有一门课,我一直在添加。
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,我必须抛出某种异常。
我可以继续,但这些是我目前遇到的最令人困惑的问题。有任何想法吗?