Я уже давно использую контейнеры IoC, но сегодня я обнаружил, что в коде снова и снова появляется некий «шаблон». Чтобы дать вам некоторое представление, я сейчас работаю над веб-приложением, которое в основном используется для анализа данных. Там есть набор функций, которые требуют от пользователя подобрать то, что мы называем QueryTypeContex
в самом начале. После выбора этого типа запроса могут быть предприняты другие шаги, но все они выполняются в этом конкретном QueryTypeContex
. В графическом интерфейсе пикап QueryTypeContex
представлен как новая вкладка с другими элементами управления.
Когда пользователь работает с данным QueryTypeContex
, все ajax-вызовы на сервер включают QueryTypeId
, который идентифицирует выбор пользователя и используется для построения QueryTypeContex
на сервере, который затем используется для различного извлечения и обработки данных.
Я обнаружил, что многие из наших контроллеров (мы используем asp.net mvc), которые созданы с использованием контейнера Ioc, имеют одну общую черту. Есть метод действия, который выглядит примерно так:
public class AttributeController : Controller
{
public AttributeController(IUsefulService usefulService)
{
_usefulservice = usefulService;
}
ActionResult GetAttributes(QueryTypeContex context)
{
var dataDto = _usefulService.Manipulate(context, currentUser);
return JSon(dataDto);
}
...
}
Чтобы связать QueryTypeContex
с аргументом действия, мы используем привязку пользовательской модели, которая извлекает некоторую информацию из базы данных. Как только служба получает QueryTypeContex
в качестве аргумента, она передает ее или ее свойства своим соавторам в аргументах метода для уровня доступа к данным экземпляра. И вот есть фабричный класс, который выглядит так
public interface IDateValueFactory
{
DateValue CurrentYear(QueryTypeContex context);
DateValue RollingMonth(int numberOfMonths, QueryTypeContex context);
DateValue RollingQuareter(int numberOfQuarters, QueryTypeContex context);
}
public class DateValueFactory : IDateValueFactory
{
public DateValueFactory(IDateValueDb dateValueDb)
{
_dateValueDb = dateValueDb;
}
public DateValue CurrentYear(QueryTypeContext context)
{
var currentYear = _dateValueDb.GetCurrentYear(context.Id);
return new DateValue(DateValueType.CurrentYear, currentYear, context);
}
public DateValue RollingMonth(int numberOfMonths, QueryTypeContex context)
{
return new DateValue(DateValueType.RollingMonth, numberOfMonths, context);
}
...
}
Как вы видите, все эти методы получают QueryTypeContex
в качестве параметра, что более важно , все они получают один и тот же экземпляр QueryTypeContex
в течение своей короткой жизни (один веб-запрос). Поэтому я начал задаваться вопросом, могу ли я реорганизовать это так, чтобы всякий раз, когда многие методы класса обслуживания требовали QueryTypeContex
в качестве аргументов, они вводились бы через конструктор, а не передавали одно и то же значение снова и снова. Например:
public interface IDateValueFactory
{
DateValue CurrentYear();
DateValue RollingMonth(int numberOfMonths);
DateValue RollingQuareter(int numberOfQuarters);
}
public class DateValueFactory : IDateValueFactory
{
public DateValueFactory(IDateValueDb dateValueDb, QueryTypeContext context)
{
_dateValueDb = dateValueDb;
_context = context;
}
public DateValue CurrentYear()
{
var currentYear = _dateValueDb.GetCurrentYear(_context.Id);
return new DateValue(DateValueType.CurrentYear, currentYear, _context);
}
public DateValue RollingMonth(int numberOfMonths)
{
return new DateValue(DateValueType.RollingMonth, numberOfMonths, _context);
}
...
}
А теперь реальный вопрос:
Это хорошая идея для такого рода вещей или это нарушает некоторые принципы дизайна, которых я должен придерживаться?
Чтобы внедрить экземпляр QueryTypeContex
, созданный с использованием информации из http-запроса, я подумал о встраивании QueryTypeId в uris, чтобы он был доступен в RouteData на сервере. Затем, перед созданием контроллера, я мог бы вытащить его, собрать QueryTypeContex
, создать вложенный контейнер IoC для этого запроса и вставить его в контейнер. Тогда всякий раз, когда некоторому классу потребуется QueryTypeContex
для выполнения своей работы, он просто объявляет его как аргумент конструктора.