Альтернативы инъекции зависимостей
Я смотрю на инъекцию depency, я вижу преимущества, но у меня возникают проблемы с синтаксисом, который он создает. У меня есть этот пример
public class BusinessProducts
{
IDataContext _dx;
BusinessProducts(IDataContext dx)
{
_dx = dx;
}
public List<Product> GetProducts()
{
return dx.GetProducts();
}
}
Проблема в том, что я не хочу писать
BusinessProducts bp = new BusinessProducts(dataContextImplementation);
Я бы продолжал писать
BusinessProducts bp = new BusinessProducts();
потому что я чувствую, что первая альтернатива просто кажется неестественной. Я не хочу знать, от чего зависит бизнес-продукт, чтобы получить продукты, также я чувствую, что мой код становится более нечитаемым.
Есть ли какие-либо альтернативы этому подходу, поскольку я хотел бы сохранить свой оригинальный синтаксис для создания объектов, но я хотел бы по-прежнему иметь возможность подделывать зависимости при модульном тестировании или это может сделать для меня эта зависимость? /p >
Я кодирую в С#, но альтернативы с других языков приветствуются
Ответы
Ответ 1
Я использую factory для моего контекста и вставляю его, предоставляя подходящее значение по умолчанию, если предоставленный factory имеет значение null. Я делаю это по двум причинам. Во-первых, я использую контекст данных как единицу работы с объектом, поэтому мне нужно иметь возможность создавать их, когда это необходимо, а не поддерживать их. Во-вторых, я в первую очередь использую DI для повышения тестовой способности, с развязкой только вторичного рассмотрения.
Итак, мой класс бизнес-продуктов будет выглядеть так:
public class BusinessProducts
{
private IDataContextFactory DataContextFactory { get; set; } // my interface
public BusinessProducts() : this(null) {}
public BusinessProducts( IDataContextFactory factory )
{
this.DataContext = factory ?? new BusinessProductsDataContextFactory();
}
public void DoSomething()
{
using (DataContext dc = this.DataContextFactory().CreateDataContext())
{
...
}
}
Альтернативой этому было бы сделать свойство factory общедоступным и ввести альтернативный factory, установив свойство. В любом случае, если вы хотите сохранить нулевой конструктор, вам необходимо указать значение по умолчанию.
Ответ 2
Вы можете создать factory. Контейнеры DI лучше всего подходят для проводки, которые происходят во время настройки - не во время выполнения (как это выглядит в случае). Фабрики могут быть реализованы по-разному, в зависимости от того, насколько они должны быть подключаемыми, и сколько мест вам нужно использовать.
Ответ 3
Обычно я имел пустой конструктор, который использует массивный экземпляр (или экземпляры, созданные IoC), и один с DI. т.е.
public class BusinessProducts
{
IDataContext _dx;
BusinessProducts()
{
_dx = new SolidDataContext();
}
BusinessProducts(IDataContext dx)
{
_dx = dx;
}
}
Таким образом вы можете использовать DI для переопределения экземпляра по умолчанию при тестировании модульного тестирования.
Ответ 4
Ваши чувства, хотя и действительны, неуместны.
Образец Dependency Injection является прямым приложением Inversion Принципа управления.
Это означает, что вместо того, чтобы ваш класс контролировал экземпляры других классов, которые он потребляет, это отношение инвертируется и ему предоставляются зависимости.
Таким образом, ваши классы естественным образом выставляют свои зависимости через аргументы или свойства конструктора. Показ презрения к этой структуре говорит, что вы по-настоящему не замаскировали шаблон.
Ответ 5
Здесь два различных случая:
В производственном коде вы никогда не будете писать
new BusinessProducts(dataContextImplementation)
потому что инъекция зависимостей, как правило, создает для вас полную иерархию объектов. Это "вирусная" природа шаблонов инъекций зависимостей, они, как правило, полностью контролируют создание ваших сервисов.
В коде unit test вы обычно создаете это самостоятельно, но нередко вы будете поставлять макет или реализацию заглушки dataContextImplementation. Поэтому обычно вы будете вводить объект, который не имеет большого количества последующих зависимостей.
Ответ 6
http://springframework.net/ и http://structuremap.sourceforge.net/Default.htm вероятно, являются в основном используемыми структурами DI для .NET-языков и будут делать то, что вам нужно.
Ответ 7
Как правило, сама структура будет иметь логику построения всего дерева объектов. Например, вместо
new SomeObjectO(diContext)
вы бы назвали фреймворк следующим образом:
DIFramework.GetNew<SomeObjectO>();
или
DIFramework.Get<SomeObject>();
Еще одна интересная структура, на которую вы можете взглянуть, если вы хотите узнать о DI, а процесс - проекты Microsoft Unity и Object Builder.
Ответ 8
Если вам действительно не нравится вводить этот экземпляр в конструктор, вы можете попытаться использовать CommonServiceLocator с вашим любимым совместимым. NET. Это позволит вам написать такой код:
public class BusinessProducts
{
IDataContext _dx;
BusinessProducts()
{
_dx = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IDataContext>();
}
public List<Product> GetProducts()
{
return dx.GetProducts();
}
}
Однако, будьте осторожны, что это не то, что большинство людей ожидали бы, когда узнают, что вы используете инфраструктуру инъекций зависимостей. Я думаю, что гораздо более распространено использование инфраструктуры инъекций зависимостей и предоставление им всех объектов для вас.
BusinessProducts bp = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<BusinessProducts>();
Если вы хотите избежать пути к интерфейсу зависимостей dependeny, использование factory, вероятно, лучший способ.
Ответ 9
Там есть техника, называемая бедным человеком DI, которая выглядит так:
public class BusinessProducts
{
IDataContext _dx;
BusinessProducts() : this(new DataContext()) {}
BusinessProducts(IDataContext dx)
{
_dx = dx;
}
public List<Product> GetProducts()
{
return dx.GetProducts();
}
}
Это не идеально, так как он связывает вас с реализацией, но является хорошим шагом в направлении развязанного кода. это похоже на @tvanfosson, но намного проще.
Вторые рекомендации для Windsor
Ответ 10
Мой код будет ссылаться на Microsoft Unity, но я уверен, что он довольно применим ко всем структурам DI. Если вы правильно используете DI, вам никогда не нужно вызывать новый BusinessObject (новый dataContext), ассоциация DI будет обрабатывать все это для вас.
Мой пример будет немного длинным, так как я буду вставлять код, который я использую для запуска сайта View Viewer Model View, полностью загруженного Unity. (Если вы хотите, чтобы полный источник просматривал мой блог и загружал его с моего сервера SVN Assembla)
Загрузите контейнер (может быть в коде, как я предпочитаю или использую конфигурацию)
protected void Application_Start(object sender, EventArgs e)
{
Application.GetContainer()
// presenters / controllers are per request
.RegisterType<IEmployeeController, EmployeeController>(new ContextLifetimeManager<IEmployeeController>())
//Data Providers are Per session
.RegisterType<IEmployeeDataProvider, EmployeeDataProvider>(new SessionLifetimeManager<IEmployeeDataProvider>())
//Session Factory is life time
.RegisterType<INHibernateSessionManager, NHibernateSessionManager>(new ContainerControlledLifetimeManager());
}
Пользовательский HTTP-модуль вызывает метод Unity BuildUp для каждой страницы во время вызова OnPreRequest.
private static void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
var handler = HttpContext.Current.Handler;
HttpContext.Current.Application.GetContainer().BuildUp(handler.GetType(), handler);
// User Controls are ready to be built up after the page initialization is complete
var page = HttpContext.Current.Handler as Page;
if (page != null)
{
page.InitComplete += OnPageInitComplete;
}
}
Презентатор контейнера страницы, украшенный атрибутом [Dependency]
public partial class Employees : Page, IEmployeeView
{
private EmployeePresenter _presenter;
[Dependency]
public EmployeePresenter Presenter
{
set
{
_presenter = value;
_presenter.View = this;
}
}
}
Ведущий с методом InjectionConstructor
public class EmployeePresenter : Presenter<IEmployeeView>
{
private readonly IEmployeeController _controller;
[InjectionConstructor]
}
public EmployeePresenter(IEmployeeController controller)
{
_controller = controller;
}
Контроллер следует примеру
public class EmployeeController : IEmployeeController
{
private readonly IEmployeeDataProvider _provider;
[InjectionConstructor]
public EmployeeController(IEmployeeDataProvider DataProvider)
{
_provider = DataProvider;
}
}
То же самое с провайдером
public class EmployeeController : IEmployeeController
{
private readonly IEmployeeDataProvider _provider;
[InjectionConstructor]
public EmployeeController(IEmployeeDataProvider DataProvider)
{
_provider = DataProvider;
}
}
Наконец, менеджер сеансов, содержащий только обычный конструктор.
public class NHibernateSessionManager : INHibernateSessionManager
{
private readonly ISessionFactory _sessionFactory;
public NHibernateSessionManager()
{
_sessionFactory = GetSessionFactory();
}
}
Итак, что происходит при запуске запроса страницы, метод BuildUp() вызывается на странице с помощью HttpModule. Затем Unity видит свойство, отмеченное атрибутом Dependency, и проверит его контейнер, чтобы увидеть, существует ли внутри него объект EmployeePresenter.
Поскольку в контейнере такого объекта нет, он попытается создать EmployeePresenter. После проверки, чтобы создать класс, который он видит внутри Presenter, ему нужен конструктор, которому необходимо ввести IEmployeeController. Поскольку в контейнере есть менеджер для контроллера, он увидит, существует ли экземпляр его в контейнере, который в начале запроса страницы не существует, поэтому он перейдет к созданию экземпляра контроллера.
Unity увидит, что контроллер требует ввода IEmployeeDataProvider, и он продолжит этот процесс, пока он, наконец, не дойдет до того момента, когда провайдеру понадобится менеджер сеанса. Поскольку диспетчер сеансов больше не нуждается в инъекции, Unity затем создаст экземпляр диспетчера сеансов, который хранит его в контейнере, для этого предоставил ContainerLifeTimeManager, ввел его в Провайдер и сохранил этот экземпляр и так далее до того места, где он закончил создание Зависимость EmployeePresenter для страницы.
Ответ 11
вы также можете посмотреть windsor для IoC.