Support pattern
The purpose of the support pattern is to implement a number of target classes all supporting a common interface target, using a targetSupport class to implement common functionality. The pattern is a more explicit version of some common less structured inheritance patterns.
Even though the purpose of the pattern it so create many target classes, the pattern is described for one of these target classes. In that context there are two actors:
- Target
- The targets are the class we want to implement.
- Support
- The targetSupport is a class that implement those parts of the target that is to be reused/shared for other target's.
There are three contracts in the pattern:
- The target interface, which defines the target's public interface.
- The targetSupport interface, which defines the target's view of the targetSupport.
- The targetSite interface, which defines the targetSupport's view of the target.
PFC has many of uses the support pattern.
The contents of the target interface solely depend on overall problem, and does not contain anything that have to do with the design patters. For the the description we have added a property and a predicate:
interface target properties targetProperty : string. predicates targetPredicate : (). end interface target
Likewise the class declaration of the target (here target_1) solely depends on the overall problem:
class target_1 : target ... end interface target_1
It will either construct objects of the target type (as above), or of a type that supports target as here:
interface target_2 supports target % additional special target_2 functionality end interface target_2 class target_2 : target_2 ... end interface target_2
The targetSupport and target classes are in symbiosis with one and other:
- The targetSite interface contains entities that the targetSupport class will need from the target.
- And the targetSupport interface contains entities that the targetSupport class provides to the target.
The exact contents of these interfaces will depend on how the targetSupport class is supposed to support the construction of the target classes. For the the description we have added a predicate predicate to the targetSite:
interface targetSite predicates sitePredicate : (). end interface targetSite
For the description we will assume that the targetSupport class will directly implement the targetProperty and provide a support predicate, supportPredicate, which the target class can use to implement the targetPredicate.
Since targetProperty is to be exposed directly as target::targetProperty it must be declared as coming from the target interface:
interface targetSupport properties from target targetProperty predicates supportPredicate : (). end interface targetSupport
targetSupport'c constructor will (at least) take a targetSite as parameter, here we have also chosen that it should have an initial value for the targetProperty:
class targetSupport : targetSupport constructors new : (targetSite Site, string TargetPropertyInit). end class targetSupport
The implementation of the targetSupport class will store the site in a fact so that the site can be accessed where required. This code also contains example implementation of the targetSupport interface
implement targetSupport facts site : targetSite. targetProperty : string. clauses new(Site, TargetPropertyInit) :- site := Site, targetProperty := TargetPropertyInit. clauses supportPredicate(...) :- ... site:sitePredicate(), % site functionality can be used where required ... end implement targetSupport
The implementation of the target classes (here target_1) we will inherit from targetSupport to get access to the shared functionality, it will also have to support and implement the targetSite interface and privide itself (i.e. This) as site:
implement target_1 inherits targetSupport % access to shared functionality by inheritance supports targetSite % support the site so that the targetSupport can access functionality here clauses new(...) :- targetSupport::new(This, "target_1_property"). % initialization of the base class % The targetProperty is directly inherited from the targetSupport % but this class will have to implement targetPredicate clauses targetPredicate() :- ... supportPredicate(), % we can use the inherited supportPredicate in the target class ... clauses sitePredicate() :- % The target class must implement the sitePredicate ... end implement target_1
The support pattern very explicitly express what functionality the support class provides to the target classes and vice versa.
A collection must implement this interface:
interface collection{@Type} predicates isEmpty : () determ. % @short Succeeds if the collection is empty. % @end predicates contains : (@Type Value) determ. % @short Succeeds if the collection contains the value @Type % @end predicates tryGetFirst : () -> @Type Value determ. % @short @Type is the first element in the collection; fails if the collection is empty. % @end predicates getAll_nd : () -> @Type Value nondeterm. % @short @Type is nondeterministic iteration of the elements in the collection. % @end properties asList : @Type* (o). % @short All the elements in the collection as a list. % @end end interface collection
If the support class have access to the getAll_nd predicate, it can implement all the other predicates in terms of it.
So the site will have to supply getAll_nd predicate:
interface collectionSite{@Type} predicates from collection{@Type} getAll_nd/0-> end interface collectionSite
And then the support class will implement all the rest:
interface collectionSupport{@Type} predicates from collection{@Type} isEmpty, tryGetFirst properties from collection{@Type} asList end interface collectionSupport
The implementation of the collectionSupport looks like this (here commented additionally):
implement collectionSupport{@Type} facts site : collectionSite{@Type}. clauses new(Site) :- site := Site. clauses isEmpty() :- not(_ = site:getAll_nd()). clauses tryGetFirst() = First :- First = site:getAll_nd(), !. clauses asList() = [ V || V = site:getAll_nd() ]. end implement collectionSupport