I am trying to understand the decorator pattern and from the examples I understand how decorator objects can be used to extend existing functionality at runtime by overridin
This is a common misconception of the decorator pattern. What you can do with the decorator pattern is to extend the functionality, not the API.
What does this mean?
It means, you can add new functionality, to the methods provided by the API, in your case IBusinessObject
. Lets say your interface has a method XmlDocument Export()
which is implemented in BusinessObject
and returns the data in the BusinessObject
instance in an XmlDocument
instance.
Now, you could create a LoggingDecorator
, which implements the Export
method like so:
public XmlDocument Export()
{
_logger.Log("Exporting...");
var result = _decoratedObject.Export();
_logger.Log("Done exporting...");
return result;
}
Or you can create a BusinessObjectSigningExportDecorator
which signs the returned XmlDocument
using the xml-dsig algorithm:
public XmlDocument Export()
{
var result = _decoratedObject.Export();
return SignXmlDocument(result);
}
You could then use them together like so:
IBusinessObject businessObject = new LoggingDecorator(
new BusinessObjectSigningExportDecorator(
new BusinessObject()
)
);
var xmlDocument = businessObject.Export();
The call to Export
will now write the log messages and sign the xml export.
But you still can use BusinessObject
without a decorator or with only one of the decorators.
The reason for using the decorator pattern is to be able to transparently add functionality. As you can see, the user of the businessObject
variable of type IBusinessObject
doesn't know and doesn't need to know the actual type that is used. It will work in the case with or without decorators.
Thinking further: When you have a factory that returns IBusinessObject
s you can extend the functionality, without changing the code that uses them and without changing the implementation of the class the implement IBusinessObject
. You just need to create another decorator and "attach" it inside the factory and therefore, you are making a change, that occurs only in a limited area of the code. So if this change breaks anything, you know exactly what code is responsible for the breakage.
Additionally, this enforces separation of concerns, because your normal business object implementation doesn't know and care about which signing algorithm should be used or that one needs to be used altogether.