У меня есть класс, к которому я постоянно добавляю.
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...
}
Мне пришло в голову, что этот класс не является открытым-закрытым из-за добавления всех этих новых функций. Поэтому я подумал о том, чтобы закрыть этот класс от этого изменения, заключив эти функции в объекты 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>();
}
}
и это был хороший первый шаг. Однако обработчику запросов необходим точный тип сохраняемого контекста, которого у меня нет. Я мог бы использовать словарь типов для типов, но это противоречит цели быть открытым-закрытым. Так что в итоге я должен был сделать что-то вроде:
public class RequestProcessorBase<TRequestContext, TRequestProcessorBase> where TRequestContext : RequestContextBase<TRequestProcessorBase>
Это странно, и мне обычно не нравится любопытно повторяющийся шаблон . Кроме того, идея о том, что пользователь заполняет контекст и просит меня сделать запрос, кажется странной, хотя это может быть просто проблемой именования.
3.) Я думал о том, чтобы избавиться от всего вышеперечисленного и просто иметь:
public AddRequest{
public AddRequest(DependencyInjectionArg1, DependencyInjectionArg2, ...){}
public void PackArgs(UserSuppliedContextArg1, UserSuppliedContextArg2, UserSuppliedContextArg3, ...){}
}
что неплохо, но API уродлив. Теперь клиентам этого объекта нужно как бы «построить» его дважды. И если они забывают вызвать PackArgs, я должен вызвать какое-то исключение.
Я мог бы продолжить, но это самые запутанные проблемы, которые у меня есть на данный момент. Есть идеи?