2010-06-12 62 views
2

我打算问一个相当复杂的问题,这也是的东西在这里沉思,如此忍受我...复杂的.NET工厂设计

我试图设计一个工厂实现了模拟应用。该模拟将由不同种类的实体组成,即它在任何方面都不是均质模拟。因此,将会有许多非常不同的具体实现,只有非常普遍的属性才会在顶层抽象出来。

我想要做的就是通过调用模型上的一个方法来创建新的模拟实体,其中包含一系列代表实体参数的命名参数,并让模型推断出什么类型的对象由入站参数(从参数的名称以及它们出现的顺序)描述,并在适当的派生类上调用工厂方法。例如,如果我传递模型一对参数(Param1 = 5000,Param2 =“Bacon”),我希望它推断名称Param1和Param2'属于'Blob1类并且调用一个名为参数Param1:= 5000,Param2:=“Bacon”的共享函数“getBlob1”,而如果我传递模型(Param1 = 5000,Param3 = 50),它会为Blob2调用一个类似的工厂方法;因为Param1和Param3按顺序'属于'Blob2。

我预见几个问题需要解决:

  • 不论是否我能在可用类型的字符串参数的名称,以及如何做到这一点反映,如果有可能
  • 无论是否有一个整洁的方式从参数列表的组合属性中进行适当的构造函数推理,或者我将不得不躲过某些“手工”操作。
  • 如果可能,我希望模型类能够接受参数作为参数而不是一些键和值的集合,这将需要模型在运行时暴露大量参数化方法,而不需要我可以明确地对它们进行编码 - 大概是针对相关名称空间中可用的每种工厂方法的编码。

我真正要问的是如何执行这样的系统,而不是从根本上是否可行。我没有.NET反思的先见之明或经验能够自己找出方法。希望这将证明一个信息丰富的讨论。

UPDATE

好了,我已经在实现一个简单的例子,一个玩具模型,本质上是一个裂缝。核心逻辑如下:

Dim members() As Reflection.MethodInfo = Me.GetType.GetMethods() 
    Dim params_enum As IEnumerator(Of String) = params.Keys.GetEnumerator 

    While params_enum.MoveNext 
     members = Array.FindAll(members, _ 
           Function(m As Reflection.MethodInfo) Array.Exists(CType(m, Reflection.MethodInfo).GetParameters(), _ 
                        Function(mm As Reflection.ParameterInfo) mm.Name = params_enum.Current)) 
     If members.Count = 0 Then Return False 
    End While 
    Try 
     CType(members(0), Reflection.MethodInfo).Invoke(Me, params.Values.ToArray()) 
     Return True 
    Catch ex As Exception 
     Return False 
    End Try 

这只是在当前实例上选择一个方法并执行它。我想把它扩展到程序集中所有类型的子集。如果可能的话,我也想这样做,通过检查它们自己的属性,让这些类型决定它们在运行时的参数初始化要求是什么,即每个标有'ParametrisedInitAttribute'的属性都对应于工厂方法中的一个命名参数。这将要求我有这些类型在运行时公开一个N参数的方法。有没有办法构造一个具有任意签名的方法,并在运行时将其添加到类实现中,或者采用类似的方法来执行此类事情?

+0

是否修复了可能参数名称的总集合?并且每个特定的参数是否有固定的类型? – tzaman 2010-06-12 11:13:23

+0

tzaman:好问题。我想答案是'这取决于';因为我还没有决定。我想到的是能够允许用户使用(例如)Excel数据的简单拖放/复制粘贴来配置模型;并且该模型通过存在的值的组合来推断用户想要添加的对象。理想情况下,模型可以猜测每个字段在标题被省略或什么时候意味着什么。一般来说,整套参数在运行时会被模型知道,而不是设计时间,但之后会被修复。 – 2010-06-12 11:20:25

+0

顺便说一句,我希望这个功能的另一个原因是程序架构的原因 - 关注点和分层功能的分离,我希望UI展示者能够简单地在模型中引入一堆值,并假设如果需要的话,它知道如何处理它们。 – 2010-06-12 11:25:30

回答

1

这似乎是一个很好的使用.NET 4的新dynamic功能的地方 - 一个DynamicObject可以覆盖TryInvokeMember来检查一个方法被调用的名称和参数;你可以使用它来构建一个工厂方法的字典,它将根据传递的参数动态调用。这是我刚煮好了一个例子:

动态工厂类:

class DynaTest : DynamicObject 
{ 
    Dictionary<string, Func<object[], object>> builders; 

    public DynaTest() 
    { 
     builders = new Dictionary<string, Func<object[], object>>(); 
     builders.Add("fur,fangs", args => 
        new Dog((string)args[0], (int)args[1])); 
     builders.Add("fur,claws", args => 
        new Cat((string)args[0], (string)args[1])); 
    } 

    public override bool TryInvokeMember(InvokeMemberBinder binder, 
             object[] args, out object result) 
    { 
     string key = String.Join(",", binder.CallInfo.ArgumentNames.ToArray()); 
     Func<object[], object> builder; 
     if (builders.TryGetValue(key, out builder)) 
     { 
      result = builder(args); 
      return true; 
     } 
     else 
     { 
      result = null; 
      return false; 
     } 
    } 
} 

我们要构建的类:

class Dog 
{ 
    string _fur; 
    int _fangs; 
    public Dog(string fur, int fangs) 
    { 
     _fur = fur; 
     _fangs = fangs; 
    } 
    public void Bark() 
    { 
     Console.WriteLine("Woof! I have " + _fur + " fur and " 
          + _fangs + " fangs."); 
    } 
} 

class Cat 
{ 
    string _fur; 
    string _claws; 
    public Cat(string fur, string claws) 
    { 
     _fur = fur; 
     _claws = claws; 
    } 
    public void Meow() 
    { 
     Console.WriteLine("Meow! I have " + _fur + " fur and " 
          + _claws + " claws."); 
    } 
} 

测试代码:

static void Main(string[] args) 
{ 
    dynamic dyn = new DynaTest(); 
    Dog d = dyn.Build(fur: "rough", fangs: 4); 
    d.Bark(); 
    Cat c = dyn.Build(fur: "shiny", claws: "sharp"); 
    c.Meow(); 
} 

输出:

Woof! I have rough fur and 4 fangs. 
Meow! I have shiny fur and sharp claws. 

注意:这是我第一次玩dynamic,所以可能有这个问题我不知道。

0

你想要做什么是完全可能的反射,可以像你描述它或多或少直截了当做到:

  • 遍历所有可用的类型。你有不同的选择如何做到这一点。你可以通过一个公共的子类,通过某个接口来选择类型,用属性标记它们......这取决于你的具体实现是什么对你最好。还有框架可以帮助你做到这一点,但你也必须检查你的具体要求。

  • 对于所有这些类型,您可以检索“工厂”,例如可能是构造函数。

  • 对于所有这些工厂,您可以查询所需的参数及其类型和名称。

  • 现在您必须为这些工厂定义一些排序规则,并且如果您想构建一个对象,您只需浏览列表并搜索第一个匹配的工厂。

实施应该很简单,但也许有点无聊。对于每一步都有不同的选项来根据您的要求微调解决方案。