The Converter


The Converter

Looks like this:

public static class Converter {

   public static Product toProductFromWidget(Widget widget) {

      return toProduct(widget.getLength(), widget.getHeight());
   }


   public static Product toProductFromThing(Thing thing) {
      return toProduct(thing.getA(), thing.getB());
   }

   private static Product toProduct(int x, int y) {
      return new Product(x, y);
   }
}

What does it do?

The converter is useful when you need a uniform treatment for disparate inputs.  One example would be if you're receiving data from multiple vendors but need to assure that regardless of upstream, the treatment and output must be the same.

In this way, a converter rather resembles an inverse Factory pattern.  Whereas a factory takes a uniform input with variance in its values and produces a range of disparate outputs based on those values, a converter takes disparate inputs and produces a uniformly formatted output.

Why is this useful?

There are numerous ways to accomplish what the Converter does, but the benefit we get from its application is a single, co-located, intuitive representation of transformation logic.  Rather than having to search your codebase for how you get from A to B, it's all in one place.

An alternative would be to push the mapping behaviors into their respective sources, Widget and Thing above. However, by doing so you risk the uniformity of your toProduct implementation.  In time, what started the same may diverge.  Even if the behavior stays identical, you may get differing implementations causing undue complexity.

To address this problem, you could have Widget and Thing each inherit from a class implementing a single toProduct method.  However, there are cases where you may not wish to or may not be able to share classes across modules.  An example would be when Widget, Thing, and Product each have their own micro-service.  Pushing the toProduct method into Widget and Thing would again risk divergence.

If your "toProduct" method shouldn't have multiple implementations and you want to minimize coupling across source classes a Converter may help.

No accessors?

Since the output of a converter is the singular Product instance, we should avoid opening it up by introducing accessors such as getX() and getY() in the above example.  Why not?  The Converter's responsibility is to provide a mapping from N+ classes to our Product class, nothing else.  Having access to anything other than Product makes the Converter's purpose ambiguous.  A future developer may be tempted to add behavior better located somewhere else.

No mutators?

All the input we need for our conversion is in the source classes.  There really should be no state in the Converter.  Adding mutators again introduces ambiguity into purpose of the class.

Since we have no mutators and no accessors we have no state.  The class is thus static.  However, there may be more complex cases where state may be necessary.  I've simply provided a simple example here.  That said, if no state is necessary, no state should be allowed.  This is a form of Principle of Least Privilege.

Comments

Popular posts from this blog

Engineering Truisms

The Telescoping Constructor (Anti-Pattern)

A Strategic Approach to Precision Process Automation