Question

I use a third party library. They pass me a POJO that, for our intents and purposes, is probably implemented like this:

public class OurData {
  private String foo;
  private String bar;
  private String baz;
  private String quux;
  // A lot more than this

  // IMPORTANT: NOTE THAT THIS IS A PACKAGE PRIVATE CONSTRUCTOR
  OurData(/* I don't know what they do */) {
    // some stuff
  }

  public String getFoo() {
    return foo;
  }

  // etc.
}

For many reasons, including but not limited to encapsulating their API and facilitating unit testing, I want to wrap their data. But I don't want my core classes to be dependent on their data (again, for testing reasons)! So right now I have something like this:

public class DataTypeOne implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
  }
}

public class DataTypeTwo implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz, String quux) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
    this.quux = quux;
  }
}

And then this:

public class ThirdPartyAdapter {
  public static makeMyData(OurData data) {
    if(data.getQuux() == null) {
      return new DataTypeOne(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
      );
    } else {
      return new DataTypeTwo(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
        data.getQuux();
      );
  }
}

This adapter class is coupled with the other few classes that MUST know about the third party API, limiting it's pervasiveness through the rest of my system. However... this solution is GROSS! In Clean Code, page 40:

More than three arguments (polyadic) requires very special justification--and then shouldn't be used anyway.

Things I've considered:

  • Creating a factory object rather than a static helper method
    • Doesn't solve the problem of having a bajillion arguments
  • Creating a subclass of DataTypeOne and DataTypeTwo that has a dependent constructor
    • Still has a polyadic protected constructor
  • Create entirely separate implementations that conform to the same interface
  • Multiple of the above ideas simultaneously

How should this situation be handled?


Note this is not an anti-corruption layer situation. There's nothing wrong with their API. The problems are:

  • I don't want MY data structures to have import com.third.party.library.SomeDataStructure;
  • I can't construct their data structures in my test cases
  • My current solution results in very very high argument counts. I want to keep argument counts low, WITHOUT passing in their data structures.
  • That question is "what is a anti corruption layer?". My question is "how can I use a pattern, any pattern, to solve this scenario?"

I'm not asking for code, either (otherwise this question would be on SO), just asking for enough of an answer to enable me to write the code effectively (which that question does not provide).

Was it helpful?

Solution

The strategy I've used when there are several initialization parameters is to create a type which just contains the parameters for initialization

public class DataTypeTwoParameters {
    public String foo;  // use getters/setters instead if it's appropriate
    public int bar;
    public double baz;
    public String quuz;
}

Then the constructor for DataTypeTwo takes a DataTypeTwoParameters object, and DataTypeTwo is constructed via:

DataTypeTwoParameters p = new DataTypeTwoParameters();
p.foo = "Hello";
p.bar = 4;
p.baz = 3;
p.quuz = "World";

DataTypeTwo dtt = new DataTypeTwo(p);

This gives a lot of opportunity to make it clear what all the parameters going into DataTypeTwo are and what they mean. You can also provide sensible defaults in the DataTypeTwoParameters constructor so that only values which need to be set can be done so in any order the consumer of the API likes.

OTHER TIPS

You really have two separate concerns here: wrapping an API and keeping the argument count low.

When wrapping an API, the idea is to design the interface as if from scratch, knowing nothing other than the requirements. You say there is nothing wrong with their API, then in the same breath list a number of things wrong with their API: testability, constructability, too many parameters in one object, etc. Write the API you wish you had. If that requires multiple objects instead of the one, do that. If it requires wrapping one level higher, to the objects that create the POJO, do that.

Then once you have your desired API, the parameter count may no longer be an issue. If it is, there are a number of common patterns to consider:

  • A parameter object, as in Erik's answer.
  • The builder pattern, where you create a separate builder object, then call a number of setters to set the parameters individually, then create your end object.
  • The prototype pattern, where you clone subclasses of your desired object with the fields already set internally.
  • A factory, which you are already familiar with.
  • Some combination of the above.

Note that these creational patterns often end up calling a polyadic constructor, which you should consider okay when it's encapsulated. The problem with polyadic constructors isn't calling them once, it's when you're forced to call them every single time you need to construct an object.

Note that usually it's much easier and more maintainable to pass through to the underlying API by storing a reference to the OurData object and forwarding the method calls, rather than trying to reimplement its internals. For example:

public class DataTypeTwo implements DataInterface {
  private OurData data;

  public DataTypeOne(OurData data) {
    this.data = data;
  }

   public String getFoo() {
    return data.getFoo();
  }

  public int getBar() {
    return Integer.parseInt(data.getBar());
  }
  ...
}

I think you might be interpreting Uncle Bob's recommendation too strictly. For normal classes, with logic and methods and constructors and such, a polyadic constructor does indeed feel a lot like code smell. But for something that is strictly a data container that exposes fields, and is generated by what is essentially a Factory object already, I don't think it's too bad.

You can use the Parameter Object pattern, as suggested in a comment, can wrap these constructor parameters for you, what your local data type wrapper is is already, essentially, a Parameter object. All your Parameter object will be doing is packing the parameters up (How will you create it? With a polyadic constructor?) and then unpacking them a second later into an object which is almost identical.

If you don't want to expose setters for your fields and call them, I think that sticking to a polyadic constructor inside a well-defined and encapsulated factory is fine.

Licensed under: CC-BY-SA with attribution
scroll top