To answer Josh's comment on why the implementation of list pagination is quite long, here's a brief background. This post may be beneficial to Java/OOP newbie, in terms of thinking in an OOP and working in a TDD (Test Driven Development) way.
It's all started with the modeling of what a Page object may look like. Well, it has to have a page number and the contents (list of records) itself. Then, we need to define what can we do with a Paginating List. We need to be able to get the first page, the last page, the previous & next page based on the current Page object. They're all described within the Paginating interface.
Then, I added two more information inside the Page class, which can be provided easily by any implementation class of Paginating interface, i.e. the total number of page and the total number of records shown per page. The addition of this two properties to the Page class proves to be a very beneficial to the PaginatingImpl class itself.
Now, before we dive into why the PaginatingImpl looks like it is, one thing needs to be restated here. The PaginatingImpl is just one example of how we can implement the Paginating interface. You can provide your own (which can be a very different) implementation. The only requirement for the implementation is only to support all of the methods declared in the Paginating interface.
When developing the PaginatingImpl, one thing I keep in mind is that I wanted this class to be immutable and thread-safe. By being an immutable class, the PaginatingImpl itself almost automatically becomes thread-safe and therefore sharable & cache-able. Hence I say that this solution can be used for a web application or a non-web application.
The implementation code within the PaginatingImpl itself is straightforward and is mainly driven by the test code. If you haven't download the test class, then I suggest you download them here first. This is the key driver which drives the PaginatingImpl & Page classes to evolve into what they are currently.
For example, to ease the assertion codes within the PaginatingTest class, I added the equals() and hashCode() methods in the Page class. Then, after performing several test cases, I remembered that the ArrayList implementation does not override the equals() and hashCode() method from Object class. Therefore, I may get an inconsistent (and invalid) result when comparing two List objects which contain exactly the same sequence of the same elements.
Hence I added two more methods, i.e. isListEqual() and listHashCode() which are merely my implementation of the equals() and hashCode() method for the List class. (Note: BTW, to all the Java newbies reading this post, if you don't know why equals() and hashCode() need to be overridden correctly at the same time, please leave me a comment, I'll try to post the explanation for them. Or you can Google them or read in your own javadoc API or search it in Javaranch's SCJP Forum.)
The toString() method was added in the Page class merely to support easy debugging which I used exactly only once before sharing this code to you all. So the method definitely has its uses.
If you read carefully enough within the PaginatingImpl, you may find strange arithmetic operation, e.g. minus 1, or minus 2, or plus 1. Well, those are small logic but they're crucial to prevent bugs occuring from this rather small implementation of List Pagination.
If you want to test yourself, just take three of my classes, Page, Paginating & PaginatingTest, then develop your own PaginatingImpl class. It's a good challenge, and you may just develop a better implementation than mine, and enrich the PaginatingTest with many more test cases.
I sure would love to hear if anyone would want to take the challenge. It's a good start to all three elements of today's software development, i.e. Java, OOP & TDD. You need to download JUnit libraries though, but it should be no big deal.
Designing just another solution maybe is an easy task, but designing a good, robust & extensible one maybe is not such an easy task.. :D
Anyway, let me know what you guys think.
I sure hope that this post is even more useful when compared to my previous one.
Further Reading:
Test Driven Development by Example
Head First Design Patterns
Design Patterns
JUnit in Action
JUnit Recipes
Dear Eric,
ReplyDeleteThis is good article.
From the newbies perspective, can you explain more, specially on why this class (or every class) must be immutable and best practice how to using JUnit ?
Thanks
Halo, saya menikmati membaca coding anda seperti anda menulisnya. Codingnya sangat menarik. Dalam semangat ingin saling belajar,... ada beberapa poin yang menurut hemat saya bisa di improve.
ReplyDelete1. Primitif attribute sebaiknya diganti dengan Object. Page object umumnya ditransfer via network. Serializable object menjadi solusi lebih elegan.
2. attribute page size pada PaginatingImpl tidak dibutuhkan. Mengingat Paginating akan digunakan oleh beberapa user, ada user yang menginginkan halaman ditampilkan 20 row, tetapi ada juga yang menginginkan 60 row sekaligus. Dari pada setiap klien diharuskan memiliki instant Paginating masing2 lebih baik paginating tidak memiliki state selain berfungsi sebagai business logic.
3 Method getFirstPage dan getLastPage bisa ditambahkan argument currentPage sehingga mengambil pageSize dari si currentPage.
4 Method getFirstPage dan getLastPage bisa dipanggil dengan memberikan nilai default pageSize.
5 Method getPageTotal page dan iterateFrom bisa mengambil page size dari currentPage yang di passing.
Dear Arif,
ReplyDeleteIt's nice to see you put some great inputs to make the sample implementation of Paginating interface, i.e. PaginatingImpl more flexible.
It's actually a good idea to release the constructor's dependency to the pagesize atttribute, to allow greater reusability when the same instance (containing the same list of records) is about to be shared with multiple users having different preferences for the page size.
Regarding the serialization on primitive data types, I believe Java supports it automatically, without me having to change them into their wrapper classes. Please CMIIW.
Anyway, thanks a lot for the input