Вопрос по refactoring, c#, dictionary, factory, switch-statement – Как сделать рефакторинг Переключиться в Словарь / Фабрика

4

Я пытаюсь запуститьРецепт' читать из текстового файла и анализировать построчно для динамического вызова ряда методов. Я думаю, что мне нужно реализовать Factory после того, как я немного погуглил, но мне не хватает некоторых ключевых деталей. Это ближайший пример, который у меня есть:

http://simpleprogrammer.com/2010/08/17/pulling-out-the-switch-its-time-for-a-whooping/

Следующий код является фрагментом того, что есть сейчас.

    internal static void Run(int Thread_ID, List InstructionSet, List[] Waveforms)
    {
        //Init
        List[] Register = new List[10];
        for (int i = 0; i < Waveforms.Length; i++) { Register[i] = new List(Waveforms[i]); }
        for (int i = 0; i < Register.Length; i++) { if (Register[i] == null) { Register[i] = new List(); } }

        //Run Recipe Steps
        foreach (var item in InstructionSet)
        {
            Step Op = Step.Parse(item.ToString());
            switch (Op.TaskName)
            {
                case "SimpleMovingAverage":
                    Register[Convert.ToInt32(Op.Args[0])] = Signal_Filters.SimpleMovingAverage(Register[Convert.ToInt32(Op.Args[1])], Convert.ToInt32(Op.Args[2]));
                    break;

                case "RollingSteppedStdDeviation":
                    Register[Convert.ToInt32(Op.Args[0])] = Signal_Filters.RollingSteppedStdDeviation(Register[Convert.ToInt32(Op.Args[1])], Convert.ToInt32(Op.Args[2]), Convert.ToInt32(Op.Args[3]));
                    break;

               //... etc. many, many methods to be called.
            }
        }
    }

... и ниже часть примера, о котором у меня есть вопросы:

public static class MoveFactory
{
    private static Dictionary moveMap = new Dictionary()
    {
        {"Up", () => { return new UpMove(); }},
        {"Down", () => { return new DownMove(); }},
        {"Left", () => { return new LeftMove(); }}
        // ...
    };

    public static IMove CreateMoveFromName(string name)
    {
        return moveMap[name]();
    }
}

Могу ли я автоматически создать список словаря? Так что всякий раз, когда я добавляю новый класс, который реализует мой Factory Interface (мой эквивалент IMove), я нене нужно обновлять мой словарь или почти любую другую часть моего кода. Возможно, это может быть вызвано как часть интерфейса?

В приведенном выше примере кода я нене вижу передачи аргументов внутрь и наружу. Глядя на мой код, у меня есть данные, которые мне нужно мутировать постепенно ... Как бы я это сделал, используя Factory.

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

Что еще более важно, пожалуйста, нетрать наше время, публикуя такие огромные вопросы. Сузить! ЧитатьКак спросить John Saunders
Я сократил объем вопроса, спасибо за отзыв. HodlDwon
В отличие от форумов, мы нет использовать "Спасибо", или же "Любая помощь приветствуетсяили подписи наПереполнение стека, Увидеть "Должен 'Привет', 'Спасибо,' слоганы, а приветствия будут удалены из постов? John Saunders

Ваш Ответ

1   ответ
6

Позволять'Заниматься этим по одному.

Динамическое построение словаря

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

Создание атрибута довольно тривиально, поэтому яоставлю это тебе, но давайПредположим, у вас есть один называетсяMoveNameAttribute это может быть применено на уровне класса. Затем вы можете украсить свои классы, которые реализуютIMove вот так:

[MoveName("Up")]
class UpMove: IMove{}

[MoveName("Down")]
class DownMove: IMove{}

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

Хотя сама Фабрика довольно коротка с точки зрения строк кода, Reflection может быть пугающим, если вы никогда не делали этого раньше. Я'Мы аннотировали каждую строку, чтобы объяснить, что происходит.

internal static class MoveFactory
{
    private static readonly IDictionary _moveTypes;

    static MoveFactory()
    {
        _moveTypes = LoadAllMoveTypes();
    }

    private static IDictionary LoadAllMoveTypes()
    {
        var asm =
            //Get all types in the current assembly
            from type in Assembly.GetExecutingAssembly().GetTypes()
            //Where the type is a class and implements "IMove"
            where type.IsClass && type.GetInterface("IMove") != null
            //Only select types that are decorated with our custom attribute
            let attr = type.GetCustomAttribute()
            where attr != null
            //Return both the Name and the System.Type
            select new
                        {
                            name = attr.Name,
                            type
                        };

        //Convert the results to a Dictionary with the Name as a key
        // and the Type as the value
        return asm.ToDictionary(move => move.name, move => move.type);
    }

    internal static IMove CreateMove(String name)
    {
        Type moveType;

        //Check to see if we have an IMove with the specific key
        if(_moveTypes.TryGetValue(name, out moveType))
        {
            //Use reflection to create a new instance of that IMove
            return (IMove) Activator.CreateInstance(moveType);
        }

        throw new ArgumentException(
           String.Format("Unable to locate move named: {0}", name));
    }
}

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

var upMove = MoveFactory.CreateMove("Up");
var downMove = MoveFactory.CreateMove("Down");

Поскольку фабрика используетСтатический конструктор, он заполнит этот список только один раз и автоматически подберет новые классы.

Передача аргументов

Я не уверен на 100%, какой у вас вариант использования, но это не так.Похоже, вам нужно передать аргументы в вашу фабрику, а не какой-то метод на вашемIMove, Однако у вас есть переменное число аргументов, которые можно передать.

Если это так, то вам просто придется жить с небольшим количеством уродства в вашем дизайне. Вам нужен очень общий метод на вашемIMove интерфейс:

public interface IMove
{
   double Compute(double val1, params int[] args);
}

Теперь ваши индивидуальные классы движения должны быть просто прилежными и проверять, чтобы убедиться, что они получают правильное количество параметров. Я'Я оставлю это как упражнение для вас, но это должно дать вам то, что вам нужно, основываясь на примере выше.

Поток безопасности

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

Теперь или нет ваши реализацииIMove нитки безопасны до вас :)

Уф! Это был длинный ответ, но, надеюсь, это поможет вам.

Круто, большое спасибо! Это должно охватывать все, что мне нужно. HodlDwon

Похожие вопросы