2016-06-14 2 views
2

Возьмем следующий пример: у меня есть несколько классов «Интеграция», которые наследуются от того же интерфейса «IChannelIntegration».Является ли это неправильным использованием интерфейсов?

public interface IChannelIntegration 
{ 

} 

public class IntegrationA : IChannelIntegration 
{ 

} 

public class IntegrationB : IChannelIntegration 
{ 

} 

public IChannelIntegration CreateIntegration(string letter) 
{ 
    if(letter = "A") 
    { 
    return new IntegrationA(); 
    } 
    else if (letter = "B") 
    { 
    return new IntegrationB(); 
    } 
    else 
    { 
    return null; 
    } 
} 

Одна из моих интеграций обеспечивает некоторую функциональность, которой нет в других интеграциях. Мне нужен способ вызова этой функции в объекте IChannelIntegration, возвращаемом функцией CreateIntegration.

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

public interface IIntegrationFeature {} 

public interface ICustomLogic : IIntegrationFeature 
{ 
    void DoSomething() {} 
} 

//Provides a concrete implementation of the logic. 
public class CustomLogic : ICustomLogic 
{ 
    public void DoSomething() {} 
} 

Затем был создан другой интерфейс, который будет подключаться к классам интеграции, чтобы поддерживать интеграцию с пользовательскими функциями.

public interface ISupportsFeature<T> where T : Interfaces.IIntegrationFeature 
{ 
    T GetFeature(); 
} 

Это будет реализовано как следует;

public class IntegrationC : IChannelIntegration, ISupportsFeature<ICustomLogic> 
{ 
    public ICustomLogic GetFeature() 
    { 
     return new CustomLogic(); 
    } 
} 

Так вот, как бы вы дали интеграцию, это конкретная логика, а затем вы бы назвали ее, используя следующее:

public static IIntegrationFeature GetIntegrationFeature<IIntegrationFeature>() 
     where IIntegrationFeature : Interfaces.IIntegrationFeature 
    { 
     IIntegrationFeature integrationFeature = null; 

     IChannelIntegration service = CreateIntegration(); 

     //Attempt to cast the integration too determine if it supports the Integration Feature. 
     ISupportsFeature<IIntegrationFeature> supportsFeature = service as ISupportsFeature<IIntegrationFeature>; 

     if(supportsFeature != null) 
     { 
      integrationFeature = supportsFeature.GetFeature(); 
     } 

     return integrationFeature; 
    } 

public void TestMethod() 
{ 
    ICustomLogic customLogic = GetIntegrationFeature<ICustomLogic>(); 
    if (customLogic != null) 
    { 
     customLogic.DoSomething(); 
    } 
} 

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

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

+4

Я голосующий, чтобы закрыть этот вопрос как не по теме, потому что OP просит просмотреть код. Код работает, нет конкретной проблемы для решения. – InBetween

+0

Мои извинения, я не знал, что есть раздел обзора кода. – Eritey

+1

