Refactor SegmentationImageProvider API
A SegmentationImageProvider provides a high level API to deal with segmented objects in a segmentation result.
Currently however there are a couple of problems with the design:
- The interface defines both object editing methods and object access methods (these 2 aspects should be separated with 2 distinct interfaces)
- Configuration is done via set methods. Calling one or the other is supposed to induce distinct behaviors:
- Setting a SegmentedPrototype should enable the provider to create segmented objects upon request. (Upon first call to a get method, current implementation creates a map of all objects found in the image)
- Setting 'known associated data' should prevent the provider from creating new data and instead reuse the provided data objects.
Unfortunately this later configuration design is quite error prone:
- The set methods may be called more than once - or not at all - which may lead to unintended behavior of the underlying implementations.
- The expected behavior for a given configuration is also neither clear nor flexible. For example: if setting only a subset of the known associated data, which behavior would be expected from the following method?
/**
* @param p The location of interest in the image
* @return An {@link Optional} holding the data found at the specified location. NB: the Point must have thesame number
* of dimension as the underlying image (minus channel).
* @see #getData(long[], int)
*/
public Optional<SegmentedObject> getData(Point p);
Of course, when the point is on a shape that was provided, the data object should be returned, however if the point is neither on the background nor on one of the provided shapes, what should happen? The method may either return an empty optional or it may return a newly created object using known associated data as prototype.
Potential Solution: use a fluent builder pattern so client code looks like this:
SegmentationImageProviderBuilder providerbuilder = providerBuilderFactory.newBuilder(segmentationResult) // -> new builder which creates a provider for a given segmentation result.
providerBuilder.doNotTranslateImageIntoObjects().build() // -> Interface with convenience methods only from (see line 192 in SegmentationImageProvider)
// OR //
providerBuilder.createObjectUponRequest(prototype).build() // -> Interface read only
// OR //
providerBuilder.createObjectUponRequest(prototype).enableEditing().build() // -> Interface read only + editing
// OR //
providerBuilder.usePrecomputedObjects(supplier).createMissingObjects(false).build() // -> Interface read only
NB1: 'Enable editing' should only be available for objects not already in the database (since other computations based on stored data may have been done and editing would render those obsolete/inaccurate without anything done about it).
NB2: We do not use DataAccess directly to avoid creating a dependency on foundationj-dbm and use another level of abstraction (supplier instead). This also has the added advantage to supply precomputed objects not stored in the database.
NB3: 'createMissingObjects(flag)' allows client to explicitely define expected behavior.