【问题标题】:Complicated .NET factory design复杂的 .NET 工厂设计
【发布时间】:2011-03-02 23:36:43
【问题描述】:

我打算问一个相当复杂的问题,在这里也是一种沉思,所以请耐心等待......

我正在尝试为模拟应用程序设计工厂实现。模拟将由不同种类的实体组成,即它在任何方面都不是同质模拟。因此,将有许多非常不同的具体实现,只有非常一般的属性会在顶层抽象出来。

我想做的是通过在模型上调用一个方法来创建新的模拟实体,该方法使用一系列代表实体参数的命名参数,并让模型推断正在描述的对象类型通过入站参数(来自参数的名称以及它们可能出现的顺序)并在适当的派生类上调用工厂方法。

例如,如果我向模型传递一对参数 (Param1=5000, Param2="Bacon"),我希望它推断名称 Param1 和 Param2“属于”类“Blob1”并调用具有命名参数 Param1:=5000, Param2:="Bacon" 的共享函数“getBlob1”,而如果我传递模型 (Param1=5000, Param3=50),它将为 Blob2 调用类似的工厂方法;因为 Param1 和 Param3 按此顺序“属于”Blob2。

我预计有几个问题需要解决:

  • 我是否可以使用字符串参数名称反映可用类型以及如果可能的话如何做到这一点
  • 是否有一种巧妙的方法可以从参数列表的组合属性中进行适当的构造函数推断,或者我是否必须“手动”进行某些操作。
  • 如果可能的话,我希望模型类能够接受参数 as 参数,而不是一些键和值的集合,这需要模型公开大量参数化方法在运行时,我无需显式编写它们 - 大概是为相关命名空间中可用的每个工厂方法一个。

我真正要问的是你将如何实施这样一个系统,而不是它是否从根本上可行。我对 .NET 反射没有远见或经验,无法自己想办法。希望这将是一次内容丰富​​的讨论。

更新:

嗯,基本上,我在实现一个简单的案例,一个玩具模型方面已经有所突破。核心逻辑是:

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 个参数的方法。有没有办法构造一个具有任意签名的方法并在运行时将其添加到类实现中,或者类似的方式来做这种事情?

【问题讨论】:

  • 可能的参数名称的总集合是否固定?并且每个具体参数都有固定的类型吗?
  • tzaman:问得好。我想答案是“视情况而定”;因为我还没有决定。我想到的是能够允许用户通过简单的拖放/复制粘贴(例如)Excel数据来配置模型;并且模型通过存在的值的组合来推断用户打算添加哪些对象。理想情况下,模型可以猜测标题被省略时每个字段的含义或其他情况。但总的来说,模型在运行时而不是设计时就知道完整的参数集,但之后会被修复。
  • 顺便说一句,我想要这个功能的另一个原因是程序架构的原因——关注点分离和分层功能,我希望 UI 演示者能够简单地抛出一堆模型中的值,并假设它知道如何处理它们(如果需要)。

标签: .net factory named-parameters


【解决方案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,所以可能存在我不知道的问题。

【讨论】:

    【解决方案2】:

    使用反射完全可以实现您想要做的事情,并且可以或多或少地按照您的描述直接完成:

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

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

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

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

    实现应该很简单,但可能有点无聊。对于每个步骤,都有不同的选项可以根据您的要求微调解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 2021-09-11
      • 1970-01-01
      • 2022-01-19
      相关资源
      最近更新 更多