2013-01-03 18 views
0

我在通用格式的文本文件:讀配方來控制數據處理步驟

ThreadID, MethodName, [Param1 | Param2 | ... ] 

而且我有一個看起來像一個工具類:

public static class Util 
{ 
    public static List<double> Foo(List<double> Source, double Scale) 
    { 
     List<double> l = new List<double>(Source); 
     for (int i = 0; i < l.Count; i++) 
     { 
      l[i] = l[i] * Scale; 
     } 
     return l; 
    } 

    public static void Fud(List<double> Source, string Name) 
    { 
     //... Chart Series 
    } 
    public static bool Fee(CustomClass MyClass, double Limit1, int Limit2) 
    { 
     //... Test MyClass values against input limits 
     return true; // false; 
    } 
} 

所以我使用的是開關/案例從文本文件中給出方法名稱,然後解析並將參數傳遞給案例中的方法。須藤代碼爲東西的效果:

static void Main(string[] args) 
    { 
     List<string> TextFile; 
     List<double>[] Register = new List<double>[3]; //lists containing data that is moved, mutated and converted to CustomClass 
     CustomClass MyClass = new CustomClass(); 
     bool Passed = false; 

     foreach (var line in TextFile) 
     { 
      string methodName = Util.ParseMethod(line);   //parsed from file 
      string param1, param2, param3, param4 = Util.ParseParams(line);   //parsed from file 
      switch (methodName) 
      { 
       case "Foo": 
        Register[param1] = Util.Foo(Register[param2], param3); 
        break; 

       case "Fud": 
        Util.Fud(Register[param1], param3); 
        break; 

       case "Fee": 
        Passed = Util.Foo(MyClass, param1, param2); 
        break; 
      } 
     } 
    } 

我不喜歡有邏輯分手我每次開發新的工具方法的時候,我必須手動添加其他case語句。 case語句變得難以維護,因爲設計時錯誤(字符串/更新中的拼寫錯誤)通常只在運行時被捕獲。

我試圖將這一切轉換爲工廠架構,但我似乎無法適應不同的方法定義。我希望取消Utility類並使用工廠界面在其自己的類中定義每個方法。

public interface IRecipe 
{ 
    string GetFactoryKey(); 
    string SerializeArgs(object[] args); 
    object[] DeserializeArgs(string args); 
    ??? DoWork(???); 
} 

的問題是,因爲如何不同每種方法在實用類是,我不能找到一種方法,在適當的接口定義它。我想要的最終結果是我可以用泛型代碼替換foreach循環中的開關,並且在實現工廠接口的每個類中定義所有基本邏輯,解析,方法定義,錯誤檢查和驗證,而不是方法調用方。我已經用盡了谷歌的東西,因爲我不知道我想要做什麼甚至叫。

回答

0

我想我想通了。 ..這是迄今爲止我完全理解的概念證明。仍然有點醜陋,但我至少在每個工廠類中都包含了醜陋。這意味着Main中的調用者可以保持相當通用,便於在循環中使用。隨着我真正的實現,我會更加重構這一點。

using System; 
using System.Collections.Generic; 
using System.Collections.Specialized; 
using System.Linq; 
using System.Text; 
using System.Reflection; 
using System.Threading; 

