The definition of how Entity objects are related to and move around through a TacsitController, ViewportManager, and Viewport seems to be vague by design. Presumably, vendors have different implementations that make creating a cohesive abstraction difficult. It is a worthy goal, however if it can be achieved. Separating the subsystems into more clearly defined subcomponents (Track providing, and track viewing) promotes encapsulation and division of responsibility. It also allows simplified compliance to the Tacsit specification for implementations which really provide only a single subsystem.
Consider NASA’s Worldwind. It is a general geographic map package. It provides little facility for getting track information from remote sources. The entire entity store concept is foreign to Worldwind; the reference implementation is purely incidental. Worldwind could work with any entity store, provided it had a well defined interface.
The reference implementation contains a sample of what an EntityRepository would look like. The definition is given here (sans comments):
public interface EntityRepository<E extends Entity> extends QueryManager
public Iterator<E> getEntities();
public void addRepositoryListener(RepositoryListener listener);
public void removeRepositoryListener(RepositoryListener listener);
Repository Listener would be defined as follows:
public interface RepositoryListener
public void entitiesAdded(RepositoryChangeEvent event);
public void entitiesRemoved(RepositoryChangeEvent event);
public void entitiesUpdated(RepositoryChangeEvent event);
public void entitiesCleared(RepositoryChangeEvent event);
This interface description should be general enough to allow for abstraction of existing implementations.
An area of common concern for this type of implementation is update speed. If data is being imported at a very fast speed, the user interface may not be able to handle the volume of updates. For instance, consider the common scenario that each update received is an array of structs containing track data that contains all the values for the track in the system. If a repository were to fire an “entitiesUpdated” event every time a property was copied into the entity, that would potentially be many new objects created:
O = (number of objects created to fire the event) *(number of properties set) * (number of tracks)
Each track will probably have 10 properties, 2 objects will be needed, and there may be roughly 2000 tracks in the system. That’s 40,000 objects every time a new track block comes in excluding anything that happens once it gets to the UI.
However, this problem can be alleviated by deviating slightly from the standard Listener implementation. Instead of broadcasting to a RepositoryListener any time a track is changed, the EntityRepository can poll its Entity objects for changes based on an update rate. It would then fire a summary of those changes to its associated listeners, effectively coalescing all the redundant events into a single one. This drastically cuts down on the number of objects created to:
O = (number of objects created to fire the event)
Each entity will need to be “polled” every update, but the polling consists simply of checking a time value against a known value. In the example above, 2000 lastModified values would have to be compared.
This allows the update interval to be easily scaled to the fastest possible rate that still gives the desired responsiveness in the user interface. The key attribute is this: from the perspective of the client of the entity repository interface, it behaves exactly as per a “normal” Listening pattern. The complexity is masked by the interface and doesn’t creep into client code.
An example of how such an implementation would work is included in the reference implementation, PolledEntityRepository.