开发者

Complicated .NET factory design

开发者 https://www.devze.com 2023-01-03 01:13 出处:网络
I\'m planning to ask a fairly elaborate question that is also something of a musing here, so bear with me...

I'm planning to ask a fairly elaborate question that is also something of a musing here, so bear with me...

I'm trying to design a factory implementation for a simulation application. The simulation will consist of different sorts of entities i.e. it is not a homogenous simulation in any respect. As a result, there will be numerous very different concrete implementations and only the very general properties will be abstracted at the top level.

What I'd like to be able to do is create new simulation entities by calling a method on the model with a series of named arguments representing the parameters of the entity, and have the model infer what type of object is being described by the inbound parameters (from the names of the parameters and potentially the sequence they occur in) and call a factory method on the appropriate derived class.

For example, if I pass the model a pair of parameters (Param1=5000, Param2="Bacon") I would like it to infer that the names Param1 and Param2 'belong' to the class "Blob1" and call a shared function "getBlob1" with named parameters Param1:=5000, Param2:="Bacon" whereas if I pass the model (Param1=5000, Param3=50) it would call a similar factory method for Blob2; because Param1 and Param3 in that order 'belong' to Blob2.

I foresee several issues to resolve:

  • Whether or not I can reflect on the available types with string parameter names and how to do this if it's possible
  • Whether or not there's a neat way of doing the appropriate constructor inference from the combinatoric properties of the argument list or whether I'm going to have to bodge something to do it 'by hand'.
  • If possible I'd like the model class to be able to accept parameters as parameters rather than as some collection of keys and values, which would require the model to expose a large number of parametrised methods at runtime without me having to code them explicitly - presumably one for every factory method available in the relevant namespace.

What I'm really asking is how you'd go about implementing such a system, rather than whether or not it's fundamentally possible. I don't have the foresight or experience with .NET reflection to be able to figure out a way by myself. Hopefully this will prove an informative discussion.

UPDATE:

Well, I've had a crack at implementing a simple case, a toy model, essentially. The core logic is thus:

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

This just picks a method on the current instance and executes it. I'd like to extend that to a subset of all the types in the assembly. I'd also like to do this, if possible, by letting those types decide what their parametrised initialisation requirements are at runtime by examining their own properties, i.e. each property marked with 'ParametrisedInitAttribute' corresponds to exactly one named argument in the factory method. This would require that I have these types expose a method with N arguments at runtime. Is there a way to construct a method with an arbitrary signature and add it to the class implementation at runtime, or a similar way of doing this sort of thing?


This seems like a good place to use the new dynamic features of .NET 4 - a DynamicObject can override TryInvokeMember to examine the name and arguments a method is invoked with; you can use that to build a dictionary of factory methods that will be called dynamically based on the passed arguments. Here's an example I just cooked up:

The dynamic factory class:

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;
        }
    }
}

The classes we want to build:

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."); 
    }
}

Test code:

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();
}

Output:

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

Note: this is my own first time playing with dynamic, so there might be issues with this I'm unaware of.


What you want to do is perfectly possible with Reflection and can be done more or less straight forward as you described it:

  • Iterate over all available types. You have different options how to do that. You can choose types by a common subclass, by a certain interface, mark them with an attribute, ... It depends on your concrete implementation what's best for you. There are also frameworks which help to do that, but again you have to check your concrete requirements.

  • For all these types you can retrieve the "factory", which might be the constructor for example.

  • For all these factories you can query the required parameters with their types and names.

  • Now you have to define some ordering rules for these factories and if you want to construct an object, you just go through the list and search for the first matching factory.

Implementation should be quite simple, but perhaps a bit boring. For each step there are different options to fine tune the solution for your requirements.

0

精彩评论

暂无评论...
验证码 换一张
取 消