2014-10-24 4 views
0

У меня возник вопрос о создании фабричного интерфейса с помощью метода create, который может обслуживать прием различных типов аргументов в зависимости от реализации.Factory Interface Create Method with object Аргумент

Чтобы дать вам немного больше фона, я использую зависимость в инъекции в проекте и требую, чтобы объекты с сохранением состояния генерировались во время выполнения - поэтому я создаю объекты, а не сами объекты, вместо самих объектов. Проблема, с которой я столкнулся, заключается в том, что для некоторых интерфейсов конкретные реализации просто не могут иметь одинаковые типы аргументов конструктора, поэтому фабрики, которые создают экземпляр этих интерфейсов, требуют почти «динамических» аргументов для передачи методу create.

Я занимаюсь этим в течение нескольких дней, и следующее - лучшее решение, которое я мог бы придумать (а именно, передать объект методу создания фабрики и направить его в конкретную реализацию фабрики) , Я действительно ищу отзывы от людей, которые раньше сталкивались с этим сценарием, чтобы узнать, что они придумали, и приемлемо ли решение, которое я предлагаю ниже.

Извините, если это отсутствует какая-либо информация, и большое спасибо!

// 
// Types... 
// 

interface IDataStore 
{ 
    List<string> GetItems(); 
} 

public class XmlDataStore : IDataStore 
{ 
    public XmlDataStore(XmlDocument xmlDoc) 
    { 
     // Initialise from XML Document... 
    } 

    public List<string> GetItems() 
    { 
     // Get Items from XML Doc... 
    } 
} 

public class SQLDataStore : IDataStore 
{ 
    public SQLDataStore(SqlConnection conn) 
    { 
     // Initialise from SqlConnection... 
    } 

    public List<string> GetItems() 
    { 
     // Get Items from Database Doc... 
    } 
} 

// 
// Factories... 
// 

interface IDataStoreFactory 
{ 
    IDataStore Create(object obj); 
} 

class XmlDataStoreFactory : IDataStore 
{ 
    IDataStore Create(object obj) 
    { 
     // Cast to XmlDocument 
     return new XmlDataStore((XmlDocument)obj); 
    } 
} 

class SQLDataStoreFactory : IDataStore 
{ 
    IDataStore Create(object obj) 
    { 
     // Cast to SqlConnection 
     return new SQLDataStore((SqlConnection)obj); 
    } 
} 

ответ

1

на основе this комментарий вам нужно один завод, который производит несколько типов IDataStore. Вы можете сделать это, создав открытый заводский метод в одном экземпляре фабрики singleton.

interface IDataStore<TStoreType> 
{ 
    void SetBaseType(TStoreType obj); 
    List<string> GetItems(); 
} 

interface IDataStoreFactory 
{ 
    IDataStore<TStoreType> Create<TStoreType>(TStoreType obj) 
} 

class DataStoreFactory : IDataStoreFactory 
{ 
    public IDataStore<TStoreType> Create<TStoreType>(TStoreType obj) 
    { 
     if (obj.GetType() == typeof(SqlConnection)) 
     { 
      var store = new SQLDataStore((SqlConnection)(Object)obj); 
      return (IDataStore<TStoreType>)store; 
     } 
     if (obj.GetType() == typeof(XmlDocument)) 
     { //... and so on } 
    } 
} 

class SQLDataStore : IDataStore<SqlConnection> 
{ 
    private readonly SqlConnection connection; 
    public SQLDataStore(SqlConnection connection) 
    { 
     this.connection = connection; 
    } 

