General musings on programming languages, and Java.

Saturday, April 09, 2005

A new design pattern (Farm) and a nifty use of annotations.

It struck me that a difficulty with using interfaces and factories in Java is that you need to know which factory goes with which interface. A naming convention can help here, but it might not be consistent. Instead, I suggest that you annotate your interface, to know about a default implementation. This immediately brings up "Interfaces shouldn't be bound to their implementations!!! This is unpure, evil code!!!". When you are navigating code, trying to find implementations for a particular interface can be annoying and time-consuming. I have set out here the most cohesive way I can think of for finding out which class to use as a default implementation when all you have is an interface. It doesn't 'bind' the implementation to the interface. It neither 'binds' nor mentions the implementation. The interface 'knows about' a factory. I experimented with non-annotation ways of doing this, such as static blocks in interfaces, xdoclet-based solutions (well, I thought about xdoclet), but I decided they were all ugly. I think what I have is the most elegant but pragmatic way. Time for some code. @DefaultFactory(BlobbyFactory.class) interface Blobby {         void doSomething(); } This says that BlobbyFactory is the default factory for the Blobby interface. So when you want a Blobby, you instantiate BlobbyFactory and call its newInstance method. As a convenience, I wrote a Farm class that you can use as follows: Blobby blobby=(Blobby)new Farm().newInstance(Blobby.class); The reason newInstance is not a static method is that you may wish to override the default factory, in some part of your application. So the unit testing part of your application might want a mock object instead of whatever BlobbyFactory gives you (actually a BlobbyImplementation in this case). Farm farm=new Farm(); farm.setFactory(Blobby.class,new MockBlobbyFactory()); then elsewhere: Blobby blobby=(Blobby)farm.newInstance(Blobby.class); In effect, a Farm is a Map with some nice methods for abstracting the rubbish away, and for using annotations when there is no map entry. The code is all available at http://lavender.cime.net/~ricky/farm.zip To build just what you need for using it, run 'ant farm.jar' and farm.jar will be produced. It's released under the BSD licence, because I want you to be able to use it. If there is some problem with my licence choice, let me know. To build an executable example jar, run 'ant farm-example.jar' and farm-example.jar' will be produced. The ant build is not very advanced, so you can work out how to do it using just javac if you don't want to use ant. javac com/rickyclarkson/farm/*.java com/rickyclarkson/farm/example/*.java java com.rickyclarkson.farm.example.Main should work, but I haven't tested that. (Use ant!) This isn't just a Java design pattern, I've also implemented it in C just for fun. Yes, I have a strange idea of fun.

5 comments:

Anonymous said...

Uhm, why not using spring or another ioc framework for managing factories and component lifecycle?

Ricky Clarkson said...

Because that adds more complexity than I need. I can learn a 3-class (well, one interface, one class, one annotation) API in a few seconds, but I would guess learning Spring would take a lot longer. I don't generally need my entire component lifecycle to be managed, just the construction of a component.

I don't know much about spring, maybe I'm assuming (ASS-U-ME) too much.

I thought spring was for J2EE, maybe I got confused.

Anonymous said...

First off, Spring is not just for J2EE. Secondly using Spring for this is frickin nuts. This is indicidive of the "Hey ma, I want to increment an integer" - "Well son, you've gota use spring!" for this.

As for needing to know what the "default" implementaion is, well why? Any decent IDE can show you very quickly what implementations you have. If you need to mock/stub out inteferaces for tesing, use mocks. If you genuinely have multiple implementations of an interface, then what is the default, and why does it matter?

Ricky Clarkson said...

> "Secondly using Spring for this is frickin nuts."

I thought so too, even in my state of ignorance.

> As for needing to know what
> the "default" implementaion is,
> well why? Any decent IDE can
> show you very quickly what
> implementations you have.

Because I don't want people who use the interface to *have to* know about the implementations. I can even keep the implementation package-private.

I could even keep my factory package-private in a 1.4-compatible version I made (the problem is that the annotation refers to the factory's class, not an instance, as annotation members can only be classes, enums, strings and some other similar things). But either way, the user of the interface just needs to know about Farm and my interface. Hopefully I can be pretty consistent and use Farm for everything that I do, thus reducing the number of API look-ups that the client programmer does.

Not everybody uses an IDE. I do use Eclipse most of the time, but at home I use vim, and when I use a machine outside my office I ssh in and use vim.

Eelco Hillenius said...

I often put the default implementation of an interface as a public static class in the interface itself. Sometimes that would get too messy, but often, for default implementations that don't encompass a lot, this imo is the neatest way to keep things structured.

Blog Archive

About Me

A salsa dancing, DJing programmer from Manchester, England.