General Rules
- The Comparator should always be in a consistent state. Every user action should run to completion or terminate itself, returning the system to a deterministic state. If there is a manipulable value, it should have some default, even if the default is to do or define nothing. All data should have a deterministic value. If an entity cannot present a consistent state, it should employ some locking mechanism to prevent others from inspecting it. It should never be possible to see an incomplete transaction.
- Entities with both persistable and non-persistable properties should distinguish what will be preserved when the entity is persisted.
- Computations should use a bounded amount of memory or have some mechanism to prevent exhausting the virtual machine heap. Even if the computation can trap the memory error, other threads may have unpredictably been unable to allocate memory and the application cannot reliably continue.
- Operations should fail fast. That is, when an operation cannot be completed, a minimum of work should be performed before the operation is aborted.
- All exceptions should be caught and handled at module boundaries. A module should never allow an exception to cross to another module, bubble up to the Comparator itself, unwind the event thread, or otherwise be handled by the default exception handler.
- The Comparator facilities for logging, debugging, and messaging should be used in preference to external libraries.
Working with Data
- Every data object or its owner must define a locking mechanism. If there is no explicitly defined mechanism, the object should only be accessed while on the event thread.
- Every time a data object is changed, the change should be announced to all potential viewers of the object. A series of operations can be treated as an atomic transaction, with a single announcement at the end, if the object is protected by a locking mechanism.
- When two views exchange data, it is always done through the Comparator storage. This allows anyone interested in watching the data to easily access the data and receive update notifications.
- A computation with discrete steps should provide access to as many of the intermediate results as possible. When appropriate, these results should be cached for faster access.
- A data object should never be cast from an immutable version to a mutable version unless there is some prearranged agreement that the object can safely be altered. This allows data providers to skip wrapping many data objects into immutable containers that consume more memory and have slower access times.
User Interface
- No view should create a program modal dialog or alter the frame outside of its view area.
- All interface components should use the Swing library.
- All changes to the user interface must be done on the event thread. This is a requirement of Swing. Any computation requiring a noticeable amount of time to complete should not be done on the event thread. This prevents the event thread from blocking which gives the program a frozen appearance.
- Never expose an interface component to multiple views. Swing does not correctly support simultaneously placing a component into multiple containers. Instead, create separate components that share a common store.
- All views should use a consistent style of widgets and graphical elements. The colors, fonts, and styles of the default widgets are defined by the look and feel. Custom widgets should use also these resource values to duplicate the visual style.
- Views should have a minimum of configurable options accessible through the interface. Any option adjustable during runtime should only change data values and not select different code paths.