Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using multiple decorators to add functionality to an object?

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.

like image 828
Andy Rose Avatar asked Apr 05 '11 11:04

Andy Rose


People also ask

Can we use 2 decorators in Python?

Python allows us to implement more than one decorator to a function.

What is a disadvantage of the decorator pattern?

Disadvantages. High degree of flexibility. High complexity of software (especially decorator interface) Expansion of function of classes without inheritance.

Which design pattern can be used to modify the functionality of an object at runtime?

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.

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?

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.


1 Answers

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 IBusinessObjects 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.

like image 192
Daniel Hilgarth Avatar answered Oct 21 '22 21:10

Daniel Hilgarth