namespace ConsoleApplication3 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<double>[] Reg = new List<double>[2]; for (int i = 0; i < Reg.Length; i++) { Reg[i] = new List<double>(); } 

      for (int i = 0; i < 100; i++) 
      { Reg[1].Add(1.0); }   //Dummy data 

      var obj = ClassFactory.CreateStep("AmplifySignal"); 
      object[] Args = obj.DeserializeProperties(" "); 
      Args[0] = Reg; 
      dynamic result = obj.Run(Args); 

      if (result != null) 
      { Thread.Sleep(0); } //breakpoint 

      Console.ReadKey(); 
     } 
    } 

    internal static class ClassFactory 
    { 
     private static readonly IDictionary<String, Type> _recipe_instructions; 

     static ClassFactory() { _recipe_instructions = MapIFactory(); } 

     private static IDictionary<string, Type> MapIFactory() 
     { 
      // Get the assembly that contains this code 
      Assembly asm = Assembly.GetExecutingAssembly(); 

      // Get a list of all the types in the assembly 
      Type[] allTypes = asm.GetTypes(); 

      var d = new Dictionary<string, Type>(); 
      foreach (var type in allTypes) 
      { 
       if (type.IsClass && !type.IsAbstract && type.GetInterface(typeof(IFactory).ToString()) != null) 
       { 
        // Create a temporary instance of that class... 
        object inst = asm.CreateInstance(type.FullName, true, BindingFlags.CreateInstance, null, null, null, null); 
        if (inst != null) 
        { 
         // And generate the product classes key 
         IFactory mapKey = (IFactory)inst; 
         object key = mapKey.GetFactoryKey(); 
         inst = null; 

         //Add the results to a Dictionary with the Name as a key and the Type as the value 
         d.Add((string)key, type); 
        } 
       } 
      } 
      return d; 
     } 

     internal static IFactory CreateStep(String key) 
     { 
      Type type; 
      if (_recipe_instructions.TryGetValue(key, out type)) 
      { return (IFactory)Activator.CreateInstance(type); } 
      throw new ArgumentException(String.Format("Unable to locate key: {0}", key)); 
     } 
    } 

    public interface IFactory 
    { 
     string GetFactoryKey(); 
     string SerializeProperties(); 
     object[] DeserializeProperties(string args); 
     dynamic Run(params object[] args); 
    } 

    public class AmplifySignal : IFactory 
    { 
     public string GetFactoryKey() 
     { return MethodBase.GetCurrentMethod().DeclaringType.Name; } 
     public object[] DeserializeProperties(string args) 
     { return new object[] { null, (int)1, (double)2.1 }; } 
     public string SerializeProperties() 
     { return " 1 | 2.1 "; } 

     public dynamic Run(params object[] args) 
     { 
      // Build Inputs 
      List<double>[] registers = (List<double>[])args[0]; 
      List<double> waveform = (List<double>)registers[(int)args[1]]; 
      double factor = (double)args[2]; 

      // Do Work 
      double[] Result = waveform.ToArray();  //Localize and Deep Copy 
      for (int i = 0; i < Result.Length; i++) 
      { Result[i] *= factor; } 
      List<double> result = Result.ToList(); //breakpoint 

      // Write Result 
      return result; 
     } 
    } 
} 

如果我做了什麼真正的褻瀆神明在這裏,而且有更好的方式來完成同樣的事情,然後通過各種手段,我開放的建議。在接受這個答案之前,我會等上一兩天,以便得到任何反饋。

0

如果DoWork方法使用單個字符串參數(逗號分隔的參數)並根據需要分割它會怎麼樣?然後,您仍然可以使用簡單的界面,並將詳細信息留給每個配方。

另一種選擇將是一個字符串數組作爲參數,類似於你如何分拆工作進入新的線程(指定調用的方法和包含其參數數組)。

+0

是的,我開始朝這個方向前進,但是因爲我想通過基於索引的列表而不僅僅是某些方法的索引值本身,所以我需要傳遞對象數組的靈活性。儘管我可以通過類屬性傳遞複雜的對象,並通過字符串傳遞參數,但確實讓我思考。那麼我可能做'T DoWork (string params)',當我用factory'(foo)object = Factory.Step (key);'如果這可以工作,那麼可以在上面的位也可以使用反射通用? – HodlDwon

+0

是否需要偶爾從DoWork方法返回某些內容?似乎任何錯誤處理都將在DoWork方法本身內解決。然後,您可以通過列表並讓每個配方根據需要投射列表中的項目。 – Pedro

+0

是的,我確實需要傳入和傳出DoWork()方法的東西。我發佈了我的概念證明,它可以編譯和工作,但隨時可以提供更多的反饋:) – HodlDwon