@Eritey: см: [codereview.stackexchange.com] (http://codereview.stackexchange.com) – JanDotNet

ответ

2

Я бы начал с начала до конца, от вашей потребности до реализации.

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

interface IIntegrationFeature {} 

и что-то, что бы предоставить Вам эту возможность

interface IIntegratedSystem 
{ 
    T GetFeature<T>(string integrationType) where T : IIntegrationFeature; 
} 

клиент этой установки будет использовать актеров, как этот

IIntegratedSystem system = .... // system implementation 

T feature = system.GetFeature<T>("Which integration type?"); 

if (feature != null) 
{ 
    //the feature exists and I'm using it 
} 

IIntegratedSystem реализация языка ответственность заключается в предоставлении IIntegrationFeature от существующего IChannelIntegration, выбранного или созданного в режиме реального времени. Вы считали, что информируете любого клиента о том, что реализация IChannelIntegration реализовала функцию путем реализации ISupportFeature<IIntegrationFeature>. Что происходит, когда интеграция имеет около 20 функций? У вас будет класс, который реализует интерфейс интеграции сам по себе и еще 20 ISupports интерфейсов. Как вы добавляете еще одну функцию? Вы модифицируете класс, добавив новый ISupports<INewFeature>, нарушающий принцип единой ответственности. И во время компиляции вам не нужно знать, что интеграция выполняет определенную функцию, поскольку клиент может ее использовать или не использовать.В основном IChannelIntegration больше походит на композитной интеграций, которые должны быть в состоянии дать IIntegrationFeature и, таким образом IChannelIntegration будет иметь определение, аналогичное IIntegratedSystem

interface IChannelIntegration 
{ 
    T GetFeature<T>() where T : IIntegrationFeature; 
} 

способ, как вы строите реализацию IChannelIntegration зависит от вас, но я предпочитают создавать функции в корне вашего приложения. Mine будет таким:

class ChannelIntegration : IChannelIntegration 
{ 
    private Dictionary<Type, IIntegrationFeature> features; 
    public ChannelIntegration() 
    { 
     features = new Dictionary<Type, IIntegrationFeature>(); 
    } 
    public void RegisterFeature<T>(IIntegrationFeature feature) where T:IIntegrationFeature 
    { 
     features.Add(typeof(T), feature); 
    } 
    public T GetFeature<T>() where T : IIntegrationFeature 
    { 
     IIntegrationFeature feature = features.TryGetValue(typeof(T), out feature) ? feature : null; 
     return (T)feature; 
    } 
} 

Класс ChannelIntegration содержит список функций. Вы используете RegisterFeature, чтобы добавить дополнительные функции, и, чтобы убедиться, что это функция, вы ограничиваете их реализацию интерфейса IIntegrationFeature. Поэтому, когда вы добавляете новую функцию, вы не меняете класс ChannelIntegration, вы используете его.

IIntegratedSystem получит доступ к функции от IChannelIntegration на основе значений времени выполнения. Это просит, как вы сказали, использовать Abstract Factory. Это будет выглядеть

interface IChannelIntegrationFactory 
{ 
    IChannelIntegration CreateIntegration(string integrationType); 
} 

IIntegratedSystem будет использовать этот завод, чтобы создать IChannelIntegration и для достижения этой цели я использовал бы механизм впрыска конструктора.

class IntegratedSystem : IIntegratedSystem 
{ 
    private IChannelIntegrationFactory integrationFactory; 
    public IntegratedSystem(IChannelIntegrationFactory integrationFactory) 
    { 
     this.integrationFactory = integrationFactory; 
    } 
    public T GetFeature<T>(string integrationType) where T: IIntegrationFeature 
    { 
     T integrationFeature = default(T); 
     IChannelIntegration integration = integrationFactory.CreateIntegration(integrationType); 
     if (integration != null) 
     { 
      integrationFeature = (T)integration.GetFeature<T>();  
     } 
     return integrationFeature; 
    } 
} 

IntegratedSystem Теперь класс использует IChannelIntegrationFactory для создания IChannelIntegration на основе значения времени выполнения, а затем он будет извлекать из него реализованный IIntegrationFeature, если таковые имеются.

Что нам теперь нужно реализовать этот завод, который будет создавать IChannelIntegration с с их IIntegrationFeature s

Пусть первым создадим новую функцию

interface ICustomFeature : IIntegrationFeature {} 

class CustomFeature : ICustomFeature 
{  
} 

и на основе этой реализации Abstract Factory, как

class ChannelIntegrationFactory : IChannelIntegrationFactory 
{ 
    public IChannelIntegration CreateIntegration(string integrationType) 
    { 
     // use integrationType to decide witch IChannelIntegration to use 
     IChannelIntegration integration = new ChannelIntegration(); 
     integration.RegisterFeature<ICustomFeature>(new CustomFeature()); 

     return integration; 
    } 
} 

Мы создали здесь ChannelIntegration с его единственной функцией. Конечно, у вас может быть несколько путей, как вы начали, где вы создаете разные IChannelIntegrations со своими ICustomFeature s и извлекаете один из них на основе параметра integrationType.

Итак, как вы это потребляете?

IIntegratedSystem system = new IntegratedSystem(new ChannelIntegrationFactory()); 
    ICustomFeature customFeature = system.GetFeature<ICustomFeature>("Which Integration?"); 
    if (customFeature != null) 
    { 
     //use this custom feature 

    } 
    else 
    { 
     // that's OK, will wait until is registered. 
    } 

Вы хотите добавить новую функцию? Зарегистрируйте его в реализации абстрактной фабрики.

So would you expect to create a new abstract factory of type ChannelIntegrationFactory for each different type of ChannelIntegration?

No. Фабрика выбирает один из из списка конкретных реализаций.Вот один пример

class ChannelIntegrationFactory : IChannelIntegrationFactory 
{ 
    public IChannelIntegration CreateIntegration(string integrationType) 
    { 
     IChannelIntegration integration = null; 
     switch (integrationType) 
     { 
      case "MobileIntegration": 
       integration = new ChannelIntegration(); 
       integration.Register<ITapGestureTrack>(new TapGestureTrack()); 
       break; 
      case "DesktopIntegration": 
       integration = new ChannelIntegration(); 
       integration.Register<IClickTrack>(new ClickTracker()); 
       integration.Register<ILargeImagesProvider>(new LargeImagesProvider()); 
       break; 
     } 
     return integration; 
    } 
} 

Вы можете даже сделать ChannelIntegration как абстрактные и создавать специализированные объединения:

class MobileIntegration : ChannelIntegration 
    { 
     public MobileIntegration() 
     { 
      Register<ITapGestureTrack>(new TapGestureTrack()); 
     } 
    } 

    class DesktopIntegration : ChannelIntegration 
    { 
     public DesktopIntegration() 
     { 
      Register<IClickTrack>(new ClickTracker()); 
      Register<ILargeImagesProvider>(new LargeImagesProvider()); 
     } 
    } 

и абстрактная фабрика становится

class ChannelIntegrationFactory : IChannelIntegrationFactory 
    { 
     public IChannelIntegration CreateIntegration(string integrationType) 
     { 
      IChannelIntegration integration = null; 
      switch (integrationType) 
      { 
       case "MobileIntegration": 
        integration = new MobileIntegration(); 
        break; 
       case "DesktopIntegration": 
        integration = new DesktopIntegration(); 
        break; 
      } 
      return integration; 
     } 
    } 

Просто увидеть этот класс как ваша систему загрузочный класс. Это единственный класс, который создает интеграцию с функциями. Кстати, именно так вы можете легко включить инверсию зависимостей, используя ваш предпочтительный IoC. Fiddle

+0

Я подумал о том, чтобы сделать что-то подобное, но не смог найти хороший способ сообщить ChannelIntegration, какие функции он поддерживает. Итак, вы ожидаете создать новую абстрактную фабрику типа ChannelIntegrationFactory для каждого другого типа ChannelIntegration? Вы, к примеру, были очень ясны и продуманы, спасибо вам большое. – Eritey

+1

@Eritey, см. Мое обновление –

+0

Я думаю, что я предпочитаю второе решение, в котором функции добавлены в конструктор. У нас есть более 15 интеграций, поэтому управление ими в одном классе может стать довольно беспорядочным. Что означает IoC? Я не видел этого раньше. – Eritey

Смежные вопросы