Реализация лестничной площадки
Я столкнулся с описанием шаблона "Stairway" в книге "Adaptive code via С#", и я действительно не понимаю, как это должно быть реализовано:
(source)
Итак, у меня есть клиентская сборка:
using ServiceInterface;
namespace Client
{
class Program
{
static void Main(string[] args)
{
// Have to create service implementation somehow
// Where does ServiceFactory belong?
ServiceFactory serviceFactory = new ServiceFactory();
IService service = serviceFactory.CreateService();
service.Do();
}
}
}
Сборка интерфейса службы:
namespace Service
{
public interface IService
{
void Do();
}
}
И сборка службы:
using ServiceInterface;
namespace ServiceImplementation
{
public class PrintService : IService
{
public void Do()
{
Console.WriteLine("Some work done");
}
}
}
И возникает вопрос: как получить объект IService
в пространстве имен Client
? Где я должен разместить фактическое создание объекта new PrintService()
? Это не может быть частью ServiceInterface
, поскольку сборка интерфейса не зависит от ServiceImplementation
. Но он также не может быть частью Client
или ServiceImplementation
, потому что Client
должен зависеть только от ServiceInterface
.
Единственное решение, с которым я пришел, - это иметь сборку Application
поверх нее, которая имеет ссылки на все три (Client
, ServiceInterface
и ServiceImplementation
) и вставляет IService
в Client
. Я что-то пропустил?
Ответы
Ответ 1
Точки входа в приложение должны быть корнем композиции, как замечает Марк Земанн в книге "Инъекция зависимостей". Здесь проблема больше связана с инверсией зависимостей, при которой оба клиента и реализация должны зависеть от абстракций.
То, что эта диаграмма не показывает, но, мы надеемся, другие части книги ясно показывают, что точка входа будет естественно и обязательно ссылаться на все, что необходимо для создания любых ваших корней разрешения (контроллеры, службы и т.д.), Но это единственное место, где есть такие знания.
Имейте в виду, что иногда клиентам достаточно "владеть" интерфейсами, от которых они зависят: интерфейс ISecurityService
может жить в сборке Controllers
, IUserRepository
может жить в сборке ServiceImplementations
, и так далее. Это нецелесообразно, когдa > 1 клиент нуждается в доступе к интерфейсу, конечно.
Если вы следуете SOLID, вы, естественно, обнаружите, что инъекция зависимостей является необходимой, но контейнеры с инверсией управления не имеют приоритета. Я все чаще использую Pure Dependency Injection (ручное построение разрешающих корней).
Ответ 2
В этом случае проект Client
должен содержать ссылки как на Service
, так и на ServiceImplementation
. Эти ссылки будут использоваться только для создания контейнера IoC, который будет использоваться как DI. При запуске приложения вам необходимо зарегистрировать все реализации интерфейса в контейнере IoC.
Если вы реализуете интерфейс ServiceImplementation
против Service
, и вы будете кодировать Client
на основе Service
intereface, тогда не будет зависимости от ServiceImplementation
.
Вы также можете увидеть, как шаблон Stairway реализован в образцах для "Adaptive Code via С#":
https://github.com/garymcleanhall/AdaptiveCode/tree/master/Sprints/sample-sprint2-markdown
Ответ 3
Я бы поместил его в ServiceFactory
. Вам нужен какой-то параметр, например. передается в конструкторе factory или извлекается из конфигурации и т.д., который определяет, какая реализация IService
создается factory.