My understanding of the Protoype design pattern is that it is used when creating an instance of a class is very time-consuming or otherwise complex. Rather than creating more instances, you make copies of the original instance, modifying them as appropriate. I want to apply a similar idea to entity EJBs. Assume I call an EJBHome.findByXXX() method to return a Collection of remote stubs. For example, EmployeeHome.findByLastName("Smith") would return all employees (as represented by remote interfaces to entity beans) with last name "Smith." Now assume that the underlying database query sorts all Smiths by first name. Therefore the Collection returned by findByLastName() has all values sorted by first name. So far so good I hope.
Now assume I want to sort the Collection by some other criterion like employee ID. I could write another findByLastName() method (like findByLastNameIDSorted("Smith")) whose underlying query does a sort of the Smiths by ID rather than first name. But that is potentially an expensive call, and I already have a Collection of all the Smiths. My desire then is to simply sort the Collection the same way I would in conventional Java--by having the objects in the Collection implement Comparable and then calling Collections.sort() on the Collection. I would think that if I I do the following this might work:
1) have Employee (the remote interface) simply extend Comparable
2) have EmployeeBean implement Comparable so that calls to the remote interfaces' compareTo() method summon the beans' compareTo method(). The findByLastName() method will return the Collection already sorted by first name thanks to the query, but sorting the resulting Collection with a properly written compareTo() method will sort the original Collection by employee ID.
Of course, if more than 2 ways of sorting the Collection is desired, then a Comparator can be used rather than implementing Comparable.
So this is my idea. Can someone tell me if this makes any sense or if I am out of my mind?
Thanks so much for reading this disertation! I welcome any insight you might have.
The idea of implementing Comparable won't work because Comparable is not a valid remote interface (it doesn't declare RemoteException in the method's throws clause).
The natural way to do what you want is indeed to use a comparator. However, the Collection instance you get from the finder might not be sortable (e.g., it might not even be order-preserving). So you should duplicate it as an array of some modifiable list implementation and sort that.
Sorry about the "cross-posting," but I wasn't sure which forum was the most suitable for this question. Which would you have chosen?
As for your points, they were excellent, especially the one concerning Comparable's not throwing RemoteException. That totally slipped my mind. However, I am unclear about your point about the Collection returned from the finder. I believe you were suggesting that the Collection returned from the finder is not necessarily ordered in the manner stated by the query. That would make perfect sense, but is that what you meant? Moreover, what do you mean that the Collection might not be sortable? Why would a Collection of Objects ever be unsortable (if a Comparator is available for use as the basis for sorting). Finally, you seem to suggest that the Collection should be transformed into another Collection that can be sorted. Do you mean taking the data in the Employee Collection (to use my example from before) and constucting an array or Collection of what amounts to Value Objects and then sorting that? That would be fine, although I would say off the top of my head that tying those "Value Objects" back to their Employee parents might not be as trivial as it may appear.
And to address your point in the "cross-post," I was obviously not as clear as I thought in indicating how my suggestion is related to the Prototype pattern. My suggestion is certainly NOT an application of the Prototype pattern, but my point was that the motivations for both are conceptually equivalent. A Prototype is used when constructing an object is expensive in some way; thus rather than construct a new object, the previous object is modified when necessary. The motivation for my sorting suggestion is quite similar indeed. Generating a Collection of remote interface stubs is an expensive operation, so if circumstances permit, I would prefer to simply modify that Collection rather than obtain a new one. Hopefully, the similarity is now clear. I am basically abstracting the motivation for a Prototype object to a motivation for a certian course of action.
Well, I look forward to your clarifications and comments, Gal. Thanks so much for your excellent response, and sorry again for the "cross-post."
Thanks for taking the time to mention the cross-post in this thread. I hope all further discussions would continue here rather than on one of the other threads.
To answer your question, I would have chosen either the design forum or the EJB programming forum for this post.
I did not mean that you should create a collection of value objects (although that could also be a good idea in some cases). I meant that you should create a new collection that has the same contents as the collection of stubs. The reason you need to do this is that the collection of stubs may be "unsortable" for two major reasons:
- It may not be a List. Collections which are not Lists are not guaranteed to have a stable order. For instance, Sets (which are Collections) are usually implemented in some sort of a binary tree or hashtable. You can't change the order of the elements in such a Collection: it is defined by the value of the element itself.
- It may be immutable, meaning you can't change it.
Thanks for explaining the prototype analogy. I understand what you meant better now. Note that the analogy still holds: duplicating a collection and then sorting it is probably orders of magnitude quicker than getting a whole new collection using a remote call.
Again, Gal, you make an excellent point. Not all objects that implement Collection enforce order--another important factor that slipped my mind. I think all the holiday revelry is catching up with me. :)
After thinking about it some more and pondering your comments, I can think of two possibilities for a course of action:
1) Copy the original Collection returned by the finder. Call Collections.sort() on the new Collection with a Comparator designed to provide a basis for ordering the remote stubs. Can clone() be called on the original Collection to produce a definitely mutable Collection? Since all concrete implementations of Collection written by Sun implement Cloneable, I guess it would work. However, it might be a bad course of action from an elegance perspective since theoretically it is possible for a class to implement Collection but not Cloneable. Therefore it would be a flawed bit of programming to assume that it does. I would be very interested in hearing your insights on using clone(), especially within the realm of enterprise Java.
2) Create a class as an Adapter (or wrapper) for the remote stubs in the Collection and have that class implement Comparable. Then call Collections.sort() on a Collection of these wrappers.
3) Combine a wrapper class with a Comparator implementation.
It seems to me the third choice may be the best in terms of readability, flexibility, and efficiency.
Thanks again, Gal, and I welcome your insights.
The clone method is not guaranteed to work. In particular, even if you clone a set, you will still get a set and it will still be unsortable.
You can copy the original collection using any number of ways. Probably the easiest would be to use the constructors of Sun's List implementation that take a Collection argument and copy it's content. LinkedList and ArrayList are two possible choices.
If you want to do some processing on the remote stubs (e.g, narrow them) you can simply iterate over the original collection, process each stub, and then add it to the new collection.
I don't see why you need an adapter. Using a Comparator seems very natural to me. There is no reason why the adapter strategy won't work though. It just seems superflous to me.
While the List/Comparable idea has it's merits, one think I think y'all both have forgotten to consider would be the caching of the EJB container. If the first "findBy" is cached, what actually happens when the second "findBySorted" is invoked?
Additionally, sorting on the original Remotes returned by the orginal "findBy" will involve a whole lot of remote calls for field accessors, right?
This is one of those cases where it seems like you'd just have to try both methods, for varying sizes of the cache and data set. Not easy to answer in the abstract - too many dependencies on vendor implementations.
"one THING" ... sheesh
Thanks for your comments, E.A. You raise a good point about vendors' providing means for enabling the "Prototype-esque" behavior I am seeking. My only concern is that such a solution would be vendor-specific and therefore not portable across implementations. Of course, that may not be a concern given the nature of a project. But you are quite right in asserting that the sorting will result in lots of calls to the remote stubs. The benefit though is portability. Moreover, if one is using EJB 2.0, one can use local interfaces to avoid a lot of the marshalling and other overhead required to generate remote method calls. In my case I am using EJB 1.1, but processing speed is not a critical factor. Therefore I will opt for the solution Gal and I have developed--creating a second Collection from that returned by my finder and then sorting that with a Comparator. It's portable, intuitive, and fast enough for my purposes. Of course, as with all things, a different project with different constraints might lead me to pursue a vendor-specific caching solution.
I did have one question though. I haven't looked into caching in any detail, but why would the first finder's cached results be in any way affected by the second finder? They are different method calls entirely that happen to be related only conceptually, and the container is unaware of the relationship. Could you clarify a bit more on this point?
And as for the typo, we all make them, so don't sweat it. We all know what you ment.
I mean.....meant. :)
I figured there would be some issues with clone() as you suggested, so I actually thought that the best way to go is exactly the way you described--constructing a second Collection with the previous one returned from the finder. I'm glad to see that you agree that would be the way to go.
As for using an Adapter, I can see why you would find it superfluous. I just mentioned it because one could have the Adapter class implement Comparable rather than passing a Comparator implementation. Whichever is best depends on the needs of the application, but I just thought I would mention the Adapter strategy for completeness.
Thanks again for your helpful insight.
how about the following way: use a session bean as a facade, and return DTOs containing the data you need. Those DTOs could implement whatever interface you need, incl. Comparable, Cloneable etc. You could also define a custon container class and define different Iterators or Enumerators resp.
Well, Thomas, while writing your own Collection and/or Enumeration/Iterator is an option, I don't see the need. It is a formidable task to write these well, and I don't see what these "custom" objects would offer that the conventional Java classes wouldn't. Perhaps you could elaborate. I would like to hear more about what you had in mind.
And forgive my ignorance, but what the heck is a DTO? I imagine they are essentially Value Objects returned from session bean database manipulations, whereby entity beans and Collections thereof are eliminated from the equation entirely. This goes to the old argument of why use entity beans at all. I am on the traditional side--use entity beans but understand their advantages and disadvantages and code accordingly. Of course, my assumption of what a DTO is might be entirely inaccurate, so please enlighten me.
Thanks for your comments.