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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With