Есть ли название для этого шаблона

Я использовал этот шаблон много раз в самых разных местах, обычно вместе с шаблоном плагина.

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

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

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

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

Суть в том, что у вас может быть несколько классов с разными сигнатурами методов, которые по-прежнему можно вызывать через общий интерфейс:

Конечный результат

public class ConcreteA : Base<MessageA>
{
    public void Process(MessageA message){...}
    public MessageA Parse(IDictionary data){...}
}

public class ConcreteB : Base<MessageB>
{
    public void Process(MessageB message){...}
    public MessageB Parse(IDictionary data){...}
}

//And both can by called by...
public void Main(){
    var data = GetDataFromIntegrationSource(someContext);
    IWorkflow impl = Factory.GetConcrete(someContext);

    //So in your classes you're able to work with strongly typed parameters,
    //But in the consuming code you still can use a common interface
    //Consuming code never even knows what the strong type is. 
    IMessage msg = impl.Parse(data);
    impl.Process(msg);
}

ПОЛНЫЙ ПРИМЕР

Интерфейсы высокого уровня

public interface IGenericeMarkerInterface
{
}

public interface IGenericWorkflow
{
    void Process(IGenericeMarkerInterface messageOrContext);

    IGenericeMarkerInterface Parse(IDictionary<string, string> commonDataFormat);
}

Абстрактная основа для сопоставления с конкретными методами

public abstract class GenericWorkflowBase<T> : IGenericWorkflow where T : IGenericeMarkerInterface
{
    public void Process(IGenericeMarkerInterface messageOrContext)
    {
        Process((T)messageOrContext);      
    }

    public IGenericeMarkerInterface Parse(IDictionary<string, string> commonDataFormat)
    {
        return DoParse(commonDataFormat);
    }

    public abstract void Process(T messageOrContext);
    public abstract T DoParse(IDictionary<string, string> commonDataFormat);
}

Сопоставление атрибутов

public class MappingAttributeUsedByFactoryAttribute : Attribute
{
    public WorkflowType SomePropertyForMapping { get; set; }
}

Конкретные реализации

public class SomeRandomlyShapedMessageOrContext : IGenericeMarkerInterface
{
    public int ID { get; set; }
    public string Data { get; set; }
}

[MappingAttributeUsedByFactory(WorkflowType.IntegrationPartnerB)]
public class ConcreteWorkflow : GenericWorkflowBase<SomeRandomlyShapedMessageOrContext>
{
    public override void Process(SomeRandomlyShapedMessageOrContext messageOrContext)
    {
        //TODO: process the strongly typed message
    }

    public override SomeRandomlyShapedMessageOrContext DoParse(IDictionary<string, string> commonDataFormat)
    {
        //TODO: parse the common data into the strongly typed message            
    }
}

Фабрика

public static class WorkflowFactory
{
    public static IGenericWorkflow Get(WorkflowType workflow) 
    {
        //TODO: find the concrete workflow by inspecting attributes
    }
}

Пример использования

public static class Program
{
    public static void Main(string[] args)
    {
        //this could be driven by a UI or some contextual data
        var someSortOfWorkflowIdentifier = (WorkflowType)args[0];
        var data = GetSomeDictionaryOfData();

        var workflow = WorkflowFactory.Get(someSortOfWorkflowIdentifier);

        workflow.Process(workflow.Parse(data));
    }
}

person Michael    schedule 15.02.2014    source источник
comment
C# поддерживает атрибуты.   -  person Hans Passant    schedule 15.02.2014
comment
@HansPassant Как и Java.   -  person Cubic    schedule 15.02.2014
comment
Я бы назвал этот ужасный дизайн запахом шаблона. Вместо правильного ООП вы что-то взламываете с помощью дженериков.   -  person Euphoric    schedule 15.02.2014
comment
@Euphoric Что бы вы сделали по-другому?   -  person Michael    schedule 15.02.2014
comment
@HansPassant, если метаданные необходимо изменить во время выполнения, интерфейс отлично подходит. особенно если часть метаданных необходимо локализовать   -  person Gusdor    schedule 15.02.2014
comment
@HansPassant На самом деле я часто использую это с шаблоном плагина, используя атрибуты конкретной реализации, которую затем использует фабрика. Я отредактировал, чтобы показать вам пример атрибута и то, как фабрика может его использовать.   -  person Michael    schedule 15.02.2014
comment
@Euphoric почему это плохо? Я нашел этот шаблон полезным в некоторых случаях, и он имеет для меня некоторый смысл.   -  person BlackBear    schedule 16.02.2014


Ответы (1)


Да, это именно то, что вы назвали: интерфейс маркера

person Ufuk Hacıoğulları    schedule 15.02.2014
comment
Все остальные согласны с этим? Прежде чем принять, я хочу удостовериться. Я знаю, что пустой интерфейс, который я использую в качестве критерия для универсального аргумента, является интерфейсом-маркером, но похоже, что здесь есть нечто большее, чем шаблон. Например, использование абстрактной универсальной базы для сопоставления интерфейсов маркеров с конкретными типами, а затем возможность создания конкретных реализаций с методами, которые принимают различные типы параметров, но при этом могут вызываться через единый общий интерфейс. - person Michael; 16.02.2014
comment
Другое дело, что иногда это даже не интерфейс маркера, иногда у вас есть базовый класс Message или Context с общими полями, но остальная часть шаблона остается идентичной с точки зрения настройки независимого от типа интерфейса, а затем общей базы под ним. - person Michael; 16.02.2014