2014-11-10 9 views
1

Вдохновленный из Mark Seemann's post: Pattern Recognition: Abstract Factory or Service Locator?Является ли это правильной или жизнеспособной реализацией шаблона Abstract Factory?

Я хотел бы написать абстрактную фабрику, как так:

public interface IAbstractFactory { 
    T Create<T>(); 
} 

Затем, связывая его с помощью Ninject так:

IKernel kernel = new StandardKernel(); 
kernel.Bind<IAbstractFactory>().ToFactory(); 

Затем можно использовать следующим образом :

public class CustomerServiceIndicatorsModel { 
    public CustomerServiceIndicatorsModel(IAbstractFactory factory) { 
     this.emailIndicatorA = factory.Create<EmailIndicatorA>(); 
     this.emailIndicatorB = factory.Create<EmailIndicatorB>(); 
     this.emailIndicatorC = factory.Create<EmailIndicatorC>(); 
    } 
} 

Опять же, я не refe где-нибудь, где это происходит, и что это работает. Ядро упоминается только в файле Global.asax.cs для привязок.

Можно ли считать приемлемой реализацию абстрактного шаблона фабрики?

У меня есть трудное время, пытаясь понять суть этого шаблона. Я понимаю шаблон Factory явно как делегированный класс, который создает экземпляры заданного типа.

public interface IEmailIndicatorAFactory { 
    EmailIndicatorA Create(); 
} 

Затем, когда я связать его ToFactory() с помощью Ninject, он должен создавать экземпляры EmailIndicatorA.

Как связать IAbstractFactory<T> с использованием Ninject, так как каждый тип:

IAbstractFactory<EmailIndicatorA> 
IAbstractFactory<EmailIndicatorB> 
IAbstractFactory<EmailIndicatorC> 

считается совершенно конкретный тип. И я не могу найти способ связать его, используя Ninject.

И я не вижу никакой пользы написания такого интерфейса, если в ответе я должен написать:

public interface EmailIndicatorAFactory : IAbstractFactory<EmailIndicatorA> { } 
public interface EmailIndicatorBFactory : IAbstractFactory<EmailIndicatorB> { } 
public interface EmailIndicatorCFactory : IAbstractFactory<EmailIndicatorC> { } 

После замечания @PrestonGuillot, я упал в последней реализации ServiceLocator вместо AbstractFactory, поскольку я использую общий метод Create<T>(), а Abstract Factory использует не общий метод Create().

Спасибо за указание, @PrestonGuillot! =)

Возможно, я преувеличиваю здесь ...Так вот моя модель:

EmailIndicator

public abstract EmailIndicator { 
    int EmailCount { get; set; } 
    DateTime OldestDateTimeReceived { get; set; } 
} 

EmailIndicatorA

public class EmailIndicatorA : EmailIndicator { } 

EmailIndicatorB

public class EmailIndicatorB : EmailIndicator { } 

EmailIndicatorC

public class EmailIndicatorC : EmailIndicator { } 

IEmailIndicatorRepository

public interface IEmailIndicatorRepository<T> where T : EmailIndicator { 
    T GetIndicator(); 
} 

public class EmailIndicatorARepository : IEmailIndicatorRepository<EmailIndicatorA> { 
    public EmailIndicatorARepository(IExchangeService service 
     , IExchangeConfiguration configuration 
     , INetworkCredentialFactory credentialFactory) { 
     exchangeService = service; 
     exchangeService.Url = configuration.ExchangeUri; 
     exchangeService = credentialFactory.Create(configuration.Login, configuration.Password);  
    } 

    EmailIndicatorA GetIndicator() { 
     // Code to query Exchange through its Web services here... 
    } 
} 

И два друга хранилище, как этот существует, так как я должен запросить три различных серверов Exchange, в моем приложении.

Я считаю, что есть место для использования Abstract Factory, и поскольку я все еще изучаю шаблон, я просто не понимаю, как его реализовать в моей ситуации.

Если не для самих индикаторов, возможно, я смогу наконец разобраться с Abstract Factory с моими репозиториями.

Насколько я понимаю, идет, вот что я хотел бы попробовать в качестве первого шага к решению:

IRepositoryFactory

public interface IRepositoryFactory<T> where T : class, new() { 
    T Create(); 
} 

IEmailIndicatorARepositoryFactory

public interface IEmailIndicatorARepositoryFactory 
    : IRepositoryFactory<EmailIndicatorARepository> { 
    EmailIndicatorARepository CreateEmailIndicatorARepository(); 
} 

IEmailIndicatorBRepositor yFactory

public interface IEmailIndicatorBRepositoryFactory 
    : IRepositoryFactory<EmailIndicatorBRepository> { 
    EmailIndicatorBRepository CreateEmailIndicatorBRepository(); 
} 

И Abstract Factory?

