Skip to main content

Designing flexible method calls

Posted by fabriziogiudici on March 25, 2008 at 6:54 AM PDT

In the latest months I've not blogged a lot about technical topics, yet
I'm still intensively working of stuff about my opensource projects
(well, of course I also work on other stuff, but I can't blog on that
because of NDAs with customers).


The point is that 95% of the development of blueMarine has been
focused, since the latest XMas, on the Metadata component. It is a
thing to import, store and query metadata extracted from photos and
other documents, and I put some demanding requirements on it, including
the capability of being pretty modular and extensible. It turned out to
be a meta-persistence facility on its own for JavaBeans, that will be
even useful for futher developments. Of course I didn't reinvent the
wheel, the thing still uses JPA + Derby, but adds a layer of services
that are focused on the special kind of JavaBeans I'll be using.



The design has been changed two or three times so far, and I'm pretty
happy this is the first time I went in mostly-TDD mode for blueMarine
(thus having 90%+ test coverage since the beginning), since I was able
to perform dramatic changes with no fear of breaking anything. After
some integration tests passed yesterday, I think that the design is
almost complete, so I started consolidating and documenting (and also
removing some possible over-engineering).



So here it is a quick technical post about it and I like to have some
feedback before I freeze the APIs.



As I said, the facility should be extensible, and this implies having
stable APIs that can be used by third parties. Stable APIs must be
however extensible as far as it's possible. A style="font-family: monospace;">Metadata
interface is central in the facility, as it manages a collection of
metadata items associated to a given object. Among other operations, style="font-family: monospace;">Metadata
exposes a findOrCreateItem()
and a storeItem().
The implementation is SPI-based, that is independent implementations of
providers will be activated on request. Providers might be able to read
data from a photo file, read or store from/to an XMP file or work
directly with the database. I can't predict in advance which kind of
providers will be able in future, including some hopefully provided by
third parties, so I'd like to have an open door so the application can
pass some special parameter to them.



So, everything boils down in how to construct a method whose arguments
are flexible enough, yet controllable, and also keeping an eye on
readability of the code. Up to a few months ago, my usual way was to
avoid fixed parameters such as:



public void
myOperation (int param1, String param2, float param3);




in favour of the use of a specific JavaBean and a method with a single
parameter:



public class
MyOperationParameters


{ style="font-family: monospace;">
 
 public void setParam1 (int v)...
style="font-family: monospace;">
 
 public int getParam1()...
style="font-family: monospace;">
} style="font-family: monospace;">


MyOperationParameters
p = new MyOperationParameters();
style="font-family: monospace;">
p.setParam1(...); style="font-family: monospace;">
public void
myOperation (p);




In this way you can add new properties to style="font-family: monospace;">MyOperationParameters
and make sure that they have the proper default values.  Not
bad, but it make it worse the readability of the caller code. Of
course, you could initialize MyOperationParameters
properties in the constructor and/or use a fluent interface, etc... But
even worse, from the design point of view, is that style="font-family: monospace;">MyOperationParameters
becomes a focal point and any eventual extension (e.g. adding new
parameters) performed by added code must use subclassing, which is evil
in this case.



This time I opted for a different solution. For instance, the method style="font-family: monospace;">storeItem() is
declared as:



public
int storeItem
(MetadataItemHolder holder, Metadata.StoreOption...
options);




where holder
is a fixed and mandatory parameter; all the remaining stuff is a
varargs of classes implementing  style="font-family: monospace;">StoreOption,
that are straigthly passed to SPI implementations.  style="font-family: monospace;">StoreOption is
just a tagging interface, that is it's empty, only used to have a
minimal syntax cheking from the compiler (so I can't pass arbitrary
objects). Then I have some concrete classes:



