mercredi 27 juillet 2016

Separating parts of a class which depend on a 3rd party API into a separate assembly

I'm developing an application that processes objects from 3rd party .NET API. Processors are created by implementing the following IProcessor interface.

public interface IProcessor
{
    Process(ApiClass input);
}

Concrete implementations of this interface may also add other properties to control how the specific processors work. For example...

public class ProcessorA : IProcessor
{
    public double X;
    public double Y;

    public Process(ApiClass input)
    {
       //Code here...
    }
}

public class ProcessorB : IProcessor
{
    public string MyParameter;
    public string MyOtherParameter;

    public Process(ApiClass input)
    {
       //Code here...
    }
}

The third party API and ApiClass only function from inside the native application. However, I would like to be able to create other .NET assemblies that are able to read and write Processors, but do not need to reference the 3rd party API. Specifically I want to be able to serialize/deserialize processors to/from JSON.

So, I'm trying to determine the best way to split the parameters of a Processor and its implementation of IProcessor.Process() into two separate assemblies. One that references the 3rd party API and another that doesn't.

One option would be to make duplicate proxy versions of the objects.

//In assembly that does NOT reference 3rd party API
public class ProcessorAProxy
{
    public double X;
    public double Y;
}

//In assembly that references 3rd party API
public class ProcessorA 
{
    public double X;
    public double Y;

    public Process(ApiClass input)
    {
       //Code here...
    }
}

But this would require that I keep the properties in sync between these classes. I would also have to write mapping code to translate between them.

Another option would be to store the parameters in the proxy class, and then create a class that inherits from this class which adds the Process() method.

//In assembly that does NOT reference 3rd party API
public class ProcessorA
{
    public double X;
    public double Y;
}

//In assembly that references 3rd party API
public class ProcessorA : ProcessorAProxy
{
    public Process(ApiClass input)
    {
       //Code here...
    }
}

This option no longer has duplicate properties that I need to keep in sync. Yet if I deserialized a ProcessorAProxy I'm not sure how I could then convert it to a real ProcessorA with out having to deal with mapping values again.

Alternative to inheriting classes I could also inject the options into the class that actually does the processing through its constructor.

//In assembly that does NOT reference 3rd party API
public class ProcessorAOptions: IOptions 
{
    public double X;
    public double Y;
}

//In assembly that references 3rd party API
public class ProcessorA : IProcessor
{ 
   public ProcessorAOptions Options;

   public ProcessorA(ProcessorAOptions opt)
   {
       Options = opt;
   }

    public Process(ApiClass input)
    {
       //Code here...
    }
}

I would then deserialize a specific type of IOptions, which would then need to get passed to the constructor of the correct concrete type of IProcessor. This could be handled by a ProcessorFactory.

IOptions options = serializer.Read(); //Read the some options
ProcessorFactory fact = new ProcessorFactory(); //Create a factory
IProcessor proc = fact.GetProcessor(options); //Get the processor for options from the factory

So far I like this option the best, but I'm not sure the best way for the ProcessorFactory to map from a specific types of IOptions to the correct IProcessor type.

One option would be to create a giant if statement that checks for each option, type.

if(opt.GetType() == typeof(ProcessorAOptions))
{
    return new ProcessorA((ProcessorAOptions)opt);
}
else if(opt.GetType() == typeof(ProcessorBOptions))
{
    return new ProcessorB((ProcessorBOptions)opt);
}
etc.. etc..

This works, but seems a little inefficient, especially if I have a lot of Processor types.

Another option would be to use conventions and reflection. For example find a class which implements IProcessor, and has a constructor which takes the specific IOptions type as a parameter. Or find a class with a name that equals the name of the options class with the "Options" suffix removed (ProcessorBOptions -> ProcessorB).

Is there a better option the using reflection? Overall is there a better way to split the code that references the 3rd party API and the code that doesn't into classes in separate assemblies that are some how mapped to one another?

Aucun commentaire:

Enregistrer un commentaire