    public List<string> GetItems() { return new List<string>(); } 
} 

Вы можете использовать этот завод, как это:

var factory = new DataStoreFactory(); 
var sqlDatastore = factory.Create(new SqlConnection()); 
var xmlDatastore = factory.Create(new XmlDocument()); 


Ваш хранилищу завод станет гораздо менее сложным, если вы будете использовать DI контейнер. Вы можете вставить контейнер на заводе и получить ваши экземпляры непосредственно из контейнера, который обычно создает ваши экземпляры снизу вверх, включая собственные зависимости, управление жизненным циклом и т. Д. Но будьте очень осторожны с этим подходом, это первый шаг к использованию шаблона локатора службы, который является anti pattern

+0

Я думаю, что мы (почти) прошли полный круг здесь, так как конкретная реализация заводского интерфейса эффективно отличает общий тип (переданный методу create) к определенному типу. Я думаю, что моя первоначальная забота заключалась в том, что «литье» (то есть в моем примере, бросающее объект на определенный тип) было бы неодобрительно. Я чувствую, что должен отмечать это как ответ из-за ваших усилий (и того факта, что это правильный ответ), однако, если в конечном итоге мы закончим кастинг, я могу придерживаться своего подхода, поскольку он соответствует тому, как я издеваюсь над объектами, когда единица тестирование. – Orinoco

+0

Я могу следить за вашими мыслями о полном круге. Но есть две большие различия. У вас теперь есть одна фабрика, а не фабрика по типу хранилища данных (фабрики в этом случае на самом деле являются фабричными методами в своем классе). Ваши отливки типа теперь находятся в одном месте, а именно на вашем заводе. Вся реализация хранилища данных проста и проста, и вы можете создавать их из любого места, используя конструкторы типа безопасного типа (в отличие от вашего предыдущего «объекта»). Если вы когда-либо используете контейнер DI, вы можете удалить отливки, и это изменение необходимо только в вашем заводском классе ==, совместимом с открытием/закрытием –

1

Не совсем уверен, если я правильно понимаю ваш вопрос, но мне это кажется немного странным, чтобы иметь заводские экземпляры, которые вы используете для создания ваших объектов statefull как вы их называете.

Чтобы ответить на ваш вопрос: generics - это ваше решение. Вы rinterface становится открытым родовое абстракция:

interface IDataStore<TStoreType> 
{ 
    List<string> GetItems(); 
} 

interface IDataStoreFactory<TStoreType> 
{ 
    IDataStore<TStoreType> Create(TStoreType obj); 
} 

и ваши классы фабрики будут выглядеть следующим образом:

class XmlDataStoreFactory : IDataStoreFactory<XmlDocument> 
{ 
    IDataStore<XmlDocument> Create(XmlDocument document) 
    { 
     return new XmlDataStore(document); 
    } 
} 

class SQLDataStoreFactory : IDataStoreFactory<SqlConnection> 
{ 
    IDataStore<SqlConnection> Create(SqlConnection connection) 
    { 
     return new SQLDataStore(connection); 
    } 
} 

Это будет работать, но из примеров, которые вы даете меня создалось впечатление, что вы с помощью заводов на всей вашей кодовой базе. Возможно, я ошибаюсь в этом вопросе, но посмотрите на свой дизайн и свести к минимуму количество фабрик. Необходимость фабрики означает смешивание данных с поведением, и это всегда, в конце концов, вызовет у вас проблемы.

Например, у вас есть какая-то услуга, которая добавляет текущего пользователя в журнал аудита при входе в систему. Этому отключению службы нужен текущий пользователь, который является типичным примером данных времени выполнения (или контекстных данных). Но вместо того, чтобы:

public class AuditLogService 
{ 
    public void AddApplicationSignIn(User user) 
    { 
     //... add user to some log 
    } 
} 

Я знаю, что это не очень хороший пример, потому что вы на самом деле не нужен завод для этого класса, но при следующем примере кода вы получите точку:

public class AuditLogService 
{ 
    private readonly IUserContext userContext;   

    public AuditLogService(IUserContext userContext) 
    { 
     this.userContext = userContext; 
    } 

    public void AddApplicationSignIn() 
    { 
     var user = this.userContext.GetCurrentUser(); 
     //... add user to some log 
    } 
} 

Таким образом, расщепляя данные от поведения, вы исключаете необходимость в фабриках. И признал, что есть случаи, когда завод является лучшим решением. Я действительно думаю, что IDataStore - это не то, для чего вам нужен завод.

Для хорошего блога по данным расщепления и поведение чтения here

+0

Это сообщение, вероятно, лучше всего объясняет сценарий того, почему я ввожу фабрики, а не сами объекты ... http: //stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-container/1945023#1945023 – Orinoco

+0

Хорошо, что ответ правильный, безрезультатный. Я получил (до сих пор) впечатление от ваших примеров, что неправильно использовал этот ответ, чтобы использовать фабрики на всей базе кода. См. Мое дополнение в ответе, почему это проблема и как вы можете это решить. –

+0

Ric, спасибо за ваши усилия, однако, к сожалению, я не думаю, что в этом случае решениями являются решения. Настраиваемые фабрики назначаются членами класса, в которые они вводятся, что, насколько я могу видеть в вас 're решение означает, что вам нужно будет знать TStoreType во время компиляции (т. е. при определении членов фабрики класса), чего у меня нет, если я не пропущу что-то в вашем ответе? Я не могу суммировать сложности того, над чем я работаю в должности SO, поэтому вам придется смириться с тем, что недели испытаний/исследований привели меня к инъекционным фабрикам. – Orinoco