C# шаблон разработки стратегии для разных типов возврата - PullRequest
5 голосов
/ 08 марта 2020

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

Минимальный пример.

Итак, мой интерфейс выглядит так:

public interface IParseStrategy
{
    object Parse(string filePath);
}

Классы, которые реализуют алгоритм:

class ParseA : IParseStrategy
{
    public object Parse(string filePath) => new ConcurrentQueue<ParseAData>();
}

class ParseB : IParseStrategy
{
    public object Parse(string filePath) => new Dictionary<string, ParseBData>();
}

Специфические c Классы "данных":

class ParseAData
{
    public int Id { get; set; }
    public string Name { get; set; }

}

class ParseBData
{
    private byte[] data;

    public byte[] GetData()
    {
        return data;
    }

    public void SetData(byte[] value)
    {
        data = value;
    }
}

Класс контекста, который определяет интерфейс Интересы для клиентов:

class Context
{
    private IParseStrategy _strategy;

    private void SetParsingStrategy(IParseStrategy parseStrategy)
    {
        _strategy = parseStrategy;
    }

    public object TryParse(string filePath, TypeToParse typeToParse)
    {
        object parsedContent = new object();

        try
        {
            switch (typeToParse)
            {
                case TypeToParse.A:
                    SetParsingStrategy(new ParseA());
                    parsedContent = _strategy.Parse(filePath);
                    break;
                case TypeToParse.B:
                    SetParsingStrategy(new ParseB());
                    parsedContent = _strategy.Parse(filePath);
                    break;
                    throw new ArgumentOutOfRangeException(nameof(typeToParse), "Uknown type to parse has been provided!");
            }
        }
        catch (Exception)
        {

            throw;
        }

        return parsedContent;
    }

}

Перечисление, в котором клиент может выбрать правильный алгоритм

public enum TypeToParse { A, B }

и, наконец, основной метод:

static void Main(string[] args)
{
    var context = new Context();
    ConcurrentQueue<ParseAData> contentOfA = (ConcurrentQueue<ParseAData>)context.TryParse("file1.whatever", TypeToParse.A);
    Dictionary<string, ParseBData>contentOfB = (Dictionary<string, ParseBData>)context.TryParse("file2.whatever", TypeToParse.B);
}

Итак, Моя проблема здесь заключается в том, что клиент должен знать классы, чтобы привести тип возвращаемого значения object.

. Как переписать это более обобщенным образом c, чтобы компилятор выводил типы автоматически с помощью ключевого слова var, поэтому вызов будет выглядеть следующим образом:

        var contentOfA = context.TryParse("file1.whatever", TypeToParse.A);
        var contentOfB = context.TryParse("file2.whatever", TypeToParse.B);

с выделенными желтыми типами:

enter image description here

Ответы [ 4 ]

2 голосов
/ 08 марта 2020

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

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

Итак, класс контекста

public object TryParse(string filePath, TypeToParse typeToParse)
{
    object parsedContent = new object();

    try
    {
        switch (typeToParse)
        {
            case TypeToParse.A:
                SetParsingStrategy(new ParseA());...
                break;
            case TypeToParse.B:
                SetParsingStrategy(new ParseB());...
                break;
        }
    }
    ....
}

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

public object TryParse(string filePath, TypeToParse typeToParse)
{
    object parsedContent = _this._stategy.Parse(filePath); //it should never know which implementation is supplied, in other words wich strategy is applied. Only at runtime t will be decided.
}

Задача 2 . Реализация стратегии двух классов имеет следующие реализации:

class ParseA : IParseStrategy
{
    public object Parse(string filePath) => new ConcurrentQueue<ParseAData>();
}

class ParseB : IParseStrategy
{
    public object Parse(string filePath) => new Dictionary<string, ParseBData>();
}

Что также нарушение. Почему ты спрашиваешь? Потому что код, вызывающий отдельные классы, должен знать, что они возвращают. Шаблон стратегии является шаблоном, ему все равно, поддерживает ли c# его или нет. object - это c# специфика c вкусностей, которые вы можете использовать для типизации любого объекта. Но это не значит, что использование объекта решает все. Даже если возвращаемые типы одинаковы (object), базовые фактические объекты не являются и, следовательно, это не может быть реализацией шаблона стратегии. Стратегии внедряются динамически, и поэтому никто не должен иметь жестко закодированных зависимостей от них. Одна из возможных реализаций может быть -

interface IData
{
}

class ParseAData : IData
{
    public int Id { get; set; }
    public string Name { get; set; }

}

class ParseBData : IData
{
    private byte[] data;

    public byte[] GetData()
    {
        return data;
    }