public interface
Metadata


  { style="font-family: monospace;">
   
public static interface StoreOption
style="font-family: monospace;">
     
{


     
}




   
public static enum StorageType implements StoreOption
style="font-family: monospace;">
     
{


       
ANY_TYPE


         
{


           
public boolean includes (final StorageType sourceType)
style="font-family: monospace;">
             
{


               
return true;


             
}


         
},


       


       
INTERNAL


         
{


           
public boolean includes (final StorageType sourceType)
style="font-family: monospace;">
             
{


               
return sourceType == INTERNAL;
style="font-family: monospace;">
             
}


         
},


         


       
EXTERNAL


         
{


           
public boolean includes (final StorageType sourceType)
style="font-family: monospace;">
             
{


               
return sourceType == EXTERNAL;
style="font-family: monospace;">
             
}


         
};


               


       
public abstract boolean includes (final StorageType sourceType);
style="font-family: monospace;">
     
}




   
public static enum ReplaceOption implements Metadata.StoreOption
style="font-family: monospace;">
     
{


       
DONT_REPLACE,


       
REPLACE


     
}




   
public static class OriginFilter implements StoreOption
style="font-family: monospace;">
     
{


       
private final String name;
style="font-family: monospace;">
       


       
public OriginFilter (final String name)
style="font-family: monospace;">
         
{


           
this.name = name;


         
}


       


       
public String getName()
style="font-family: monospace;">
         
{


           
return name;   
style="font-family: monospace;">
         
}


     
}


 
 ...


}



As you can see, some of the classes have their own logic, as for the style="font-family: monospace;">includes()
method in StorageType
(in contrast, adding several specific pieces of logic to a central style="font-family: monospace;">MyOperationParameters
would quickly turn it into a "small" fat class, if you pardon the pun).



With the proper static import, I can call:



metadata.storeItem(holder, INTERNAL, new
OriginFilter("Database"), REPLACE);
style="font-family: monospace;">


which seems pretty readable and self-documenting to me. New SPI
implementations can define their own parameter types by just
implementing StoreOption.
 Method implementations retrieve the arguments by a special
lookup static method such as findOption()
in the example below (which also provides the default value, just to
avoid extra checks for null
values):



   
public void storeMetadataItem (final
MetadataItemHolder holder, final StoreOption...
options)


     
throws Exception


     
{


       
logger.fine(String.format("storeMetadataItem(%s, %s)", holder,
Arrays.toString(options)));
style="font-family: monospace;">
        ReplaceOption
scratchOption = MetadataSpiUtils.findOption(options, DONT_REPLACE,
ReplaceOption.class);


       


 
      ...
style="font-family: monospace;">
 
    }
style="font-family: monospace;">


Last but not least, using Arrays.toString()
it's easy to produce a pretty good logging information such as:

12:04:39.736 [main] FINE MetadataItemProviderSuppor - storeItem(PhotoDataObject[V7210188.JPG], [Basic@131], [INTERNAL, REPLACE])


I don't think it's new stuff, and I'm sure people is already using this
style somewhere, but I could not find examples.



So, what do you think?

Comments

All in all this seems fine. You might want to consider a small builder-style parameter object, that gets the mandatory objects at the constructor, and whose setters return itself (this).
This would allow you to write in a DSL-like manner:
new OperationParameters(aHolder).replace(true).type(EXTERNAL)
This would be type-safe, code completion could help (since, for example, type() would get a single parameter of type StorageType) and you could set the default stuff in the constructor. You could even statically import a factory method, and avoid the new, as in: createParameters(aHolder).replace(true).type(EXTERNAL) Look at pages 5-10 here (watch out, it's a PDF): Effective Java reloaded.
I think having a full-fledged builder here is a bit too much, Mr Bloch is suggesting this as he likes the actual objects to be final, which helps with concurrency.
Michael

I used this something like this in my blog while trying to simulate named parameters. I applied it to GridBagLayout. http://aberrantcode.blogspot.com/2008/01/named-paramaters-and-layout-man... I've never seen this used in real code though.