Coding guidelines

This document describes the coding guidelines for blueMarine. All the code in the Metadata, blueMarine-core and blueMarine components must adhere to these guidelines.

This document is a draft.

Use English names

English must be used for all the names of classes, methods and variables. Please respect the language: make a check with a dictionary to avoid typos before introducing a new part in an API.

Use it.tidalwave.logger.Logger

All the classes that does some reasonable computation must support logging. Use it.tidalwave.logger.Logger as the logging facility and declare for every class:

import it.tidalwave.logger.Logger;

public class MyClass
  {
    private final static String CLASS = MyClass.class.getName();
    private final static Logger logger = Logger.getLogger(CLASS);
  }

The availability of the CLASS constant is good in cases you need to call the logger.throwing() method that expects the class name. Please note that logger is lowercase in spite of being final - this is to decluttering the code listing.

it.tidalwave.logger.Logger exposes similar methods as java.util.logging.Logger adding the String.format() variant:

logger.fine("The result of the computation is %d", value);

It's very important to use this approach instead of concatenating strings with the + operator, since it can benefit from performance optimizations if the related logging level is disabled.

Design by contract

Components that are not merely entities or value object must be first declared by their interface. The implementation must be registered by means of the META-INF/services mechanism and made available by means of a service locator. See also how to design packages.

Provide Service Locators

Services must be not instantiated by a regular constructor; they must be registered with the META-INF/services mechanism instead and made available by means of a Service Locator as in the following example:

public interface MyService
  {
    public static final class Locator
      {
        private Locator()
          {
          }
       
        @Nonnull
        public static final MyService findMyService()
          {
            final Lookup lookup = Lookup.getDefault();
            final MyService myService = lookup.lookup(MyService.class);

            if (myService == null)
              {
                throw new RuntimeException("Cannot find MyService");   
              }

            return myService;
          }
      }

    public void myMethod();
  }

Please note the following characteristics of a service locator:

  1. Must be a final static inner class of the service interface.
  2. Must have a private constructor.
  3. Must have a single static findServiceName() method that never returns null.
  4. Must check for the return from the Lookup and eventually throw a RuntimeException in case of error.

A typical use of a Service Locator is:

public class MyServiceClient
  {
     @Nonnull
     private final MyService myService = MyService.Locator.findMyService();
  }

Mark variables as 'final' whenever is possible

Whenever it's possible, fields, method parameters and local variables must be declared as final:

public class Example
  {
     private final String property;

     public Example (final String property)
       {
         this.property = property;
       }
  }

 

Don't accept null parameters

Unless otherwise stated, methods don't accept null parameters. Enforce this policy and eventually throw IllegalArgumentException (not NullPointerException). Prefer special constant values instead of null; in case of overloading, provide methods that don't require the 'nulled' argument.
For instance, instead of:

public void myMethod (String arg1, String arg2, String arg3);

that could be called as

myObject.myMethod("foo", null, null);

use

myObject.myMethod("foo", DEFAULT_XX, DEFAULT_YY);

where DEFAULT_XX and DEFAULT_YY can be e.g. imported with a static import.

Otherwise, provide:

public void myMethod (@Nonnull String arg1)
  {
    // enforce non null parameter, eventually throws exception
    internalMyMethod(arg1, null, null);
  }

public void myMethod (@Nonnull String arg1, @Nonnull String arg2, @Nonnull String arg3)
  {
    // enforce non null parameters, eventually throws exception
    internalMyMethod(arg1, arg2, arg3);
  }

private void internalMyMethod (@Nonnull String arg1, @CheckNull String arg2, @CheckNull String arg3)
  {
    // do not enforce non null parameters
  }

As you can see, parameters must be annotated with @Nonnull or @CheckNull

Don't return null values

Unless otherwise stated, methods shouldn't return null values. Instead of returning null, they should throw a checked exception. For instance, instead of coding:

public interface MyService
  {
    public String doSomething();
  }

MyService myService = ...;
final String result = myService.doSomething();

if (result != null)
  {
    ...
  }

prefer this approach:

public interface MyService
  {
    @Nonnull
    public String doSomething()
      throws NotFoundException;
  }

MyService myService = ...;

try
  {
    final String result = myService.doSomething();
    ...
  }
catch (NotFoundException e)
  {
  }

It's important that the relevant exception is checked, so client code will be compelled to handle all the possible outcomes. Methods must be marked as @Nonnull.

This rule can be relaxed when methods similar in function to those of a Map are defined; for instance it is allowed to do the following:

public interface MyService
  {
    public void setAttribute (@Nonnull final String name, @Nonnull final Object value);
    public @CheckNull Object getAttribute (@Nonnull final String name);
  }

In this case it's mandatory to mark the relevant methods with @CheckNull.

Don't rely on toString() return value

toString() is provided for debugging purposes only. Do not rely on the information that could be retrieved by parsing the returned string: it can be changed without notice.

Design classes as immutable whenever is possible