    public void SetData(byte[] value)
    {
        data = value;
    }
}

public interface IParsedObject 
{
    void process(<Some other dependencies>);
}

public class ConcurrentQueue<T> : IParsedObject where T: ParseAData
{

}

public class ParsedDictionary<T> : IParsedObject where T: ParseBData
{

}


public interface IParseStrategy
{
    IParsedObject Parse(string filePath);
}

//the method will be somesiliar to this 
public IParsedObject TryParse(string filePath, TypeToParse typeToParse)
{
    IParsedObject parsedContent = _this._stategy.Parse(filePath); //it should never know which implementation is supplied, in other words wich strategy is applied. Only at runtime t will be decided.
}

class ParseA : IParseStrategy
{
    public IParsedObject Parse(string filePath) => new ConcurrentQueue<ParseAData>();
}

class ParseB : IParseStrategy
{
    public IParsedObject Parse(string filePath) => new Dictionary<string, ParseBData>();
}

с этими модификациями, которые вы теперь можете написать -

static void Main(string[] args)
{
    var contextA = new Context();
    contentA.SetParsingStrategy(new ParseA());


    var contextB = new Context();
    contextB.SetParsingStrategy(new ParseB());

    var contentOfA = contextA.TryParse("file1.whatever", TypeToParse.A);
    var contentOfB = contextB.TryParse("file2.whatever", TypeToParse.B);
}

или

static void Main(string[] args)
{
    var context = new Context();
    contentA.SetParsingStrategy(new ParseA());
    var contentOfA = context.TryParse("file1.whatever", TypeToParse.A);

    context.SetParsingStrategy(new ParseB());
    var contentOfB = context.TryParse("file2.whatever", TypeToParse.B);
}

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

Ваш пример, вероятно, не подходит для паттерна стратегии. Я постарался исправить это как можно больше, чтобы вы лучше поняли, что не так в вашей реализации. Не все шаблоны поддерживают все сценарии ios. Вот пример реализации шаблона стратегии, который очень похож на ваш https://refactoring.guru/design-patterns/strategy/csharp/example.

Надеюсь, это поможет.

Примечание Код, который я предоставил, не работает. Они, вероятно, даже не скомпилируются, они просто существуют для 1057 * идеи, лежащей в основе стратегии. Правильная реализация будет иметь разные коды для каждого из классов ParseA и ParseB

Подробнее Паттерны стратегии и Io C, (Inversion of Cntrol) идут рука об руку. Попробуйте выучить Io C, и вы поймете, что шаблон стратегии намного легче выучить. https://en.wikipedia.org/wiki/Inversion_of_control

1 голос
/ 08 марта 2020

Простой ответ: вы не можете обойти эту проблему:

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

Что бы ни было дано пользователю при ответе, ему нужно знать тип объекта, который будет возвращен, это не будет известно до времени выполнения или, возможно, до этого пользователем класса контекста только со знанием внутренней реализации класса Context.

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

Если пользователи Context должны передать опцию функция TypeToParse пользователям контекстного класса лучше иметь 2 функции, которые возвращают правильный тип, например

class Context
{
    public ParseAData ParseAsA(string filePath)
    {
        ...
    }

    public ParseBData ParseAsB(string filePath)
    {
        ...
    }
}
0 голосов
/ 08 марта 2020

Основываясь на ответах здесь, я думаю, что мне удалось решить это. Итак, это реализация, которую я искал:

Фиксированный контекстный класс

class Context
    {
        private IParseStrategy _strategy;

        public void SetParsingStrategy(IParseStrategy parseStrategy)
        {
            _strategy = parseStrategy;
        }

        public T TryParse<T>(string filePath)
        {
            T parsedContent = (T)_strategy.Parse(filePath);

            return parsedContent;
        }

Main

        var context = new Context();

        // parse A first
        context.SetParsingStrategy(new ParseA());
        var contentOfA = context.TryParse<ConcurrentQueue<ParseAData>>("file1.whatever");

        // parse B second
        context.SetParsingStrategy(new ParseB());
        var contentOfB = context.TryParse<Dictionary<string, ParseBData>>("file2.whatever");

Теперь тип может быть выведен компилятором:

enter image description here

В дополнение к этому, я думаю, что я должен поставить некоторые противопоставления TryParse метод ...

0 голосов
/ 08 марта 2020

Здравствуйте, я думаю, вы неправильно понимаете шаблон стратегии. Подробнее о том, как решить вашу проблему в этой теме: https://social.msdn.microsoft.com/Forums/en-US/2bbef57c-4172-48a1-b683-faf779d6a415/strategy-pattern-with-specific-return-types?forum=architecturegeneral

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...