3

My question is similar to (but not exactly the same as) this question. This question was also helpful but it is less similar to my current question than the previous question that I linked to.

I have a framework that runs automated testing. Basically, an XML "script" has data on which methods to call against our API; our engine runs the specified methods with reflection.

Due to how the logs are generated, sometimes we want to replace certain method calls with another one. (There's not really any way around that). A factory "knows" which methods those are based on an XML file that contains the "mapping."

// Originally we called this a "Command" pattern but it may be more accurate to call it a Strategy
// See if we're supposed to replace this method call with something else
bool wereAnyCommandsRun = await ExecuteCommandsIfThereAreAny(interfaceName, methodName);

// If we're NOT supposed to replace this method call, go ahead and execute it
if (!wereAnyCommandsRun)
{
   // Do the method call
   // ...
}

We do the XML mapping like this:

<map method="CaptureDomain" interface="IFrameworkInterface" command="ApplyXsltCommand"/>

Right now "Commands" (probably more accurately "Strategies") are completely standard - they implement an interface with an Execute() method.

Now I need to add parameters to this. I don't know in advance which parameters they will need, so I'm trying to come up with something that wouldn't be completely hideous and fragile. My main requirements are that I don't want the design to become overly complicated; also, the primary point of this is to make my engine as "future-proof" as possible, so I want to make sure that it doesn't require modification if I add or refactor a method or a new requirement comes in (meaning that the Command and its caller have to be loosely coupled).

A few ideas I had and rejected:

  • Accept a params array and use reflection to decide whether the caller passed in values of the correct type. I don't like this though because it dramatically increases the coupling between the command, and the whole point of the mechanism is to reduce the coupling.
  • Create parameter "objects" for each possible type. This, too, would create a lot of "coupling."
  • Parameterized factories

Some ideas I'm seriously considering:

  • Have a Context object like one of the answers in the question I linked to suggested. If I'm reading the question I linked to correctly, the advantages here would be that the caller wouldn't have to "know" anything about command in advance. There could be a performance hit if I end up capturing too much information about the caller, but performance isn't a major consideration here.
  • I have a MethodInfo object and parameter info for the method I'm replacing - maybe add this as a third "item" in the Factory's mapping. The first problem I see here is similar to the downside of the previous solution: the fact that a parameter is necessary for the method being called doesn't imply that it's also necessary for the Command. Also, my Commands may be called by several different methods, so it's important that there be no coupling at all between them (so it's more complicated than just "if you see this parameter list, use that command"). Could this lead to too much coupling?
  • Try to implement some kind of a Visitor pattern instead of the Strategy pattern. However, I'm a little hazy on how that would actually work to tell the truth. Would this just add needless complexity, or would it actually be a legitimate solution here?

I'm hoping that this isn't too broad or opinion-based (please let me know if so and I'll edit - I'm definitely not trying to ask a "gimme teh codez" type question), but does anyone see a problem with these solutions?

  • 1
    Why don't you just pass a JSON string as the parameter(s)? – Robert Harvey Oct 31 '16 at 18:53
  • 2
    If you're going to make your own programming language, then you should just make your own programming language. – Telastyn Oct 31 '16 at 18:54
  • @Telastyn My intent isn't to make a separate programming language per se - I just need to be able to re-run a series of pre-recorded steps - so far reflection's been adequate for the job. – EJoshuaS Oct 31 '16 at 18:56
  • 2
    Right, but now you're mocking up XML with function names and parameters. Hell you're even doing type checking to make sure the parameters passed in are correct. – Telastyn Oct 31 '16 at 19:01
  • @Telastyn I did actually consider just outputting C# or Lua at one point and to heck with the XML but it ended up being simpler to just do reflection. – EJoshuaS Oct 31 '16 at 19:49
2

Well, you could use dynamic and ExpandoObject.

_parameterNames => Name of parameter

_data => Data to be passed

private dynamic Create(string[] data)
        {
            dynamic expando = new ExpandoObject();
            for (var i = 0; i < _parameterNames.Length; i++)
            {
                ((IDictionary<string, object>) expando).Add(_parameterNames[i], data[i]);
            }
            return expando;
        }

This will give you a very flexible means to pass parameter(s) and data to your 'reflector' to add all the parameters to the method that is being invoked via reflection. I would keep parameters in order to preserve sanity.

1

What kind of parameters are we talking about here? Are they dependencies (repositories, services, adapters, etc) or are they inputs (entity id, mode selection, category id, etc)?

If they're dependencies, then use an IoC Container. Register your dependencies at startup, and then use the IoC Container to resolve your command instances for you as needed.

If, on the other hand, the parameters are inputs, then Jon's answer seems fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.