public abstract IEmailIndicatorRepositoryFactory 
    : IEmailIndicatorARepositoryFactory 
    , IEmailIndicatorBRepositoryFactory 
    , IEmailIndicatorCRepositoryFactory { 
    EmailIndicatorARepository CreateEmailIndicatorARepository() { // create instance here... } 
    EmailIndicatorBRepository CreateEmailIndicatorBRepository() { // create instance here... } 
    EmailIndicatorCRepository CreateEmailIndicatorCRepository() { // create instance here... } 
} 

Это лучший подход?

Я как бы потерял и смутил здесь.

+0

Если вам нужны экземпляры 'EmailIndicatorA',' EmailIndicatorB' и 'EmailIndicatorC' для создания экземпляра' CustomerServiceIndicatorsModel', почему бы вам просто не использовать их в конструкторе? Похоже, вы не делаете ничего, что требует от фабрики абстрактной фабрики. –

+0

Вы правы. Возможно, мой пример не очень хорошо подобрался, и я не мог думать ни о чем другом с моей головы. Кроме того, как насчет реализации шаблона «Абстрактная фабрика»? –

+0

Независимо от того, сообщение в блоге, на которое вы ссылаетесь, напрямую отвечает на ваш вопрос. «Абстрактная фабрика - это общий тип * с универсальным методом Create *, локатор сервисов - это не общий тип с универсальным методом Create. *", вы попадаете в последнее. –

ответ

1

Я нахожу диаграммы Wikipedia article довольно просветительской.

Я думаю, что название «Абстрактная фабрика» немного вводит в заблуждение в мире .net/C#. Вам нужен только abstract class, если язык не поддерживает интерфейсы или вы хотите совместно использовать реализацию (но помните favor composition over inheritance).

Поскольку вы упомянули Марк Симэн я хотел бы, чтобы отогнать мою интерпретацию несколько постов, касающихся IoC и шоколадную фабрику:

  • не использует локатор службы
  • создать композитный корень. В идеале он должен создавать все объекты за один раз в начале.
    • как таковой вы должны проектировать свои объекты без учета состояния и только обрабатывать данные - по возможности (разделение данных и логика обработки). Это означает, что вам нужно только создавать более или менее классы данных/структуры данных, которые не имеют каких-либо зависимостей. Все объекты с зависимостями создаются за один проход при запуске приложения.
  • поздно творение не неспособности быстро
    • если вы используете позднее создание, назовет ли вам это служба поиск или завод, это уменьшает проверяемость, поскольку вопросы не будут видны при запуске приложения, а только когда вы используете/создаете объект позже.
    • Чтобы исправить это, создайте все зависимости объекта, который будет создан фабрикой уже @composition root. Только поздно создайте фактический объект.
    • Это делается с использованием абстрактного заводского шаблона.

Поэтому, учитывая ваш пример это будет:

public interface IEmailIndicatorFactory 
{ 
    IEmailIndicator Create(); 
} 

с тремя реализациями, один для EmailIndicatorA, EmailIndicatorB и EmailIndicatorC каждого. Теперь, чтобы аргумент имел значение, EmailIndicator нуждается в зависимости от EmailIndicatorA (или B, C). Скажем, это требует IServiceA:

internal class EmailIndicatorA : IEmailIndicator 
{ 
    private readonly IServiceA serviceA; 

    public EmailIndicatorA(IServiceA serviceA) 
    { 
     this.serviceA = serviceA; 
    } 

    (...) 
} 

Теперь должен быть соответствующим заводом, который принимает зависимости EmailIndicatorA:

internal class EmailIndicatorAFactory : IEmailIndicatorFactory 
{ 
    private readonly IServiceA serviceA; 

    public EmailIndicatorAFactory(IServiceA serviceA) 
    { 
     this.serviceA = serviceA; 
    } 

    public IEmailIndicator Create() 
    { 
     return new EmailIndicatorA(this.serviceA); 
    } 
} 

Вот и все. Если EmailIndicatorA имел дополнительные зависимости, их также следует вводить в EmailIndicatorAFactory.

Теперь преимущество выполнения всего этого кода происходит не быстро. Все другие проблемы могут быть устранены путем правильного использования привязок и интерфейсов .ToFactory(), которые не являются слишком общими, но конкретными (в основном такими же, как с абстрактной фабрикой выше!).

Я думаю, вы должны тщательно взвесить про и против.

Марк также сказал, что он редко использует контейнеры IoC. Вместо этого он чаще всего предпочитает дитя бедного человека. См. this post.

Я думаю, что Марк очень хорош в том, чтобы указывать про и на кон, и читать его сообщения очень образованно. Я не согласен со всеми его выводами, но TBH я хотел бы с ним работать в течение некоторого времени (не только на несколько недель, дольше) и , а затем посмотреть, согласен ли я или нет.

Сказав, что я хотел бы предложить вам альтернативный способ обработки вариантов использования абстрактного шаблона фабрики. Так как я уже отправил его на SO до этого ничего не оставалось делать, кроме как к link it :)

В последней ноты, я не знаю ни одного IoC контейнера, который показывает Func<> или интерфейс фабрики, как .ToFactory() Ninject, который в начале создания экземпляров зависимостей , Это делается только после выполнения Func<> или вызова метода IFooFactory.CreateFoo(). Таким образом, проблема отказоустойчивости еще не решена автогенерированными фабриками. Однако, что сделано, что некоторые контейнеры имеют методы проверки, которые проверяют, что типы могут быть созданы, и что нет Captive Dependencies. Например, Simple Injector

+0

Вдохновленный вашим подходом, я, наконец, решил продолжить работу с провайдерами, которые, как мне кажется, являются более простым способом, хотя на самом деле он выполняет ту же задачу, то есть создает экземпляры экземпляров на основе какого-то контекста или зависимостей , С другой стороны, я всегда ценю ваши ответы. Они всегда просвещали мой путь к более правильному использованию ДИ и IoC. Спасибо. –