3
\$\begingroup\$

I need to create a class which acts as a paginated list. My use case is like this:

I am writing a library which makes a service call to fetch a List<obj>. Since the List<> can be large, the service expects me to call with a PAGE_SIZE. The service has also provided me with a TPS of x which I need call it with.

I need to do two things:

  • Rate Limit my calls to the Service
  • Expose a PaginatedList<> from the library so that any client can use the Library and rate limit as per their TPS.

I was wondering whether the way I am implementing this PaginatedList is correct or if there is a better way.

public MyPaginatedList {
  private static final PAGE_SIZE = 50;
  private ServiceClient client;
  private int pageToProcess = 0; //to keep track of which page we need to process next
  private List<Data> data; // this is what the library exposes to outside world
  public MyPaginatedList(ServiceClient client) {
    this.client = client;
  }

  public MyPaginatedList getDataList(){
    Servicerequest request = new ServiceRequest();
    request.setIndex(PAGE_SIZE*pageToProcess++); // fetch the next set of items

    this.data = client.APICall(request);
    return this; // we return the current object
  }
}

The public API exposed by this Library would use MyPaginatedList like this:

class Library {
  private ServiceClient client;

  public Library(ServiceClient client) {
    this.client = client;
  }
  public MyPaginatedList fetchData() {
    return new MyPaginatedList(client).getDataList();
  }
}

Any client of this library can use MyPaginatedList and make Rate Limited Calls like this.

MyPaginatedList resultData = new Library().fetchData();
while(true) {
  for(Data d: resultData.getData()) // loop over the list exposed by MyPaginatedlist
  ...
  }
  RateLimiter.acquire(); // use guava rate limiter, would block till a token is available
  resultData = resultData.getDataList();
  if(resultData.getData().isempty()) {
    break;
  }
}

Edit:-

3 straightforward approaches come to my mind -

  1. The one described above. Where the PaginatedList would expose a way to fetch next pages (batch of elements or List<>)
  2. Let the Library expose 2 API's -

    PaginatedList fetchData() -> client would call this one initially
    PaginatedList fetchNextPage(PaginatedList) -> client would call this for sub-sequent pages

  3. This one is slightly modified version of approach 2. Let library expose a modified fetchData()

    PaginatedList fetchData(PaginatedList list) -> initially client would need to create a new PaginatedList(), which would initialize it's state like pageToProcess = 0; This API kind of takes in the metadata associated with the service call to identify which page to get next

Any more suggestions or variations would be appreciated!

\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Review! I hope you get some good answers. \$\endgroup\$
    – Phrancis
    Commented Apr 6, 2016 at 17:08

1 Answer 1

1
\$\begingroup\$

spring has the concept of paging and they do something like this:

public Page findAll(Pageable pageable);

where the Page interface has the following methods (trimmed):

getTotalElements()
getTotalPages()
isFirst()
isLast()
nextPageable()
previousPageable()

it also extends the iterable interface so you can loop like this:

Library library = new Library();
Pageable pageRequest = ...
Page<T> page;
do {
  page = library.getData(pageRequest);
  for (T t: page) {
    log.debug("T is {}", t);
  }
  pageRequest = page.nextPageable();
} while( !page.isLast() );

the most notable difference is that the page being requested is not stored as a state in the client library. by storing the state you make it harder to use the same instance from multiple places in the code.

see http://docs.spring.io/spring-data/rest/docs/1.1.x/reference/html/paging-chapter.html

\$\endgroup\$
1
  • \$\begingroup\$ I think the same Library Instance is still usable in different places, since the fetchData() method returns a new instance of PaginatedList. The reason I kept the List<> in the PaginatedList is to enable the client to call the getter() on it & retrieve the same List<>. \$\endgroup\$
    – AgentX
    Commented Apr 7, 2016 at 5:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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