lundi 31 octobre 2016

How to parameterize Commands without the caller "knowing" anything about the Command ahead of time

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" that

// 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. Obviously, though, there could be a performance hit if I end up capturing too much information about the caller.
  • 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 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").
  • 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, and it seems like it could just complicate things.

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? Are there any other possibilities (besides what I've listed here and in the other question) that would be better?

Aucun commentaire:

Enregistrer un commentaire