Skip to main content
This revision made August 11, 2011 17:49, by Fabrizio Giudici
« earlier revision revert to this « later revision
Difference compared to previous revision
= Coding Guidelines = == Lombok == blueBill makes extensive use of the [http://projectlombok.org/ Lombok] annotation processor for reducing boilerplate code. == Basic coding guidelines == === Threading annotations === tbd === Logging === For legacy reasons, blueBill uses a custom logger from the [http://thesefoolishthings.kenai.com TheseFoolishThings project]. There is ongoing work for replacing it with the popular SLF4J. All the newly created classes must use SLF4J; the logger variable must b declared as:
name="java">
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

...

public class MyClass
  {
    private final static Logger log = LoggerFactory.getLogger(MyClass.class);

    ...
  }
The variable name is important because a new version of Lombok will generate automatically the
declaration with the name 'log'. === Naming conventions === For classes: * *Support: a class implementing an interface providing a partial or default implementation of methods - it's intended to be ALWAYS subclassed by concrete implementations; * Default*: a class providing a default implementation of an interface; while it can be subclassed, it can also be used on its own (unlike a *Support) * *able: a typical suffix for a Role (e.g. Renderable) * *View: an interface describing a MVC View * *ViewController: an interface describing a MVC Controller * Default*ViewController: a default implementation of a MVC Controller * Android*ViewController: the Android implementation of a MVC Controller * *Activity: an Android Activity * *Vocabulary: a facility class only containing some constants with semantic identifiers For packages: * *.ui: a package containing UI related stuff * *.impl: a package containing implementation classes that are not exposed * *.spi: a package containing *Support or *Default classes. == Architectural Patterns == === Finders === tbd === Dependency management === blueBill uses the Service Locator pattern to deal with inter-class dependencies. There is plan to replace this with a more fashionable dependency injection based on the javax.inject.Inject annotation, but some comprehensive tests must be run to understand whether the use of reflection for dependency injection has an impact in Android (some Google documents discourage massive uses of reflection). The Locator class must be used as in this example:
import it.tidalwave.netbeans.util.Locator;

...

public class MyClass
  {
    private final MyService myService = Locator.find(MyService.class);
    ...

    public void method()
       {
         Locator.find(MyOtherService.class).doSomething();
       }
  }
Using locators within the code as in the method() example is discouraged, since it's a conding style that can't be immediately replaced with the @Inject annotation. If a service must be looked up lazily (that is, not during field initialization), the following code is recommended:
import javax.inject.Provider;

public class MyClass
  {
    private final Provider myService = Locator.createProviderFor(MyService.class);
    ...

    public void method()
       {
         myService.get().doSomething();
       }
  }
Service resolution will only be done when get() is invoked. Injecting Providers is supported by the @Inject annotation. === Data, Context and Interaction (DCI) === blueBill Mobile is designed following the
[http://en.wikipedia.org/wiki/Data,_Context,_and_Interaction Data, Context and Interaction (DCI) practice]. You can find a comprehensive description of DCI in an [http://www.artima.com/articles/dci_vision.html Artima article] and a short presentation, also focused on the blueBill usage, at [http://www.slideshare.net/fabriziogiudici/dci-data-context-and-interaction-jug-lugano-may-2011 Sideshare]. In short, this practice mandates each concept are modelled by two kinds of objects: # a single '''Datum''' class, which is usually stateful and contains the properties and behaviours that are strictly part of the concept nature; # many '''Role''' interfaces, which are stateless (since they work on their Datum state) and expose a simple and well focused behaviour. Usually Roles implement recurrent behaviour, so their interfaces are highly reusable, while their implementations are usually strictly coupled with the Datum they are attached to. In pratice, Roles are adapters to Datum classes. In fact, Datum classes should not directly talk together, or to other facilities such as the User Interface or the persistence service; instead, interactions must be mediated by Roles. Roles are bound to their Datum in a dynamically way ("injected") according to a '''Context''', which can be very long (spanning the whole life of the application) or even very short (related to a single operation). The following are the most used Roles in blueBill mobile: * '''Displayable''': provides a string with the display name of the Datum. * '''LocalizedDisplayable''': an extension to Displayable which provides support for different languages (Locales); * '''StringRenderable''': renders a Datum into a String using a pattern or some options (thus producing some detailed rendering than the simple display name); * '''HtmlRenderable''': like StringRenderable, but producing HTML markup; * '''ActionProvider''': provides contextual actions that can be run on the Datum; * '''ViewFactory''': creates an Android View; * '''ViewRenderable''': renders the Datum to an Android View; * '''TextViewRenderable''': renders the Datum to an Android TextView or TextSwitcher. Role binding to their Datum is implemented by means of the NetBeans Platform Lookup. A Lookup is a bag of objects, which can be retrieved by using their class as a String. There's a global Lookup, which works as a global context:
Lookup globalContext = Lookup.getLookup();
</pre>

but other instances can be created for implementing specific contexts bound to single Datum objects or other parts of the application. Objects with a local context implements the Lookup.Provider interface, which provides the method getLookup()

Taxon taxon = ...
Lookup context = taxon.getLookup();
</pre>

In practice, a Datum can retrieve a Role with the following code:

a">
Taxon taxon = ...;
String displayName = taxon.getLookup().lookup(Displayable.class);
To keep the code cleaner and
more readable, the following approach is preferred (but it's semantically equivalent to the previous one):
Taxon taxon = ...;
String displayName = taxon.as(Displayable);
</pre>

Note that Displayable is just a shortcut for Displayable.class - the trick is provided by specific final static fields that are provided by each Role and can be imported statically.

The as() method is declared by the As interface (this means that all objects providing a context must implement both the Lookup.Provider and the As interfaces) and a common implementation can be provided by subclassing the AsLookupSupport class.

The actual classes which are responsible for binding Roles to their Datum are CapabilityProviders, special factories that are related to a specific Datum and provide factory methods for its Roles. For instance, the following class:

@ServiceProvider(service=CapabilitiesProvider.class) 
public class TaxonTextRenderablesCapabilityProvider extends CapabilitiesProviderSupport
  {
    @Override @Nonnull
    public Collection createCapabilities (final @Nonnull Taxon taxon) 
      {
        return Arrays.asList(new TaxonStringRenderable(taxon), new TaxonHtmlRenderable(taxon));
      }
  }
declares to be a CapabilityProvider for Taxon (it's sufficient to specify the Datum as the generic parameter) and creates the TaxonStringRenderable and TaxonHtmlRenderable Role implementations. Note that CapabilityProviders are annotated with @ServiceProvider, which generates META
-INF/services information that can be automatically discovered by the NetBeans Platform Lookup. At the moment, @ServiceProvider doesn't work on Android, since META-INF information is discarded [http://services.tidalwave.it/jira/browse/BBMA-194 BBMA-194]. Thus, all classes annotated with @ServiceProvider must be manually registered in the BlueBillLookup class that works as a temporary patch of the NetBeans Platform global Lookup. CapabiltyProviders registered by @ServiceProvider have a global context effect: their managed Roles are injected always, for the whole scope of the application. If a narrower scope is required, the ThreadBoundLookupContext class can be used:
CapabilityProvider localCapabilityProvider1 = new ...();
CapabilityProvider localCapabilityProvider1 = new ...();
Lookup lookup = Lookups.fixed(localCapabilityProvider1, localCapabilityProvider2);
 
ThreadBoundLookupContext.with(lookup).run(new Task()
   {
     public void run()
       {
         // ... instantiate Taxon objects - roles from localCapabilityProvider1 and 2 will be injected as well
       }
   });
A typical use for ThreadBoundLookupContext is in the MVC pattern for specifying Roles to render a Datum in a way that is specific to a given View. === Model, View, Controller (MVC) === The [http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller MVC pattern
] in blueBill Mobile is heavily influenced by the DCI design. In particular, the interactions between a Model, a View and a Controller occur by means of Roles. ==== Views and Controllers ==== Views and Controllers must be first declared by their interfaces. There must be no references to a specific UI technology, such as Android, Swing or a Web framework, in these interfaces: this make it possible to reuse most of the code in different contexts. They must expose semantically significant methods, whose meaning is well understood in the context of the interaction with the user. This means, for instance, that instead of declaring a generic notifyError() method, a specific notifyErrorInSelection() must be used. This makes it easier to write integration tests. When a View and a Controller exchange Datum instances (e.g. when a Controller populates a View, or a View notifies the selection of a Datum), they must be wrapped in a PresentationModel (see below). There must be no threading constraints on Views and Controllers methods: this means that methods must be callable by any thread - View implementations should take care of executing them in the proper UI context. This approach makes it possible to reuse default implementations of controllers in scenarios where there are no threading issues (for instance, a Web application). Most of the Controller implementation is implemented in a DefaultXXXController class, which is still independent of the technology. Typically, but not necessarily, these DefautXXXControllers are extended by an AndroidXXXController to provide some Android-specific behaviour (most of the times is just the injection of specific Roles). Views are implemented by Android Activity classes, which should just create the specific widgets, populate them with data, register listeners that call back the controller and take care of UI threading issues (this means that any method which is declared in the View interface must have its body wrapped into a runInUIThread() call). ==== Model ==== Pending: the Models related to forms, which should just carry the information rendered in the input widgets and input by the user, are currently undefined. When a Datum is to be rendered on a View, it must not be passed naked, but rather wrapped in a PresentationModel.
Taxon taxon = ...;
PresentationModel presentationModel = new DefaultPresentationModel(taxon);
view.render(presentationModel);
A PresentationModel doesn't expose any specific behaviour other than being Role-aware: it extends the As and Lookup.Provider interfaces. The DefaultPresentationModel by default exposes the same Roles of the Datum it wraps, but it provides a place for adding, removing or modifying the existing Roles. If you know the NetBeans Platform, the responsibility of PresentationModel is very similar to the one of Node. Typically, the following Roles are injected: ViewRenderable or TextViewRenderable, ActionProvider; sometimes ViewFactory. This makes it possible to control the rendering and the user interaction of each Datum in a View-specific fashion. There's a specific Android adapter for PresentationModel: PresentationModelAdapter. It must be used in all cases in which an Adapter to a List widget must be provided. In particular, PresentationModelAdapter wraps a collection of PresentationModels and mandates that each item provides the ViewFactory and ViewRenderable Role for creating a widget to render the item. Optionally, an ActionProvider can be also present, in which case it will be used to provide the click behaviour (the preferred action) and to create a pop up menu when a longpress gesture is performed. For the latter task, PresentationModelAdapter provides a listener that must be bound to a list, and two methods related to contextual menus which must be called by the Activity, as explained in the following example:
public class MyActivity extends Activity implements MyView
  {
    private ListView list;
 
    private PresentationModelAdapter adapter;
 
    public void populate (final @Nonnull List<PresentationModel> presentationModels)
      {
        runOnUiThread(new Runnable()
          {
            public void run()
              {
                adapter = new PresentationModelAdapter(MyActivity.this, presentationModels);  
                list.setAdapter(adapter);
                list.setOnItemClickListener(adapter.getOnItemClickListener());
              }
          });
      }
 
    @Override
    public void onCreateContextMenu (final @Nonnull ContextMenu menu,
                                     final @Nonnull View view,
                                     final @Nonnull ContextMenuInfo menuInfo)
      {
        adapter.onCreateContextMenu(menu, view, menuInfo);
      }
 
    @Override
    public boolean onContextItemSelected (final @Nonnull MenuItem item)
      {
        return adapter.onContextItemSelected(item);
      }
 
    ...
  }
=== ControlFlow === tbd == UML Stereotypes == The javadoc documentation automatically generates embedded UML diagrams. These are the used stereotypes: * '''«Activity»''' an Android activity * '''«Controller»''' a MVC controller * '''«Datum»''' a DCI Datum * '''«View»''' a MVC View * '''«Role»''' a DCI Role * '''«RoleInjector»''' an injector of a DCI Role * '''«Service»''' a generic service Please have a look at the section about MVC for a short discussion about the way MVC is implemented.
 
 
Close
loading
Please Confirm
Close