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 overriding methods of the decorated object and how they add additional functionality via new method implementations.
Where I am getting a bit confused is where multiple decorators are used and how to access the extended functionality. Typically when looking at examples of the decorator pattern something like this is shown:
IBusinessObject businessObject = new MailDecorator(
new SmsDecorator(
new FaxDecorator(
new BusinessObject()
)
)
);
the idea being to take an object and dynamically add mail, sms and fax functionaliy. Now if you wanted to send a mail which is a method provided by the MailDecorator class you could do something like this:
If(businessObject is MailDecorator)
{
((MailDecorator) businessObject).SendMail();
}
However this would not work if you wanted to send an SMS or fax as in the normal implementation of the decorator pattern you will not have access to the object referenced by the decorator.
Is this a limitation of the decorator pattern or is it beyond the scope of what you can expect to achieve with this pattern or am I completely misunderstanding something here?
Would a different pattern be more appropriate?
The standard definition
“Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality”
implies that this should be achievable with this pattern but appears to break down when using multiple decorators on one object.
Python allows us to implement more than one decorator to a function.
Disadvantages. High degree of flexibility. High complexity of software (especially decorator interface) Expansion of function of classes without inheritance.
Decorator Pattern The decorator design pattern is used to modify the functionality of an object at runtime. At the same time, other instances of the same class will not be affected by this, so individual object gets the modified behavior.
A decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors. Structural design patterns are concerned with how classes and objects can be composed, to form larger structures.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With