java - paginated calls on a service method without changing signature of original method -
i have use case need add pagination inputs (viz. page number , page size) existing service calls (that returns list of results) without changing existing signature (because break existing clients). 1 way of achieving set inputs in threadlocal , have implementation read threadlocal , pagination logic. code point of view, like:
try { paginationkit.setpaginationinput(pagesize, pagenumber); // set threadlocal list<specialobject> results = specialservice.getspecialobjs(); //results count equal pagesize value } { paginationkit.clearpaginationinput(); // clear threadlocal }
from client's point of view, not @ elegant , wanted wrap functionality better syntactic sugar. there 2 approaches had in mind , wanted know if generic enough use case has been solved pattern elsewhere. there many such services , trying put decorator each of services not desirable.
approach 1: mockito style of mockito.when(methodcall).thenreturn(result)
kind of sugar. code may like:
specialservice specialservice = paginationdecorator.prepare(specialservice.class); // spy capable of forwarding calls actual instance list<specialobject> results = paginationdecorator.withpagesize(pagesize).onpage(pagenumber).get(specialservice.getspecialobjs()).get(); // get() added clear threadlocal
i tried borrow code mockito create spy, ongoingstubbing<t>
interface quite intertwined in subsequent call chaining/creation code , smelling of should avoid.
approach 2: use java.util.function capture method call , accept 2 additional parameters pagenumber , pagesize play threadlocals. code may like
list<specialobject> results = paginationdecorator.withpaging(specialservice.getspecialobjs(), pagesize, pagenumber);
paginationdecorator.java:
public static list<t> withpaging(function<u, list<t>> call, int pagesize, int pagenumber) { try { paginationkit.setpaginationinput(pagesize, pagenumber); // set threadlocal return call.apply(); // clearly, missing here! } { paginationkit.clearpaginationinput(); // clear threadlocal } }
i not able formulate how use call correctly here.
can please tell me :
- which of these 2 better approach
- if available recipe elsewhere using different approach
- suggest ways forward implement approach 1 or 2. #2 seems cleaner me (if works).
feel free critique approach , in advance read!
p.s.: liked iterator recipe in question, primary problem of syntactic sugar still desired.
your second variant not work, because using wrong interface (function
expects input argument) , no syntax create function instance, ordinary invocation expression.
you have several choices
use
supplier
. interface describes function without parameters , returning value.public static <t> t withpaging(supplier<t> call, int pagesize, int pagenumber) { try { paginationkit.setpaginationinput(pagesize, pagenumber); // set threadlocal return call.get(); } { paginationkit.clearpaginationinput(); // clear threadlocal } }
instead of insisting on return
list<t>
, allow return type raises versatility. includes possibility returnlist
of something.then, may use either, method reference
list<specialobject> results=paginationdecorator.withpaging( specialservice::getspecialobjs, pagesize, pagenumber);
or lambda expression:
list<specialobject> results=paginationdecorator.withpaging( () -> specialservice.getspecialobjs(), pagesize, pagenumber);
stay
function
, allow caller pass required argumentpublic static <t,r> r withpaging( function<t,r> call, t argument, int pagesize, int pagenumber) { try { paginationkit.setpaginationinput(pagesize, pagenumber); // set threadlocal return call.apply(argument); } { paginationkit.clearpaginationinput(); // clear threadlocal } }
now, caller has provide function , value. since intended method instance method, receiver instance can treated function argument
then, function might again specified either, (now unbound) method reference
list<specialobject> results=paginationdecorator.withpaging( specialservice::getspecialobjs, specialservice, pagesize, pagenumber);
or lambda expression:
list<specialobject> results=paginationdecorator.withpaging( ss -> ss.getspecialobjs(), specialservice, pagesize, pagenumber);
there alternative both, resorting
autocloseable
, try-with-resource, rathertry…finally
. define helper class as:interface paging extends autocloseable { void close(); static paging withpaging(int pagesize, int pagenumber) { paginationkit.setpaginationinput(pagesize, pagenumber); return ()->paginationkit.clearpaginationinput(); } }
and use like
list<specialobject> results; try(paging pg=paging.withpaging(pagesize, pagenumber)) { results=specialservice.getspecialobjs(); }
the advantage not break code flow regarding intended action, i.e. unlike lambda expressions may modify local variables inside guarded code. recent ides warn if forget put result of
withpaging
inside propertry(…)
statement. further, if exception thrown , 1 occurs inside cleanup (i.e.clearpaginationinput()
), unlikefinally
, secondary exception not mask primary 1 recorded viaaddsuppressed
instead.that’s prefer here.
Comments
Post a Comment