Decorator
Objective
Implementing additional responsibilities to an object, so dynamic, and provide a flexible alternative to extend the functionality of a subclass.
Function
Facilitate the addition of functionality to a class dynamically.
Structure
The client is always interested in CentralFunctionality.doSomething(). The client may, or may not, be interested in OptionOne.doSomething() and OptionTwo.doSomething(). Each of these classes always delegates to the base class Decorator, and that class always delegates to the object 'wrappee' content.
The structure that meets this pattern is shown in Figure 1
Figure 1: UML Diagram Decorator Pattern
Applications
The use of the Decorator pattern is recommended when:
- The system requires that responsibilities be added dynamically and transparently to individual objects; that is, without affecting the other objects.
- Withdrawal of responsibility without affecting operation is required.
- Extensibility to subclass is impractical. Sometimes a large number of independent extensions is possible and would result in an explosion of subclasses for to support each combination.
Design Patterns Collaborators
- A Decorator pattern is normally considered a single-component generated composite pattern; as a Decorator adds responsibilities additional.
- The Decorator and Strategy patterns work together, in the area of variation of an object; one is in charge of its presentation while the other their form, respectively.
Scope of action
Applied at the object level.
Problem
To add a specific behavior or state to the individually and at run time, it is required to implement the inheritance; without However, this applies to an entire class in a static manner and the customer loses control.
Solution
The Decorator pattern forms the interface of the component that works as the boundary between a class and its respective subclasses, which is transparent to clients, the decorator class refers the order to the component and performs while transparency allows for recursively nesting decorators so that an unlimited number of aggregate liabilities are satisfied dynamically.
Diagram or Implementation
Figure 2: UML Diagram Decorator Pattern
- Client Class performs an operation on the DecoratorA component.
- The component DecoratorA performs the same operation on component DecoratorB.
- The DecoradorB component class performs an action on ConcreteComponent.
- The component type DecoratorB executes a decoration operation.
- The component type DecoratorA executes a decoration operation.
- The Client class receives as a result an object decorated by all the Decorators, which encapsulated the Component in several layers.
Study Cases
Interface System
Figure 3: UML Diagram Interface System
Figure 4: UML Diagram Interface System
Tourist Reservation System
Figure 5: UML Diagram Tourist Reservation System
Figure 6: UML Diagram Tourist Reservation System
Implementations of the decorator pattern:
"""
Attach additional responsibilities to an object dynamically. Decorators
provide a flexible alternative to subclassing for extending
functionality.
"""
import abc
class Component(metaclass=abc.ABCMeta):
"""
Define the interface for objects that can have responsibilities
added to them dynamically.
"""
@abc.abstractmethod
def operation(self):
pass
class Decorator(Component, metaclass=abc.ABCMeta):
"""
Maintain a reference to a Component object and define an interface
that conforms to Component's interface.
"""
def __init__(self, component):
self._component = component
@abc.abstractmethod
def operation(self):
pass
class ConcreteDecoratorA(Decorator):
"""
Add responsibilities to the component.
"""
def operation(self):
# ...
self._component.operation()
# ...
class ConcreteDecoratorB(Decorator):
"""
Add responsibilities to the component.
"""
def operation(self):
# ...
self._component.operation()
# ...
class ConcreteComponent(Component):
"""
Define an object to which additional responsibilities can be
attached.
"""
def operation(self):
pass
def main():
concrete_component = ConcreteComponent()
concrete_decorator_a = ConcreteDecoratorA(concrete_component)
concrete_decorator_b = ConcreteDecoratorB(concrete_decorator_a)
concrete_decorator_b.operation()
if __name__ == "__main__":
main()
class Book {
private $author;
private $title;
function __construct($title_in, $author_in) {
$this->author = $author_in;
$this->title = $title_in;
}
function getAuthor() {
return $this->author;
}
function getTitle() {
return $this->title;
}
function getAuthorAndTitle() {
return $this->getTitle().' by '.$this->getAuthor();
}
}
class BookTitleDecorator {
protected $book;
protected $title;
public function __construct(Book $book_in) {
$this->book = $book_in;
$this->resetTitle();
}
//doing this so original object is not altered
function resetTitle() {
$this->title = $this->book->getTitle();
}
function showTitle() {
return $this->title;
}
}
class BookTitleExclaimDecorator extends BookTitleDecorator {
private $btd;
public function __construct(BookTitleDecorator $btd_in) {
$this->btd = $btd_in;
}
function exclaimTitle() {
$this->btd->title = "!" . $this->btd->title . "!";
}
}
class BookTitleStarDecorator extends BookTitleDecorator {
private $btd;
public function __construct(BookTitleDecorator $btd_in) {
$this->btd = $btd_in;
}
function starTitle() {
$this->btd->title = Str_replace(" ","*",$this->btd->title);
}
}
writeln('BEGIN TESTING DECORATOR PATTERN');
writeln('');
$patternBook = new Book('Gamma, Helm, Johnson, and Vlissides', 'Design Patterns');
$decorator = new BookTitleDecorator($patternBook);
$starDecorator = new BookTitleStarDecorator($decorator);
$exclaimDecorator = new BookTitleExclaimDecorator($decorator);
writeln('showing title : ');
writeln($decorator->showTitle());
writeln('');
writeln('showing title after two exclaims added : ');
$exclaimDecorator->exclaimTitle();
$exclaimDecorator->exclaimTitle();
writeln($decorator->showTitle());
writeln('');
writeln('showing title after star added : ');
$starDecorator->starTitle();
writeln($decorator->showTitle());
writeln('');
writeln('showing title after reset: ');
writeln($decorator->resetTitle());
writeln($decorator->showTitle());
writeln('');
writeln('END TESTING DECORATOR PATTERN');
function writeln($line_in) {
echo $line_in."
";
}
// 1. "lowest common denominator"
interface Widget {
void draw();
}
// 3. "Core" class with "is a" relationship
class TextField implements Widget {
private int width, height;
public TextField(int width, int height) {
this.width = width;
this.height = height;
}
public void draw() {
System.out.println("TextField: " + width + ", " + height);
}
}
// 2. Second level base class with "isa" relationship
abstract class Decorator implements Widget {
// 4. "has a" relationship
private Widget widget;
public Decorator(Widget widget) {
this.widget = widget;
}
// 5. Delegation
public void draw() {
widget.draw();
}
}
// 6. Optional embellishment
class BorderDecorator extends Decorator {
public BorderDecorator(Widget widget) {
super(widget);
}
public void draw() {
// 7. Delegate to base class and add extra stuff
super.draw();
System.out.println(" BorderDecorator");
}
}
// 6. Optional embellishment
class ScrollDecorator extends Decorator {
public ScrollDecorator(Widget widget) {
super(widget);
}
public void draw() {
super.draw(); // 7. Delegate to base class and add extra stuff
System.out.println(" ScrollDecorator");
}
}
public class DecoratorDemo {
public static void main(String[] args) {
// 8. Client has the responsibility to compose desired configurations
Widget widget = new BorderDecorator(new BorderDecorator(new ScrollDecorator(new TextField(80, 24))));
widget.draw();
}
}