Child pages
  • Contributions

Extension by Decoration

 

 

THE INFORMATION HEREIN IS PROVIDED “AS IS,” WITHOUT ANY WARRANTIES OR REPRESENTATIONS, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION, WARRANTIES OF NONINFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Is there any IPR and/or Patentable Interest Declaration associated with this contribution NO


Background

In this paper we will look at using ‘decoration’ as an alternative to subclassing.

 

Decorating rather than subclassing

Subclassing Example

Let’s start with a simple example

Figure 1

Note that class Colour is abstract.

So of course I can only instantiate the subclasses and they inherit Colour’s attributes (and methods)

Figure 2

Large inheritance trees can cause issues and if we want to further decouple the subclasses from the parent by changing from white box to black box reuse, we can replace each inheritance relationship with an association.

Inheritance causes issues because attributes and methods inherit down, so you may inherit a lot of junk you don’t want and you can’t hide the inherited things away.

Also unless you allow for multiple inheritance, you can end up with inheritance trees mixing concepts ( http://www.martinfowler.com/apsupp/roles.pdf ).

Decorating can be especially useful to replace subclassing between modules where we want to reduce the coupling between modules.

A good example of where this makes sense is to represent explicit Catalogues and Inventories. We would normally model Catalogue composing many CatalogueEntry. Rather than making CatalogueEntry the parent of existing classes, a decoration solution allows us to simplify the interface seen by Catalogue and leave the natural class hierarchy for the entries. CatalogueEntry stores the attributes needed for entry management without polluting the entry classes with them.

 

Example converted to using Decoration

Each inheritance relationship has been replaced with a corresponding association relationship with :

  • Navigability in the same direction as the inheritance arrow (which keeps the package dependency graph unchanged if the parent and child are in different modules)
  • Multiplicity 1 at the parent (decorated) end
  • Multiplicity 0..1 at the child (decorating) end
  • Also we have tagged this as a decoration association with a stereotype (see later for reasoning)
  • The diagram deliberately doesn’t use Composition or aggregation black diamonds – the stereotype is a better way of clarifying the different intent

Figure 3

Note that we have had to make Colour concrete. It could make sense to add a stereotype that indicates if we allow “undecorated instances” of this object.

Note also that we don’t need the BlueGreen subclass anymore as we can decorate a Colour instance with both a Blue and a Green instance.

Also the model is similar to what we see from an OO to relational mapping where we use the table per class option ( https://www.ibm.com/developerworks/library/ws-mapping-to-rdb/ ).

Note also that it would make sense for Colour to only be instantiated by Blue or Green classes – the decorating instance should control instantiation, update and deletion of its decorated instance.

 

So our instance diagram is now as shown in the figure below.

Figure 4

 

Note that unlike the Gang of Four OOAD decoration pattern, we don’t have a common parent class for Blue and Colour, so we don’t have polymorphic behaviour any more. If this is an issue, then subclassing should be retained.

 

Adding the <<Decorates>> stereotype to the association could allow :

  • A validator to check that the association is directional (even if it doesn’t cross packages)
  • To validate that the correct multiplicity has been used on the navigable and non-navigable ends
  • A generator to create delegation code
  • A generator to create deletion cascading code

 

 

‘Multiple inheritance’ decoration (where a decorator class decorates more than one core class may be allowed or disallowed by a validator depending on the team needs.

 

If the lack of navigability from the decorated class to the decorating class(es) is an issue, then use of the observer pattern may prove useful ( https://en.wikipedia.org/wiki/Observer_pattern ). For instance the observer pattern may be useful if a decorated class gets a request to delete itself to  determine if there are any other decorators or not.

 

Regarding the lack of composition on the decorating association, we need to consider :

  • If Colour was originally concrete, then it could exist without any decorators. If I now add a decorator, should the decorator delete the Colour instance when it is deleted ?
  • What about the Blue Green case where I allow more than one decorator on a core class. Does deletion cascade only when the last decorator is deleted ?
  • The suggestion is that the Colour decorators create their Colour instance as part of their constructor (see code example), so therefore it makes sense that they should also be responsible for deleting it, so the deletion should be from the decorator to the core class.

 

Method Inheritance vs Delegation

In figure 1, Blue inherits the calcHue() method.

In figure 3, the decorators need to have their own calcHue() method that should just delegate to Colour.calcHue() as shown in the “Code Examples” section.

 

Note that a code generator could generate these delegating methods automatically if it recognises the <<Decorates>> stereotype.

 

When to use Subclassing

Subclassing is still useful in a number of cases, especially when polymorphism is needed.

The decorator pattern is useful to layer models in a loosely coupled manner and to prevent enormous inheritance trees especially when used for optional (technology) modules.

 

Knowing if there may be decorators

One of the issues of the pattern is having to query to find out what decorators exist.

A way of reducing this issue is to have a class to register which features are enabled.

It can be as simple as adding the classes like below, which show the capabilities in a ConstraintDomain. The state could be available, enabled …

Figure 5

 

Extending the Decoration pattern to multiples

A similar pattern arises when we have a device that has a number of functions of different technologies.

We wouldn’t have used subclassing in this case but probably a central device class composing all of the different functions.

As explained in TMF TR-225 Appendices document, section  6 Appendix : E, we need to invert the dependencies, and we end up with a model pattern as shown below.

 

Figure 6

Because we have replaced the composition, we have used a stereotype called <<ContainedBy>> to clarify the slightly different semantic intent.


Code Examples

 

Constructors

 

// note this code is a rough example only and has not been tested or run at all

//

// example of using the classes
Blue b1 = new Blue (); // create a blue instance, and its core instance too

Green g1 = new Green ( b1 . getColour ()); // create a green decorator on b1’s core instance

 

//

// ===========================

Class Colour {

String hue ;
 

public String getHue (){

return h ;

}

}

 

//

// ===========================

Class Blue {

Colour c ;
 

Blue (){ // standard constructor

super ();

this . c = new Colour ();

}
 

Blue ( Colour c ){ // special constructor

super ();

assert c != null ;

this . c = c ;

}
 

public String getHue (){ // delegate to Colour

return c . getHue ();

}

public void removeColour (){

c = null ;

return ;

}

}


The Decorator Design Pattern

Decorator is an extension pattern, i.e. it is used to extend the behaviour of an existing class.

“The decorator pattern is an alternative to subclassing.” [Wikipedia]

Note that Component and ConcreteComponent are the original classes and Decorator and ConcreteDecorater are the decorating classes. Splitting the decorator into two classes allows for more than one concrete decorator class to be defined. If this is not needed, then a single concrete  Decorator class can be used.

Also note the similarity to the Composite pattern; it is in fact a degenerate form with only one component.

Note that for this to work, clients must reference Component, not ConcreteComponent.

Figure 7 – Decorator Pattern [Wikipedia] (modified)

 

An example of the use of the decorator pattern in Java itself is the FilterInputStream class, which decorates the InputStream class.


Note that in Java, it is common to use interfaces instead of abstract parent classes, as shown in the diagram below.

Note that DecoratorWindowTest is the ‘client’, and that it uses the Window interface.

Window and Simple window would be the existing classes.

WindowDecorator and its subclasses are the added decorators.

Figure 8 - Decorator Pattern in Java [Wikipedia] (modified)

From an instance point of view, we can think of the decorator instances being wrappers.

For example from https://assist-software.net/blog/implementation-decorator-pattern-c we could decorate an expresso class with milk and chocolate decorators, and the diagram below shows the wrappering concept and also the method call delegation.

Figure 9 – Wrapper and Delegation example


Decorator Design Pattern Example

 

Assume this is our existing model.

We wish to allow for :

  • Coffee with dairy
  • Coffee with sweetener
  • Coffee with liqueur

 

If we subclass Expresso then we need to allow for 2 3 - 1 subclasses, which isn’t scalable

 

So an order looks like this (instance diagram)

 

Using the decorator design pattern, we end up with instead

Note that we are still subclassing the additives under Beverage, but we are subclassing the abstract parent, not the concrete child.

The same order as before now looks like this. Note that the instance diagram is not showing the directionality of BeverageDecoratorForBeverage.

 

 

 


Decorator design pattern versus the Decoration analysis Pattern

 

The Decorator design pattern is designed to match types by the client using the abstract component class or interface and by the decorators subclassing the abstract class or implementing the interface respectively.

In an information model that doesn’t have methods we can remove this requirement and just keep the association, giving us the Decoration analysis pattern.

Also the Decoration analysis pattern allows us to decorate concrete classes (otherwise we would need to have abstract classes everywhere.

 

Note that it is also interesting to compare the decoration analysis pattern with the role object pattern [Fowler, Dealing with Roles]. We have the same issue with implementing roles using subclassing, that we end up with a combinatorial explosion of subclasses. The decoration analysis pattern can be seen as a variation of the role object pattern.  Also note that Martin’s paper doesn’t show the option of roles of roles (role chaining) ,

 


Model Examples

This section shows how the decoration pattern is proposed to be used in the ONF model.

ONF PTP Clock

We will start by showing a simple block diagram representation of a PTP Clock.

 

Figure 10 - PTP Clock block diagram

Applying the decoration pattern to the core ONF model gives us the PTP Clock model below.

Figure 11 - PTP Model

The diagram below shows an instance diagram of a clock example.

Figure 12 - PTP Instance Diagram


The text below shows how the right portion of the instance diagram above could be represented (using XML here) on a management interface.

Note :

  • That the composition has been mapped to XML containment
  • That we are containing the decorated instances inside the decorating instances (consistent to how the original inheritance can be thought of)

 

 

< PtpClockFunction clockId = "DO:4:FF:FF:B8:C6:0" domain = 0 clockType = "ORDINARY_CLOCK"
    defaultDataset = "c2ds1" isSlaveOnly = true>

< PcAsymmetric id = "6457a86a-87a6-4979-b445-1cdeef4c450a" name = "Clock 1"
              type = "PTP CLOCK" state = "ENABLED" contextualName = "ne1/ptpClock1"/>

< PtpClockPort synchInterval = 6 minDelayReqInterval = -4>

< PcEndpoint contextualId = "1" state = "ENABLED" role = "PTP_SLAVE“/>

</PtpClockPort>

</PtpClockFunction>

 


ONF ERP

We will start by showing a simple block diagram representation of an ERP Node.

 

Figure 13 - ERP Block Diagram


Applying the decoration pattern to the core ONF model gives us the ERP model below.

Figure 14 - ERP Model

The diagram below shows an instance diagram of an ERP Node example.

Figure 15 – ERP Instance Diagram


The text below shows how the instance diagram above could be represented (using XML here) on a management interface.

Note :

  • That the composition has been mapped to XML containment
  • That we are containing the decorated instances inside the decorating instances (consistent to how the original inheritance can be thought of)
  • Non-containment association ErpInstancePortOnRingPort is instantiated by a reference from the ErpInstancePort to the ErpRingPort using the ErpRingPort contextualId

< ErpNodeCd rapsVersion = "VERSION1" notificationEnabled = false ringSystemControl = "enabled" maxInstancesSupported=20>

    < CdSymmetric id = "ed0a5905-2537-4296-bcc1-01ae39fa9501" name = "Erp Function 1" type = "ERP CD" state = "ENABLED"
        contextualName = "ne1/erpFunction"/>

    < ErpRingNode ringId = 1>

        < PcAsymmetric id = "c7aad536-3432-449c-9a12-c307e9d7ce82" name = "ring1" type = "ERP RING NODE" state = "ENABLED"
           contextualName = "ne1/erpFunction/ring1"/>

            < ErpRingPort portId = "PORT0">

                < PcEndpoint contextualId = "0" state = "ENABLED" role = "RING_PORT"
                    tpeId = "cadaf678-52e1-4db8-a5bd-4dc78f3ad6aa"/>

</ErpRingPort>

< ErpRingPort portId = "PORT1">

    < PcEndpoint contextualId = "1" state = "ENABLED" role = "RING_PORT" tpeId = "297f1741-1f1c-46fe-a155-1a6101ad6f05"/>

</ErpRingPort>

< ErpInstanceNode type = "MAJOR_RING" wrtTimer = 5 guardTimer = 500 holdOffTime = 0 reversion = "REVERTIVE"
           state = "MANUAL_SWITCH">

    < ErpInstancePort rpl = "OWNER" state = "BLOCKING" erpRingPortId = "PORT0"/>

    < ErpInstancePort rpl = "NEIGHBOR" state = "FORWARDING" erpRingPortId = "PORT1"/>

</ErpInstanceNode>

     </ErpRingNode>

</ErpNodeCd>


Antenna Model (Inside – Outside Pattern)

We wish to add Antenna to our Equipment Model.

We note that :

  • We have internal antennae that are non FRU (like a PhysicalConnector – they are really the connector to the ‘ether’)
  • We have external antennae that are plugged into a PhysicalConnector and so are a piece of Equipment
  • We have many attributes common to both internal and external antennae that we don’t want to duplicate

So based on dot points 1 and 3, the following model would make sense.

Based on dot point 1, we would like ExternalAntenna to subclass Equipment, but because it is already subclassing Antenna, we would need to use multiple inheritance.

 

Using the decoration pattern, we can replace one of the multiple inheritance relationships with a decoration association, getting us back to single inheritance.

Also note the impact this has on identity and the semantics of the two concrete antenna classes.

The ExternalAntennaProperties decorator will share the identity of the decorated Equipment instance.

The contained InternalAntenna can be given a localIdentifier (local to Equipment.globalId)