3

I'm writing an interface to bundle two underlying APIs into one new. The APIs provide data about archived invoices.

All three APIs (the two old ones and my new one) have different data structures.
For example: <INVOICE_NO> (old XML), "number" (old JSON), "formatted_number" (new JSON) all mean the same field.

I've created a class for each API and let Spring's RestTemplate handle the parsing/ formatting of the responses.

Now, after receiving a ClassA object from the first API and a ClassB object from the second API, I have to convert them to ClassC objects and return them in it's format.

My first approach was to create two constructors for ClassC which either take an object of type ClassA or an object of type ClassB as argument.

But now, I'm not sure if that's the right thing to do because I'm coupling two data transfer objects.

Would it be better to create an InvoiceConverter class or even something else?

3

But now I'm not sure if that's a nice thing to do because I'm coupling two data transfer objects.

Yes, this looks like a code smell.

Would it be better to create an InvoiceConverter class or even something else?

Probably yes. If I got you right, you then will have a data flow like

     [class A or B object] -> [Converter] -> [C object] -> [code using C]

and Converter is the only place in your code which is directly dependent from the APIs A and B, whilst C objects as well as the code using C objects will not depend on A or B any more (which is probably your goal here, to decouple the different parts of your system).

If, however, you make C objects directly dependent on A and B and try to avoid a separate converter class, all code using C objects will still depend on A and B, which is probably something you want to avoid. Think about what this means when you want to place the code using C in a separate library (using a compiled language). In the second case, this library needs to be linked against the APIs of A and B, whilst in the first case the lib does not need a linkage against A or B.

3

This seems like a perfectly good example of using Adapter Pattern

Basically, you would create one interface that would have one method:

public interface Adapter
{
   ClassC ConvertObject();
}

And then you would create two classes, implementing this interface, like this:

class AdapterClass : Adapter
{
   private ClassA adaptee;
   public AdapterClassA(ClassA pAdaptee)
   {
        adaptee = pAdaptee;
   }

   public ClassC ConvertObject()
   {
      //Code that converts ClassA to ClassC
   }
}

class AdapterClassB : Adapter
{
   private ClassB adaptee;
   public AdapterClassB(ClassB pAdaptee)
   {
        adaptee = pAdaptee;
   }

   public ClassC ConvertObject()
   {
      //Code that converts ClassB to ClassC
   }
}

This way, you are decoupling the logic of type conversion into different classes, leaving the same interface to the user class.

Now, one might raise an issue with unnecessarily creating an interface when nobody but ClassC will invoke the constructor of adapter classes. The interface is not there just because of who will invoke the constructor. For instance, it will be easier to write unit tests if you have something like this.

Next, you might want to decouple the classes completely, and move the invokation of the constructor to some factory class, where ClassC would just see the Adapter interface and then it would not depend on ClassA or ClassB in any way. Of course, this is flirting with the violation of KISS principle, but I would call it a judgment call.

Bottom line is: If you want to decouple ClassC from ClassA and ClassB, there must be something that binds adapter classes. It can be an abstract parent class, or a regular parent class or an interface. Considering that the parent entity carries no information, it is logical to use interface.

  • I don't see the point of an interface here - who is going to invoke the constructor if not the client class? – Jacob Raihle Jun 1 '17 at 8:04
  • 1
    I amplified my answer to address this comment. Thank you for the question. – Vladimir Stokic Jun 1 '17 at 11:11
  • While the adapter pattern is one possible solution, what you end up describing looks more akin to the abstract factory pattern (another viable solution). – cbojar Jun 2 '17 at 1:44
  • Not really abstract factory. Just factory pattern, in this particular case, which I stated in the answer. To be perfectly precise, I would say it is adapter pattern expanded with factory pattern. It just goes to show that seldom you have a situation of clear cut distinction between patterns, and often you have overlapping and combination of two or more patterns. – Vladimir Stokic Jun 2 '17 at 6:08
0

You are right, you need a converter. And sure, you don't want to create an specific interface for every conversion, that's what generics are for. I am not sure of the syntax in java, but i am pretty sure is doable as in C#:

First I'll create an interface to deal with conversions from one type to another:

public interface IObjectConverter<TSource, TResult>{
       TResult Convert(TSource source);
}

But you don't want to be referencing this for each conversion you want to make, that is why we'll create another interface, like this:

public interface IConverter(){
       TResult Convert<TSource,TResult>(TSource source);

       IObjectConverterFactory Factory;
}

Note that we have another interface here, the IObjectConverterFactory. You will use this on the implementation of IConverter.Convert to get the IObjectConverter object. The factory is like this:

public interface IConverterFactory
{
    IObjectConverter<TSource, TResult> Create<TSource, TResult>();
}

So we have 3 interfaces, which might seem like a lot, but the idea here is to facilitate the use (and not increase coupling). You'll only need to reference IConverter and tell the Convert method the source type and the result type.

You'll declare only:

IConverter _converter;

And use it like this:

var classC1 = _converter.Convert<ClassA,ClassC>(classA);

var classC2 = _converter.Convert<ClassB,ClassC>(classB);

From there you only need to worry about the specific implementations of the converters.

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.