8

I'm developing some code that utilizes generics, and one of my guiding principles was to make it usable for future scenarios, and not just today's. However, several coworkers have expressed that I may have traded off readability for the sake of extensibility. I wanted to gather some feedback about possible ways to resolve this.

To be specific, here is an interface that defines a transform - you start with a source collection of items, and you apply the transform to each element, storing the results in a destination collection. Additionally, I want to be able to return the destination collection to the caller, and rather than forcing them to use a Collection reference, I want them to be able to use whatever collection type they actually provided for the destination collection.

Finally, I make it possible for the type of items in the destination collection to be different from the type of items in the source collection, because maybe that's what the transform does. In my code, for instance, several source items make up one destination item after the transform.

This yields the following interface:

interface Transform<Src, Dst> {
    <DstColl extends Collection<? super Dst>> DstColl transform(
                Collection<? extends Src> sourceCollection,
                DstColl                   destinationCollection);
}

I tried to be all nice and apply Josh Bloch's PECS principle (producer extends, consumer super) to make sure the interface is usable with super- and sub-types where appropriate. The end result is somewhat of a monstrosity.

Now, it would have been nice if I could extend this interface and specialize it somehow. For example, if I don't really care about playing nice with subtypes of the source items and supertypes of the destination items, I could have:

interface SimpleTransform<Src, Dst> {
    <DstColl extends Collection<Dst>> DstColl transform(
                  Collection<Src> sourceCollection,
                  DstColl         destinationCollection);
}

But there is no way to do that in Java. I want to make implementations of this interface something that others would actually consider doing, as opposed to running in fear. I've considered several options:

  • Don't return the destination collection. Seems weird given that you do a transform but get nothing back.
  • Have an abstract class that implements this interface, but then translates the parameters to something easier to use and calls another method "translateImpl()" which has the simpler signature and thus presents less of a cognitive burden for implementers. But then it's weird to have to write an abstract class just to make an interface user-friendly.
  • Forgo extensibility and just have the simpler interface. Possibly couple that with not returning the destination collection. But then that limits my options in the future.

What do you think? Am I missing an approach I could use?

4
  • 2
    Trying to predict a future scenario instead of knowing the use case is not a good idea. This is where the KISS principle is in play.
    – lorus
    Feb 13, 2013 at 4:15
  • 1
    Additionally to the KISS principle, there's also the YAGNI principle suggesting you shouldn't do that.
    – Frank
    Feb 13, 2013 at 6:11
  • @lorus - my idea was to have a "transform" interface that I could use for a variety of transforms, as opposed to one specific case that is tailored to a specific combination of source and target item types. A way of summing up my post is "how do I keep it simple in a manner that wouldn't require significant rework in the future". We're in a typical corporate setting where deadlines are king and I can't just go refactor things unless there is a sound business reason. Same concern applies to YAGNI in Frank's comment.
    – RuslanD
    Feb 13, 2013 at 7:56
  • Instead of Collection<? extends Src> srcCollection, you should use Iterable<? extends Src> Feb 14, 2013 at 13:29

3 Answers 3

3

Guava Collections already provides this functionality and if you can hold off a little longer, Java 8 will provide this also :-). FWIW I think your use of ? extends T is the correct idiom, not much you can do given that Java uses Type Erasure

4
  • Unfortunately it's unlikely I'll be able to convince my team and management to jump straight from Java 6 to Java 8 :) In the meantime, could you please clarify what you mean by "Guava Collections already provides this functionality"? Are you talking about transforming collections? A pointer to a relevant resource would be very useful. Thanks!
    – RuslanD
    Feb 13, 2013 at 0:35
  • I am confused as to why erasure is significant here. Are you thinking of what C# x.0 call in and out generic parameters? / Using Collection<? super Dst> would be ideal here, if it wasn't also returning the same type (presumably the same reference, which isn't really a great idea (I think Java SE 8 will do that for some methods (into))). It's interesting to note that the likes of Collections.fill use wildcard unnecessarily to be more expressive. / IIRC, Java SE 6 finally becomes unsupported this month. Feb 13, 2013 at 2:29
  • 1
    Guava is code.google.com/p/guava-libraries
    – Tyler
    Feb 13, 2013 at 6:52
  • 2
    +1 for Guava - it's excellent and should be part of every Java developer's toolkit (also Joda Time and Joda Money)
    – Gary
    Feb 13, 2013 at 9:21
0

I personally do not see much problem with the way you've defined your interface, however I am used to toying with Generics and I can understand that your coworkers would find your code to be a bit of a mouthful if that is not their case.

If I were you, I would go with the third solution you state: make things simpler at the expense of potentially having to go back to it later. Because after all, maybe you won't have to in the end; or you would end up being able to use it fully at some point, but not enough to make up for the effort you've put into making this interface so generic, as well as the effort your coworkers may have made to wrap their heads around it.

Another thing to consider is how often you use this interface, and at which level of complexity. Also, how useful this interface proves to be in your project(s). If it allows for high reusability of some components (and not just potential, but actual), it is a good thing. If a much simpler interface works as well 90% of the time, you could ask yourself whether for the remaining 10% a complex interface would prove useful or not.

I do not think it would be a very good idea to not use super and extend, though if you can do without them for the moment, I am sure your colleagues won't mind seeing them disappear. However, do you really have so many situations where you need to change the type of Collection as well?

Some already given advices are really good, and I agree with Frank about following the YAGNI principle unless you have very good reasons not to. You can still change the code when the need for more complexity will arise, but it is not of much use to develop things you are not pretty sure will be used soon. Also, Martijn's advice of using Guava is one to consider seriously. I haven't had the opportunity to use it yet, but I have heard a lot of good about it, including in a presentation where the Transformer pattern got discussed (you can watch it online on InfoQ if you are interested).

As an aside, wouldn't it be better in your current interface to have destinationCollectionbe of type Class<DstColl>?

0

I like to wrap Java collections. The proper encapsulation can really help. This pattern does need to be related based on type, but greatly leaves every line of code unaware of the type of collection.

For example suppose there is a list of products. The product class would be immutable and have a few utility methods in it. The product list class can add one product or another list to it. If you wanted to run a method over the whole list, it would be in the product list class. There is a method that would take a filter list class and give a new product list with those filtered. The filtered would be deleted from the original.

There is a filter interface that decides if ONE product passes the filter. There would be a filters list class that has a list of filters and implements the interface by requiring ALL filters to pass a product for the product to pass.

Funny thing is I can a stack and change it to a linked list with no noticeable changes like this. It can loop and do conditionals very noticeable and simply. So it dresses up imperative code and adds flexibility. And is orgonized well.

1
  • Um, do you realize you can do all that in a few lines of code with standard Java collections, streams and lambdas? Having a special "product list class" would be a really, really horrible waste of time that creates nothing but maintenance headaches. Nov 17, 2016 at 23:06

Your Answer

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

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