Tuesday, August 10, 2010

"RowSet Wrapper List Strategy" and reality

"RowSet Wrapper List Strategy" and reality....

From Core J2EE Patterns: Best Practices and Design Strategies.

While the Transfer Object Collection strategy is one way to implement your finder methods,it might prove expensive if the query returns a large set of results and you end up creating a large collection of transfer objects, which are used sparingly by the client. Clients typically search and use the first few result items and discard the rest. When an application is executing a query that returns a large set of results, the RowSet Wrapper List strategy might be more efficient though it requires additional work and adds more complexity.

Code presented in the book is focused on RowSet wrapper and the code has 61+73 lines just as sample, and is
suitable only for RowSets. Get the book, read the samples, it is nice reading. However:

Using generics and AbstractList we can rewrite the original to 20lines long
"General Wrapper List" implementation, capable to transform any List<O> into List<E> by supplied convert method.


import java.util.AbstractList;
import java.util.List;

public abstract class WrappedList<O, E> extends AbstractList<E> {
public abstract E convert(O original);
private List<? extends O> original;
public WrappedList(List<? extends O> original) {
this.original = original;
// TODO: defensive copy ?, performance will suffer,
// and here we code for performance
// this.original=new ArrayList<O>(original);
}
public E get(int index) {
// TODO: caching ?
return this.convert(original.get(index));
}
public int size() {
return original.size();
}
}


This code has the same "quality" as the one presented in the book, and creates:
unmodifiable list (unexpected that view modifies list) which is
indirectly mutable (for sake of performance we do not make defensive copy of supplied original list),
that produce not equals (!=) instances with subsequent get(index) for the same index.

Using this to produce RowSet Wrapperfrom book, requires subclasing (anonymous inner class)
and this extra code (from book):

21 public Object get(int index) {
22 try {
23 rowSet.absolute(index);
24 } catch (SQLException anException) {
25 // handle exception
26 }
27 // create a new transfer object and return
28 return
29 TORowMapper.createCustomerTO(this);
30 }

moved to overrided convert method.

This way we have implemented optimized collection suitable to be used inside
c:forEach or jsf:table tags supporting start and end attribues.

Imagine situation where you have 1000 records from model and need to view only 10 of them.


<c:forEach items="${list}" var="l" begin="0" end="9">....


If list is constructed with the usuall "Value Object Collection" (copy and transform all)
it caouses 1000 calls to convert instances
of Value Object of type O into Value Objects of type E,
and creates 1000 of new instances of E.
In out case only 10 calls will be made and 10 new objects will be created.

Surprise:

<c:forEach items="${list}" var="l" begin="10" end="19">....


I would expect 10 again. But wrong it is 20 !.

<c:forEach items="${list}" var="l" begin="990" end="999">....

Will be 1000.

Thanx for clever code inside javax.servlet.jsp.jstl.core.LoopTagSupport.

Another idea of "too early full copy" arrays can be found here:
org/apache/taglibs/standard/tag/common/core/ForEachSupport.

Conclusions:

Even if you try to design with performance in mind,
even if you try to design by patterns and strategies,
choice of other libraries can eliminate
or even negate your efford.
.

Maybe, we will fix this soon, stay tuned.

No comments:

Post a Comment