Class organization, in particular method signature patterns, can provide many benefits to the developers. Primarily, we live in a day and age of the beautiful IDE with the glorious auto completion functionality. I think I hit ctrl+space more times a day than you can imagine. Utilizing patterned method signatures give the developer the opportunity to find all methods of a certain type quickly.
Let’s look at some basic functionality encountered on a daily basis.
- Finder methods – these are typically data retrieval methods designed to return a collection of objects to the caller. This is the most common type of method and has many different permutations.
- Save Methods – Save methods are typically used to persist data or state to some data store. I typically write save methods so that they dynamically handle the logic regarding whether the data needs to be inserted or updated.
- Delete Methods – You guessed it.
- Run methods – Run some other process in the background. This usually involves a stored procedure, threading, etc.
Let’s look at some method signature templates for these types.
Finder Methods
public Interface findInterface(Object var1, Object var2);
public Collection<Interface> findInterfaces(Object var1, Object var2);
You’ll notice the return value of the method is a collection of interfaces and that interface name should be used in the findXXX method name. What does this do? Developer can locate all finder methods on an interface by typing “fin” and then auto completing. This also allows the developer to find a particular methods associated with the interface they are working with. Let’s say we have an interface representing an energy deal. Here’s what it would look like.
public Deal findDeal(long dealId);
public Collection<Deal> findDeals(String energyType);
public Collection<Deal> findDeals(String counterpartyType);
Ok, so this is a bit different from what you might have seen in other development environments. For example, some shops might go with a pattern of public Collection<Deal> findDealsByEnergyType(String energyType); and include a description of the parameters in the method signature. Where this breaks down is when you go work for an ACTUAL development shop that has a bit more complicated interface. Can you imagine writing a method called:
public Collection<Deal> findDealsByPriceCounterpartyIdFlowDateStartDateEndDatePipelineId(Double price, long counterpartyId, Date flowDate, Date startDate, Date endDate, long pipelineId);
This could easily be changed to:
public Collection<Deal> findDeals(Double price, long counterpartyId, Date flowDate, Date startDate, Date endDate, long pipelineId);
Why? The parameter descriptions are RIGHT THERE. Make the parameter names descriptive and kill two birds. Not too mention the benefits of camel casing get destroyed. I have even seen some shops put an “And” between each of the parameters. Talk about LONG methods. Also, order becomes an issue.
However, this is impossible if you decide to do things such as:
public Collection<Deal> findDeals(Double d1, long c1, Date date1, Date date2, Date date3, long p1);
The parameters MUST have descriptive names.
Exceptions can be had but follow the signature as close as possible. For example, let’s say you wanted two methods for finding deals – one for the exact start date and end date and one for finding all deals between a date range. Here’s how to do it.
public Collection<Deal> findDeals(Date startDate, Date endDate);
public Collection<Deal> findDeals(Date betweenStart, Date betweenEnd);
But what happens if you have a particular need to locate all deals that fall within the date range but don’t necessarily have all of it’s dates inclusive in that range? That’s where you’ll need to step out of the pattern and write something custom.
public Collection<Deal> findDealsNonInclusive(Date betweenStart, Date betweenEnd);
The secret to method signatures is to follow the pattern as closely as possible but as with everything else, there will be exceptions. Be smart about it and make sure to review the anomaly with the team to explain why you stepped out.
Save Methods
public Interface saveInterface(Interface var1);
Same method signature allows for quick auto completes. Always return the exact object, populated with any auto generated data such as auto increments.
Delete Methods
public void deleteInterface(Interface var1);
Same simple pattern. See how easy it gets?
Run Methods
public void runProcessName();
This signature type allows for encapsulation of the implementation details from the caller. For example, let’s say you have a stored procedure summing the value of a day’s deals. Calling that stored procedure using this signature:
public void runDealAggregator(Date flowDate);
This allows to to search for what you want to do in layman’s term and doesn’t require the developer to know (or care) what the name of the actual stored procedure is. On top of that, if you change implementation, to say, kick this job off on a separate server via a web service call, you don’t have to rename it. You would if you method signature looked like:
public void callSPDEAL_AGGREGATOR_FOR_DATE(Date flowDate);
In conclusion, method signature are a great way for your team to self-organize it’s code. There are also some additional ways to help with code organization such as method ordering (finds first, then saves, then deletes, then runs). The trick is to decide on a template, and follow it.
Other potential benefits include generic transactional capabilities. For example, you (normally) don’t want to start and end transactions for simple finder methods but you would always want to on a save. Hibernate and aspects could be used to wild-card match on method signature, so any method with a save* signature has transactions required.
Good luck with your code and remember, if you don’t periodically review code, this kind of thing won’t be followed – I can guarantee it.