Whenever it's possible, classes should be immutable, that is their status must be set only in the constructor and never changed later. Immutable classes don't expose setter methods. If possible, use the 'final' keyword to enforce this policy:

public class MyClass
  {
    private final String property; 

    public MyClass (final String property)
      {
         this.property = property;
      }
  }

The 'final' keyword must be omitted in case of Serializable objects, since the default constructor must be provided:

public class MyClass implements Serializable
  {
    private String property; 

    public MyClass()
      {
      }

    public MyClass (String property)
      {
         this.property = property;
      }
  }

 

Design flexible methods

Special care should be taken when designing the signature of methods in public APIs. In particular, the challenge is how to achieve a good stability of the API (i.e. no uncompatible changes as time goes on) and flexibility (i.e. how to add support for new features), while keeping the number of methods on an interface as small as possible.

Let's consider a single function of a given MyService:

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

This is how the method has been designed at the beginning, as from initial specification the need for those three parameters emerged. In a next release, it arises the need for a further boolean parameter, so it apparently makes sense to overload the method:

public void myFunction (int param1, String param2, float param3);
public void myFunction (int param1, String param2, float param3, boolean param4);

In this way we have backward compatibility and flexibility, but at the expense of simplicity. As time goes on, more and more overloaded methods would be added.A better solution could be to use a JavaBean to encapsulate all the parameters:

public class MyFunctionParameters
{
   public void setParam1 (int v)...
   public int getParam1()...
   public void setParam2 (String v)...
   public String getParam2()...
   // etc...
}

public void myFunction (MyFunctionParameters params);

MyFunctionParameters p = new MyFunctionParameters();
p.setParam1(...);
p.setParam2(...);
myFunction(p);

In this way it is possible to add new properties to MyFunctionParameters, keeping a single method (not overloaded) while not breaking existing code; this approach also supports parameters with a default value, for which it is not required an explicit call to the setter method. 

While code reability has a negative impact by this approach, the major pitfall is the poor support by third party extensions of the method. Suppose that myFunction() is just an entry point in a service façade which supports pluggable implementations, perhaps by means of SPI (Service Provider Interface). The call and its parameters would be just forwarded to other code, that could be provided by a third party; and the latter might want to add its own parameters.

Of course one could think of subclassing MyFunctionParameters for carrying extra properties and forcing a downcast in the SPI code. This is doable, but introduces subclassing, whose use should be avoided when possible.

Thus, a better approach is needed and it's the one required for blueMarine code. The basic point is to make use of tagging interfaces and varargs:

public interface MyService
  {
    public static interface MyParameters
      {
      }

    public static enum OptionA implements MyParameters
      {
         OPTION1, OPTION2, OPTION3
      }

    public static class StringParameter implements MyParameters
      {
        private final String name;
       
        public StringParameter (final String name)
          {
            this.name = name;
          }
       
        public String getName()
          {
            return name;   
          }
      }
   ...
   public void myService (MyParameters ... parameters);
}

With the proper static imports to avoid too much verbosity, it is now possible to invoke the method as:

myFunction(OPTION1, new StringParameter("foo"));

The tagging interface makes it possible to implement some compiler checks, to it is not possible to mistakenly pass wrong parameters, while the varargs makes it possible to add as many parameters as needed. By implementing MyParameters, third parties can implement their own extensions without any subclassing. In addition, by proper design of names, the code can be very readable - the "new StringParameter()" might not appear very sexy, but is just a generic name; think to something more sophisticated as:

myFuction(new Includes("*.java"), new Excludes("*.properties"));

Of course, since a dynamic approach has been selected, a bit of more work is required by the implementation of the method in order to retrieve the parameters it needs (and eventually enforce their validity). On this purpose, a small utility class is provided by the module Core/Arguments:

public final class Arguments
  {
    @CheckForNull
    public static <T,O> T find (@Nonnull final Class<T> argumentClass,
                                @CheckForNull final T defaultOption,
                                @Nonnull final O ... arguments)
      throws IllegalArgumentException { ... }

    @Nonnull
    public static <T,O> Collection<T> find (@Nonnull final Class<T> argumentClass,
                                            @Nonnull final O ... arguments) { ... }
  }

Its two methods can be used to extract from the vararg parameter the single value(s) you need. For instance:

private final static DEFAULT_SPARAM = new StringParameter("");

public void myService (MyParameters ... parameters)
  {
    OptionA optionA = Arguments.find(OptionA.class, OPTION1, parameters);
    StringParameter sParam = Argument.find(StringParameter.class, DEFAULT_SPARAM, parameters);
    ...
  }

As you can see, find() supports returning a default value if parameters doesn't contain any instance of the given class. The find() variant that returns a Collection supports the capability of passing multiple instances of the same type (while the former variant of find() will throw an exception if more than a single instance is found).

It is to be clarified that is not mandatory to design all parameters of a method in this way; it is allowed to keep standard, fixed parameters in all cases in which their presence doesn't jeopardize flexibility. So it is legal to write:

myFunction(String name, int value, MyParameters ... arguments);