4

テキストファイルから読み取った「レシピ」を実行し、行ごとに解析して、一連のメソッドを動的に呼び出そうとしています。かなりのグーグルを行った後、ファクトリを実装する必要があると思いますが、いくつかの重要な詳細が不足しています。これは私が持っている最も近い例です:

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

次のコードは、現在のスニペットです。

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

        //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<string, Func<IMove>> moveMap = new Dictionary<string, Func<IMove>>()
    {
        {"Up", () => { return new UpMove(); }},
        {"Down", () => { return new DownMove(); }},
        {"Left", () => { return new LeftMove(); }}
        // ...
    };

    public static IMove CreateMoveFromName(string name)
    {
        return moveMap[name]();
    }
}
  1. 辞書リストを自動的に生成できますか?そのため、ファクトリインターフェイス(IMoveに相当)を実装する新しいクラスを追加するたびに、辞書やコードの他の部分を更新する必要はありません。おそらく、これはインターフェイスの一部として強制される可能性がありますか?

  2. 上記のサンプルコードでは、引数を出し入れしているのがわかりません。コードを見ると、段階的に変更する必要のあるデータがあります...ファクトリを使用してこれを行うにはどうすればよいですか。

  3. それぞれが独自のレシピを実行している複数のワーカーに異なる初期データを渡したいので、ファクトリはスレッドセーフである必要があります。

4

1 に答える 1

7

これらに一度に取り組みましょう。

辞書を動的に構築する

これは、実際には、リフレクションカスタム属性の組み合わせを使用して行うのは非常に簡単です。

MoveNameAttribute属性の作成は非常に簡単なので、調べるのはあなたに任せますが、クラスレベルで適用できる呼び出された属性があると仮定しましょう。IMove次に、次のように実装するクラスを装飾できます。

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

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

これで、Reflectionと小さなLINQを使用して、これらのクラスタイプをディクショナリに抽出し、カスタム属性で指定されたキーを使用して、オンデマンドでこれらのタイプの新しいインスタンスを作成できます。

Factory全体はコード行の点でかなり短いですが、Reflectionは、これまでに行ったことがない場合は気が遠くなる可能性があります。何が起こっているのかを説明するために、すべての行に注釈を付けました。

internal static class MoveFactory
{
    private static readonly IDictionary<String, Type> _moveTypes;

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

    private static IDictionary<string, Type> 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<MoveNameAttribute>()
            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");

ファクトリは静的コンストラクタを使用するため、このリストに1回だけ入力され、新しいクラスが自動的に取得されます。

引数の受け渡し

ここでのユースケースが100%わからないのですが、ファクトリに引数を渡す必要があるようには見えませんIMove。ただし、渡すことができる引数の数は可変です。

これが事実である場合、あなたは単にあなたのデザインに少し醜さを持って生きなければならないでしょう。IMoveインターフェイスには非常に一般的なメソッドが必要です。

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

これで、個々の移動クラスは勤勉になり、適切な数のパラメーターを取得することを確認する必要があります。これは演習として残しておきますが、上記の例に基づいて必要なものが得られるはずです。

スレッドセーフ

現状では、上記のファクトリ実装は共有状態に依存しないためスレッドセーフであり、基盤となるディクショナリは基本的に不変です。を呼び出すたびCreateMoveに、新しいIMoveインスタンスが返されます。

IMoveの実装がスレッドセーフであるかどうかはあなた次第です:)

ふぅ!それは長い答えでしたが、うまくいけば、これがあなたを助けるでしょう。

于 2012-12-21T05:43:20.897 に答える