Search code examples
javaapidata-access-layer

What's the Ideal Way to Define Singular vs Plural Gets in a Storage API?


I've got an internal storage layer in my application, which handles Foo objects. During Get operations, the data layer has significant benefits to clustering gets, but I only actually do multiple gets about 10% of the time. Here are various approaches I've considered:

Approach A:

interface FooStorage {
  Foo getFoo(String name);
  List<Foo> getFoos(List<String> names);
}

Approach B:

interface FooStorage {
  List<Foo> getFoos(List<String> names);
}
class StorageUtility {
  public static <T> T firstOrNull(List<T> data) { ... }
}

Approach C:

interface FooStorage {
  List<Foo> getFoos(String... names);
}
class StorageUtility {
  public static <T> T firstOrNull(List<T> data) { ... }
}

The downside to Approach A is having a larger surface that I need to support.
The downside to Approach B is having the consumer build a List when 90% of the time I don't need it. The downside to Approach C is the overhead of copying a list to an array 10% of the time.

Is there a canonical right way to do this?


Solution

  • In this type of situation, I would tend go with the following construct:

    Foo getFoo(String name) {
        return firstOrNull(getFoos(name));
    }
    List<Foo> getFoos(String ... names) {
        return getFoos(Arrays.asList(names));
    }
    List<Foo> getFoos(List<String> names) {
        ....
    }
    

    Your client should use the most appropriate method each time, and if you latter discover that performance requires a more targeted approach for getFoo(name), you can re-implement that single method.

    I would argue that it is more important to keep the consumer code readable (avoid creating lists just to satify the API), than saving a few lines of code in the interface/implementation of the storage system.