java - Reducing if else branches and cleaning code using chain of functions, does it make sense -
i working on legacy code has lots of if/else. can use conditional decomposition and/or guard statements etc. clean it. approach can used create functions out of branches , execute these functions chain , exit first response. wondering if using function chain overkill? views?
my chain looks below. simplified example.
public class requeststatehandler { private final list<function<requeststateinfo, optional<response>>> handlers = new arraylist<>(); public requeststatehandler() { handlers.add(requeststateinfo -> xstatevalidator(requeststateinfo)); handlers.add(requeststateinfo -> ystatevalidator(requeststateinfo)); handlers.add(requeststateinfo -> zstatevalidator(requeststateinfo)); } public response getresponse(requeststateinfo requeststateinfo) { try { (final function<requeststateinfo, optional<response>> handler : handlers) { final optional<response> response = handler.apply(requeststateinfo); if (response.ispresent()) { return response.get(); } } } catch (exception e) { logger.error("some log", e); } return getdefaultresponse(requeststateinfo); } private optional<response> xstatevalidator(requeststateinfo requeststateinfo) { final a = requeststateinfo.geta(); if (a.isabc()) { return optional.of(requeststateinfo.getx()); } return optional.empty(); } private optional<response> ystatevalidator(requeststateinfo requeststateinfo) { if (requeststateinfo.isxyz()) { final response response = requeststateinfo.getx(); final a = response.geta(); if (a.issomeothertest()) { return optional.of(response); } } return optional.empty(); } private optional<response> zstatevalidator(requeststateinfo requeststateinfo) { final response response = requeststateinfo.getx(); final a = response.geta(); if (a.issomeanothertest()) { return optional.of(response); } return optional.of(getdefaultresponse(requeststateinfo)); } private response getdefaultresponse(requeststateinfo requeststateinfo) { return new response(a.create(requeststateinfo.gety()), null, null, null); } } i thought of creating reusable executor can have business logic here. me looks cleaner add dependency execute functions in order. chain of responsibility, say. thoughts?
a generic executor like:
import org.apache.commons.lang3.validate; import org.slf4j.logger; import org.slf4j.loggerfactory; import java.util.arraylist; import java.util.list; import java.util.optional; import java.util.function.consumer; import java.util.function.function; import java.util.function.supplier; public class chainedexecutor<t, r> { private final static logger logger = loggerfactory.getlogger(chainedexecutor.class); public final list<function<t,optional<r>>> handlers; private r defaultresponse; private chainedexecutor() { this.handlers = new arraylist<>(); } public r execute(t request) { return execute(request, optional.empty(), optional.empty()); } public r execute(t request, function<t, r> defaultsupplier) { return execute(request, optional.of(defaultsupplier), optional.empty()); } public r execute(t request, consumer<exception> exceptionhandler) { return execute(request, optional.empty() ,optional.of(exceptionhandler)); } public r execute(t request, function<t, r> defaulthandler, consumer<exception> exceptionhandler) { return execute(request, optional.of(defaulthandler), optional.of(exceptionhandler)); } public r execute(t request, supplier<r> defaultsupplier) { final function<t, r> defaulthandler = (input) -> defaultsupplier.get(); return execute(request, optional.of(defaulthandler), optional.empty()); } private r execute(t request, optional<function<t, r>> defaulthandler, optional<consumer<exception>> exceptionhandler) { optional<r> response; try { (final function<t,optional<r>> handler: handlers) { response = handler.apply(request); if (response.ispresent()) { return response.get(); } } } catch (exception exception) { handleorlog(exceptionhandler, exception); } return getdefaultresponse(defaulthandler, request); } private void handleorlog(optional<consumer<exception>> exceptionhandler, exception exception) { if (exceptionhandler.ispresent()) { exceptionhandler.get().accept(exception); return; } logger.error("unhandled exception occurred while executing handler chain. developer error."); } private r getdefaultresponse(optional<function<t, r>> defaulthandler, t request) { if (defaulthandler.ispresent()) { return defaulthandler.get().apply(request); } return getdefaultresponse(); } public r getdefaultresponse() { return defaultresponse; } public static class builder<t, r> { private final chainedexecutor<t, r> chainedexecutor = new chainedexecutor<>(); public builder(list<function<t,optional<r>>> handlers) { validate.notnull(handlers); (final function<t, optional<r>> handler: handlers) { validate.notnull(handler); handlers.add(handler); } } public builder() {} public chainedexecutor.builder<t, r> withhandler(function<t, optional<r>> handler) { validate.notnull(handler); chainedexecutor.handlers.add(handler); return this; } public chainedexecutor.builder<t, r> withdefaultresponse(r defaultresponse) { validate.notnull(defaultresponse); chainedexecutor.defaultresponse = defaultresponse; return this; } public chainedexecutor<t, r> build() { validate.istrue(!chainedexecutor.handlers.isempty()); return chainedexecutor; } } } edit
i ended not doing this. thought crossed mind. felt make more cryptic colleagues. though, removing if/else. went oops way (polymorphism).
from point of view first version looks simpler , easier read, go if there no win in performance in second version. extraction of generic method good, seems not straight forward because each statevalidator looks different.
Comments
Post a Comment