7

Today, talking with some colleagues of mine, we were discussing the usage of tuples. The specific problem was: we have an API returning a list of some kind of objects (let's say POJO instances)

public List<Pojo> getPojos()

we decided to use pagination in order to prevent enormous responses

public List<Pojo> getPojos(int page, int pageSize)

the next step was: can we return both the list and a boolean saying if more results are available preventing useless calls?

My approach was to consider the results as a pair of values

public Pair<List<Pojo>, Boolean> getPojos(int page, int pageSize)

Some colleagues say that this approach can hide the meaning of the returned variables because it is not explicit what the boolean means, their proposal was to introduce an "ad hoc" object to handle the response

class PojoResponse{
    List<Pojo> result;
    Boolean hasMore;
}
public PojoResponse getPojos(int page, int pageSize)

In my opinion, this solution breaks the uniformity of the APIs, introduces new classes that should be maintained and, in my opinion, hides the real objective of the method, in this case, return a list of POJOs.

What is your opinion on that problem? Find articles on these "style problems" on the internet is not always so easy.

4
  • 2
    PagedResponse<T> { List<T>, bool } is another alternative. It might be more useful to return the total number of available results rather than just a bool. If you're designing a HTTP Api, you could put something like that in the X-Total-Count header. In fact I like what this guy does, something like PagedResponse<T> { T data; Paganation paging: { int total; int page etc } }. – Nathan Cooper Apr 26 '17 at 23:10
  • For your example I think the Boolean is a bad choice no matter where you stick it or how you name it. Far better would be a response that includes and integer representing the total # of items. – Paul May 17 '17 at 2:04
  • at some number of pages, sending all those trues is slower than asking for one page too many – Ewan May 17 '17 at 12:48
  • "not explicit what the boolean means" can almost always be solved by replacing the boolean with an enum. – Idan Arye May 17 '17 at 13:07
21

I think using Tuples in API boundaries is troublesome. If you just have one, it's easy enough to remember what Item1 is, but when you have 20, it gets harder to remember.

The question is is it worth it having names for my parameters?

Making little classes is somewhat annoying, but it's a small price to pay to gain the ability to name the items. Now, your IDE can remind you which is what. The maintenance cost of the class is minimal, the up front cost of defining it is low, but the mental cost is remembering what's what becomes 0.

2
  • With C#7, you can provide names for the items of a Tuple. Then things might change (still using C#6, I generally avoid Tuples because of readabiltiy concerns). – Bernhard Hiller Apr 27 '17 at 8:00
  • Hopefully record types are integrated into C#8, and with it primary constructors. I don't know whether Java will have a similar feature, maybe with Panama? – Stefan Hanke Apr 30 '17 at 3:25
3

In my opinion, neither is correct. A better solution would be to have a PojoRetriever class which implements Paginator, which handles the pagination and keeps track of the state (i.e. page size, current page) so that the client code can simply call a method to determine if there is more data.

A lot of the time when you feel the need to return multiple values, you could (should, imo) use an object to store/handle them.

2
  • This only works for local APIs. If your API is a web interface the return values should be data only. – Idan Arye May 17 '17 at 12:56
  • @IdanArye you can provide a URL for the next page within the response, so the user of the API can easily call the next page within the API. If I remember correctly, Twitter does this. – Jelle May 17 '17 at 13:09
3

Your query for information will need to return both count of matching items as well as the current payload of information. Typically what I do for this scenario is to create a generic object that handles the pagination logic:

public class Page<T>
{
    public int Total {get;}
    public int PageSize {get;set;}
    public int PageNum {get;set;}
    public bool HasNext {get;}
    public bool HasPrevious {get;}
    public List<T> Records {get;}
}

Given the current page size and page, and the total number of records that apply to the query, everything else could be calculated. The container class Page<T> handled all the pagination logic, and it could be reused for any type we needed.

0

If the only two options you have are the ones you listed, then I have to agree with @whatsisname and suggest that you go with the class.

However, in this case, I believe that you might have a better solution:

int numberOfRemainingItems getPojos(int page, int pageSize, out List<Pojo> retrievedPojos)

You redesign your getPojos method to return the list of pojos as an out parameter, and the return value of the method is the number of remaining Pojos in whatever repository you are getting them from. Of course, there are variations to this solution:

  • You could return a bool if you just know whether the repository is empty or not, but do not know how many pojos are left there
  • The List retrievedPojos parameter does not have to be an out parameter, in which case you would have to instantiate and initialize the list before the method is called - I would not suggest this for multiple reasons

Generally, whenever you have a method that returns more than one value, either refactor it to use two methods, or return the values using out parameters.

-2
class PojoResponse{
    List<Pojo> result;
    Boolean hasMore;
}

is a glorified version of

Pair<List<Pojo>, Boolean>

As long as your team agrees to one approach and sticks with it, I don't see it as an issue to debate too much.

However, I would recommend a strategy that uses neither.

Add another function

Boolean hasMorePages(int page, int pageSize);

Then use that function call instead of using the hasMore member variable of PojoResponse.

Caveat If hasMorePages is compute intensive, this might not be the better option.

4
  • Actually the "hasMore" improvement was introduced because each call to the API is a HTTP request and the read operation performed over the db is actually intensive – Cattani Simone Apr 26 '17 at 21:50
  • We are actually evaluating to simple return the list and eventually perform a useless one more call at the end, but I would consider what could be the best "style" solution using the "hasMore" approach – Cattani Simone Apr 26 '17 at 21:53
  • @CattaniSimone, making another HTTP request might not be bad unless you are making hundreds of such requests per minute. – R Sahu Apr 26 '17 at 21:58
  • @CattaniSimone: and why can't your client API just buffer the "hasMorePages" information from the last HTTP request for a List<Pojo>, so hasMorePages just returns this buffered value? – Doc Brown Apr 27 '17 at 6:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.