Patterns, conventions, principles

Design patterns

Design patterns are commonly accepted ways of solving specific coding tasks. Some patterns are quite simple, some are grand and apply to the high-level design of a codebase.

Many Java libraries demonstrate common patterns, Swing uses them extensively eg actionListeners illustrate the Observer pattern.

Some of the patterns and conventions are indicated via annotations, mostly as a statement of intent and aide-memoir.

Annotations

@Cache

Singleton (@Singleton)

Only one copy of this object should exist. An example is the zodiac application itself. Since only one copy can exist attempts to create a new instance return the already existing instance. All of the member fields then also inherently become singletons - as a field of the zodiac class there will only ever be one instance of the entity database. Which is the design intent.

@ZodiacEntityReference

Dependency injection (@Inject)

Not actually really implemented, but it’s concept is indicated via the @Inject annotation. A full implementation makes use of a framework that typically creates additional ‘glue’ code automatically. That would be overkill for this codebase.

The pattern is used to avoid constructors and methods with long parameter lists and provides (when fully implemented) greater modularity and flexibility for unit testing. In this codebase the annotation is used to identify where a (typically) static class member needs initialising before instances of the class can be used. An example is the processorFactory class - it is provided with a valid XStream object which it will use to process XML. All later usage of the Factory uses the same configured XStream.

Naming conventions

Not a design pattern, but it helps to have some sort of coherent naming scheme.

  • test files starting ‘mock’ : used in integration testing to provide ‘mocks’ - simplified and hard-coded parts of the system that aren’t actually being tested.
  • test files ending in ‘IT.java’ : identifies the more involved integration tests.
  • files starting ‘i’ : java interfaces
  • files starting ‘e’ : java enums

Principles

The mantra of software design is “high cohesion, low coupling”. Classes in a package should perform the same kind of functionality and each class aims to reduce use of other classes.

Reducing coupling

Using event buses allows an object to broadcast out a message without having to explicitly know anything about the receiver. The bus also allows any object to listen-in so a single message can be acted on at multiple points in the code. This facilitates code that can focus on doing it’s own specific task.

The zodiac class creates two such buses. The windowBus accepts messages about windows, clicks and user interactions. All the GUI stuff. When the interface needs to message the JMonkeyEngine application states (eg that the application is closing) it does so by broadcasting a message that gives JME opportunity to shutdown it’s operations, while the GUI takes care of itself. A second gameBus is used to broadcast events that happen in the game world eg ships departing a starbase. The separation is for clarity.

Both buses are asynchronous so a message can be dispatched without waiting for a response.

Threading

The main JME application and the Swing interface happen on two separate threads. GUI processing should always happen on the Swing thread to preserve UI responsiveness.