First Past the Post
Often, we write code to work out the first answer of a bunch of available ones. Let’s look at that in Java.
01 02 03 04 05 06 07 08 09 10 11 12 | public Widget getAppropriateWidget(CustomerRequest request) { if (shelfstock.contains(request.getBarcode()) { return new ShelfWidget(); } if (backroomStock.contains(request.getBarcode()) { return new BackroomWidget(); } if (supplier.contains(request.getEan()) { return new SupplierWidget(); } return null ; } |
You’ll have to imagine the more complex scenario, hiding behind the above simplified code. What this algorithm does is try options in order of priority until it finds one that works, or it fails, in which case it returns nothing.
Let’s also imagine that the calls to contains
are expensive for some reason – perhaps each of these objects is hiding a webservice, or complex database query.
Let’s start out by refactoring the above code two ways. Let’s make it use Optional
, and let’s make it use subroutines for each of the methods.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | public Optional<Widget> getAppropriateWidget(CustomerRequest request) { Optional<Widget> shelfWidget = getShelfWidget(request); if (shelfWidget.isPresent()) { return shelfWidget; } Optional<Widget> backroomWidget = getBackroomWidget(request); if (backroomWidget.isPresent()) { return backroomWidget; } Optional<Widget> supplierWidget = getSupplierWidget(request); if (supplierWidget.isPresent()) { return supplierWidget; } return Optional.empty; } // imagine the subsidiary functions |
So, this is sort of better than null
being the return for not found and is trying hard to use subroutines to make this function describe itself, but it’s having trouble with the fact that each of the Optional
objects returned can’t be chained into a chain of responsibility.
We could cheat:
01 02 03 04 05 06 07 08 09 10 11 12 13 | Optional<Widget> shelfWidget = getShelfWidget(request); Optional<Widget> backroomWidget = getBackroomWidget(request); Optional<Widget> supplierWidget = getSupplierWidget(request); return firstNonEmpty(shelfWidget, backroomWidget, supplierWidget); private static Optional<Widget> firstNonEmpty( Optional<Widget> ... options) { return Arrays.stream(options) .filter(Optional::isPresent) .findFirst() // makes an optional of optional here... .orElse(Optional.empty()); } |
The above code is sort of better but now has to pre-calculate all possible answers before selecting one. We need to be able to avoid costly option calculation if the answer is available sooner.
The First Past the Post with Optionals Solution
Pass either a stream or varargs array to a function, formed of objects that will supply an optional. If any of them supplies a non-empty then it wins.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | // calling code public Optional<Widget> getAppropriateWidget(CustomerRequest request) { return firstAvailable(() -> getShelfWidget(request), () -> getBackroomWidget(request), () -> getSupplierWidget(request)); } // this is a general purpose solution // feel free to use it @SafeVarargs private static <T> Optional<T> firstAvailable( Supplier<Optional<T>> ... options) { return Arrays.stream(options) .map(Supplier::get) .filter(Optional::isPresent) .findFirst() .orElse(Optional.empty()); } |
Published on Java Code Geeks with permission by Ashley Frieze, partner at our JCG program. See the original article here: First Past the Post Opinions expressed by Java Code Geeks contributors are their own. |