Java Design – Keeping Argument Counts Low with Third Party Dependencies

constructorsdesignjavathird-party-libraries

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).

Best Answer

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.

Related Topic