2015-09-22 3 views
2

Ниже приведены мои классы фабрики по импорту. Если я хочу добавить новые ImportTypes, просто добавьте новый тип в перечисления ImportTypes и добавьте случай в заводский класс, и он отлично работает.Расширяемый шаблон дизайна завода

Мой вопрос в том, как я могу сделать его более независимым и простым в расширении. Допустим, пользователь хочет добавить новый ImportType, вместо того, чтобы менять код, он пишет свою собственную DLL и реализует интерфейс ... любые хорошие предложения/идеи?

типы Импорт

enum ImportTypes 
{ 
    DefaultImport, 
    C2CImport 
} 

Импорт интерфейс

public interface IImportService 
{ 
    void Import(Argument arguments, ImportDefinition config); 
} 

Импорт завод

class ImportFactory 
    { 
    public static IImportService GetService(ImportTypes type) 
    { 
     switch (type) 
     { 
      case ImportTypes.DefaultImport: 
       return new DefaultImportService(); 

      case ImportTypes.C2CImport: 
       return new C2CImportService(); 
     } 

     return null; 
    } 
} 

Тест

IImportService docImportService = ImportFactory.GetService(ImportTypes.DefaultImport); 

ответ

1

Для достижения этой цели необходимо поддерживать регистрацию новых услуг во время выполнения. Таким образом, вы не можете использовать enum, так как услуги не известны заранее; они должны ссылаться, например, через строку:

class ImportServiceHandler 
{ 
    private readonly Dictionary<string, Type> _importServices = 
     new Dictionary<string, Type>(); 

    public ImportServiceHandler() 
    { 
     RegisterService("DefaultImport", typeof(DefaultImportService)); 
     RegisterService("C2CImport", typeof(C2CImportService)); 
    } 

    public void RegisterService(string name, Type serviceType) 
    { 
     if (!serviceType.IsAssignableFrom(typeof(IImportService))) 
     { 
      throw new ArgumentException("Type specified doesn't implement IImportService", nameof(serviceType)); 
     } 
     _importServices.Add(name, serviceType); 
    } 

    public IImportService GetService(string name) 
    { 
     if (_importServices.ContainsKey(name)) 
     { 
      return (IImportService)Activator.CreateInstance(_importServices[name]); 
     } 
     return null; 
    } 
} 
0

Вот один из способов ...

В коммутатору заявлении, «по умолчанию» будет обрабатывать любые неопознанные типы. Тип будет рассмотрен в конфигурации вашей программы. Пользователь должен будет добавить свой тип и путь к сборке, которая реализует этот тип. Им нужно будет реализовать свой интерфейс. Случай по умолчанию будет загружать сборку и создавать экземпляр объекта и возвращать его вам.

Чтобы загрузить сборку, вы должны использовать AppDomain.CurrentDomain.Load(). Чтобы создать экземпляр объекта, вы должны использовать {Assembly} .CreateInstance().

class ImportFactory 
{ 
    public static IImportService GetService(ImportTypes type) 
    { 
    switch (type) 
    { 
     case ImportTypes.DefaultImport: 
      return new DefaultImportService(); 
     case ImportTypes.C2CImport: 
      return new C2CImportService(); 
     default: 
      return CustomImportService(type); 
    } 
    return null; 
    } 
} 

CustomImportService будет выглядеть примерно так ...

IImportService CustomImportService(ImportTypes type) 
{ 
    var assmBytes = GetAssemblyIDByType(type); // you would implement this based on your configuration approach 
    var assm = AppDomain.CurrentDomain.Load(assmBytes); 
    var obj = (IImportService)assm.CreateInstance("IImportService"); 
    return obj; 
} 

Это только начало, конечно, и нужно работать.

0

Вы можете использовать образец прототипа.

Создайте класс, который получает типы импорта во время выполнения и возвращает клоны этих объектов.
Он позволит вам загружать ImportTypes во время выполнения, и заводский класс не нуждается в изменении.

http://www.dofactory.com/net/prototype-design-pattern

Эта ссылка показывает идею.

0

Вы можете иметь что-то вроде этого: (обработка ошибок опущена для ясности)

public class Factory { 

    //store how different objects are created 
    private IDictionary<string, Func<IImportService>> mappings = new Dictionary<string, Func<IImportService>>() 

    public void Register(string key, Func<IImportService> expression) 
    { 
     mappings[key] = expression; 
    } 

    public IImportService GetService(string key) 
    { 
     return mappings[key](); 
    } 
} 

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

factory.Register("service",() => new MyCustomImportService()); 

Если вы знаете тип вы хотите во время компиляции вы можете предоставить переопределения, которые принимают тип в подписи. т.е. Register<MyCustomImportService>(...) и GetService<MyCustomImportService>. На данный момент, вероятно, было бы полезно рассмотреть, хотите ли вы какой-то легкий контейнер IoC

0

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

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

Такой дизайн приложений действительно требует подхода, основанного на зависимости на основе инъекций. Что-то вроде MEF, может быть? Вы можете быть в состоянии уйти от определения зависимостей, как это:

[Export("C2CImport", typeof(IImportService))] 
public class C2CImportService : IImportService 

В дополнение к [Import] -ную зависимость в код вы хотите, вы можете вручную решить эту зависимость так:

myCompositionContainer.GetExports<IImportService>("C2CImport"); 

(https://msdn.microsoft.com/en-us/library/dd833299(v=vs.110).aspx)