473,569 Members | 2,741 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Patterns: the Decorator pattern

11,448 Recognized Expert MVP
Greetings,

last week we talked a bit about the Visitor design pattern. This week we'll
talk a bit about additional functionality that is sometimes wanted, i.e.
the functionality is optional. Assume there is a lot of optional functionality
that people want.

This article discusses the Decorator (or 'Wrapper') pattern. For the sake of
the example we'll use array manipulation. People always fiddle diddle with
arrays, i.e. they copy values from one array to another, move elements around
in arrays etc. etc. as if there were no Object Oriented programming.

Since the early days of Fortran people have been messing around with arrays
and mutilating those poor processors that have to copy data over and over
all the time.

Let's do something about it: The following interface is an abstraction of
an array:
Expand|Select|Wrap|Line Numbers
  1. public interface Sequence<T> {
  2.     public int length();
  3.     public T get(int idx);
  4.     public void set(T, int idx);
  5. }
A Sequence has a length and we can get and set elements in a Sequence; nothing
spectacular. Note that this interface is a generic interface: the Sequence
manipulates elements of type 'T'.

Let's encapsulate a simple array:
Expand|Select|Wrap|Line Numbers
  1. public class ArraySequence<T> implements Sequence<T> {
  2.  
  3.     // the encapsulated array:
  4.     private T[] array;
  5.  
  6.     // the constructor:
  7.     public ArraySequence(T[] array) { this.array= array; }
  8.  
  9.     // interface implementation:
  10.     public int length() { return array.length; }
  11.     public T get(int idx) { return array[idx]; }
  12.     public void set(T elem, int idx) { array[idx]= elem; }
  13. }
Why build all that code for just a simple array? We could've used the array
itself in the first place, so why all this complicated stuff? This is why:
sometimes we want to manipulate arrays but we don't want to copy and move
and swap all those elements over and over again: we build Decorators for it
instead. The ArraySequence class does nothing special: it simply encapsulates
an array for us. Let's build an abstract class from which we can easily build
other concrete Sequence implementations :
Expand|Select|Wrap|Line Numbers
  1. public abstract class AbstractSequence<T> implements Sequence<T> {
  2.     // another sequence:
  3.     protected Sequence<T> seq;
  4.  
  5.     // an abstract method that gives us an index value:
  6.     protected abstract int index(int idx);
  7.  
  8.     // the constructor:
  9.     public AbstractSequence(Sequence<T> seq) { this.seq= seq; }
  10.  
  11.     // interface implementation:
  12.     public int length() { return seq.length(); }
  13.     public T get(int idx) { return seq.get(index(idx)); }
  14.     public void set(T elem, int idx) { seq.set(elem, index(idx)); }
  15. }
The class by itself can't do anything (it's abstract), but it gives us all
the functionality we need for concrete Sequence implementations . The following
Sequence treats all elements of a Sequence in reverse order:
Expand|Select|Wrap|Line Numbers
  1. public class ReverseSequence<T> extends AbstractSequence<T> {
  2.     // implementation of index() method:
  3.     protected int index(int idx) { return seq.length()-idx-1; }
  4.  
  5.     // the constructor:
  6.     public ReverseSequence(Sequence<T> seq) { super(seq); }
  7. }
You see no interface implementation in this class because everything already
was implemented by the AbstractSequenc e; all that this class implements is
the index() method which gives the additional functionality. Note that the
encapsulated 'seq' Sequence is passed to the superclass in the constructor.

If you create a ReverseSequence , given another Sequence you can treat the
other Sequence as if it were reversed (i.e. last element first and vice versa).
The added functionality is implemented in the index() method. Here's a bit
of code that shows how it's used:
Expand|Select|Wrap|Line Numbers
  1.     Integer[] a= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
  2.     Sequence rev= new ReverseSequence<Integer>(
  3.                 new ArraySequence<Integer>(a));
  4.     ...
  5.     for (int i= 0, n= rev.length(); i < n; i++)
  6.         System.out.print(rev.get(i)+" ");
  7.     System.out.println();
The output is: 9 8 7 6 5 4 3 2 1 0
From the snippet above you can see why a Decorator is also named a Wrapper:
the Sequence objects 'wrap' other Sequence objects (passed to the constructors).
Also note from the snippet above that our code didn't touch the array directly
anymore after we've wrapped it in a Sequence.

Here's another Sequence implementation: it puts the elements from the first
part of the other Sequence interleaved with the elements from the last part
of the sequence:
Expand|Select|Wrap|Line Numbers
  1. public class MergeSequence<T> extends AbstractSequence<T> {
  2.     // implementation of index() method:
  3.     protected int index(int idx) { 
  4.         if ((idx&1) == 0) return idx/2; // idx is even
  5.         return (seq.length()+idx)/2; // idx is odd
  6.     }
  7.  
  8.     // the constructor:
  9.     public MergeSequence(Sequence<T> seq) { super(seq); }
  10. }
If you use the code snippet above but use a MergeSequence instead of a
ReverseSequence the output will be: 0 5 1 6 2 7 3 8 4 9. I leave it up to
you what the output will be if you wrap your original array in both an
MergeSequence and a ReverseSequence as in:
Expand|Select|Wrap|Line Numbers
  1. Sequence magic= new MergeSequence<Integer>(
  2.             new ReverseSequence<Integer>(
  3.                 new ArraySequence<Integer>(a)));
I also leave it up to you to figure out if it would make any difference if
you'd used this wrapping order instead:
Expand|Select|Wrap|Line Numbers
  1. Sequence magic= new ReverseSequence<Integer>(
  2.             new MergeSequence<Integer>(
  3.                 new ArraySequence<Integer>(a)));
Note that if you wanted to do what these two Wrappers do using just a simple
array you have to play clever tricks and/or use temporary array(s) to hold the
intermediate results.

Here's a bit more complicated Sequence: it catenates two other sequences as
if they were just one bigger Sequence. We do have some work to do in the
implementation of the interface methods for this class so we don't
extend from the AbstractSequenc e. Here goes:
Expand|Select|Wrap|Line Numbers
  1. public class CatenateSequence<T> implements Sequence<T> {
  2.     // the first and second Sequence:
  3.     private Sequence<T> first, second;
  4.  
  5.     // little helper method:
  6.     private boolean isFirst(int idx) { return idx < first.length(); }
  7.  
  8.     // implementation of index() method:
  9.     private int index(int idx) { 
  10.         if (isFirst(idx)) return idx;
  11.         return idx-first.length();
  12.     }
  13.  
  14.     // the constructor:
  15.     public CatenateSequence(Sequence<T> first, Sequence<T> second) {
  16.         this.first = first;
  17.         this.second= second;
  18.     }
  19.  
  20.     // interface implementation:
  21.     public int length() { return first.length()+second.length(); }
  22.     public T get(int idx) { 
  23.         if (isFirst(idx)) return first.get(index(idx));
  24.         return second.get(index(idx)); 
  25.     }
  26.     public void set(T elem, int idx) {
  27.         if (isFirst(idx)) first.set(elem, index(idx));
  28.         else second.set(elem, index(idx));
  29.     }
  30. }
The last piece of code shows how we can catenate three arrays together
using the CatenateSequenc e Wrapper:
Expand|Select|Wrap|Line Numbers
  1. Integer[] a= { 0, 1, 2 };
  2. Integer[] b= { 3, 4, 5 };
  3. Integer[] c= { 6, 7, 8, 9 };
  4. ...
  5. Sequence cat= new CatenateSequence<Integer>(
  6.             new ArraySequence<Integer>(a),
  7.             new CatenateSequence<Integer>(
  8.                 new ArraySequence<Integer>(b),
  9.                 new ArraySequence<Integer>(c)));
If you print this Sequence, the output again will be: 0 1 2 3 4 5 6 7 8 9.

Note that sneakily I used another design pattern too for the CatenateSequenc e
class: the Composite pattern. But we'll talk in a next tip about that one.

Guess what a mess we can make if we wrap the CatenateSequenc e in the other
Sequences again. Note that not a single array element is moved or copied.
We add functionality at will by wrapping up a Sequence in another Sequence
that implements the functionality for us. We can wrap Sequences in any
order we want and thus we can accomplish any functionality we want. We now
have three classes:

1: ReverseSequence
2: MergeSequence
3: CatenateSequenc e

If we had implemented any or all of those functionalities using separate
classes we would've ended up with a big number of classes. Now we only
have three of them which we can combine in any order we want and as many
as we want. (I've excluded the ArraySequence class in the count because we
always need it to encapsulate raw, simple arrays).

The Readers, Writers, InputStreams and OutputStreams in the Java core classes
are implemented in a similar way: you wrap (or 'decorate') byte or character
streams using all these wrappers. Each wrapper adds a bit of functionality if
you need it.

Next time we talk a bit about the why and when we can or cannot use inheritance
of implementation (extend a class from another one). The results may be
surprising now and then.

kind regards,

Jos
Apr 29 '07 #1
0 6557

Sign in to post your reply or Sign up for a free account.

Similar topics

8
1606
by: NKOBAYE027 | last post by:
Hi Folks: Can anyone direct me to a good resource on design patterns. I'd like to use them for a project I'm working on regarding set theory - in particular I'd like to use the Decorator pattern to present an interface for a 'sortable' set template derived from the default unsorted set template. And, I'd like to have a Factory pattern used as...
2
4534
by: Tim Smith | last post by:
Dear All, Silly question, but I am having trouble understanding the diagram on the inside back cover entitled "Design Pattern Relationships." It shows the relationships between all of the design patterns. Unfortunately, the book doesn't exactly explain what it all means. Although it looks very pretty - would anyone give me some...
8
1683
by: Alex Vinokur | last post by:
Any links to get started with Design Patterns, including samples? Thanks in advance. Alex Vinokur email: alex DOT vinokur AT gmail DOT com http://mathforum.org/library/view/10978.html http://sourceforge.net/users/alexvn
11
4290
by: FluffyCat | last post by:
In Febraury - April of 2002 I put together in Java examples of all 23 of the classic "Gang Of Four" design patterns for my website. Partly I wanted to get a better understanding of those patterns. They are still online at http://www.fluffycat.com/java/patterns.html Since September 2003 I've mainly been using PHP, and now that PHP 5 is...
12
7022
by: Steve Jorgensen | last post by:
The classing Visual Basic and VBA support for polymorphism, let's face it, is a bit on the weak side, and built-in support for inheritance is non-existent. This little essay is about some patterns I've ended up using successfully for certain kinds of inheritance and polymorphism, and some that have not worked out so well. Let's start with...
3
4094
by: yb | last post by:
Hi, I just started reading design patterns and looking at the Lexi example. I'm very new to this so please bear with me. I understand the Decorator pattern, but a bit confused by the Maze example used for creational patterns. Is this example a bit contrived? I would have thought a decorator pattern would make more sense instead of...
0
1985
by: AMDRIT | last post by:
I am looking for better concrete examples, as I am a bit dense, on design patterns that facilitate my goals. I have been out to the code project, planet source code, and microsoft's patterns and practices site and it just isn't sinking in all that clearly to me. Currently we have code in production and it all works well, however it is...
9
3449
by: Christian Hackl | last post by:
Hi! I've got a design question related to the combination of the NVI idiom (non-virtual interfaces, ) and popular object-oriented patterns such as Proxy or Decorator, i.e. those which have the basic idea of deriving from a base class and delegating to an object of it at the same time. My problem is that I cannot seem to combine those two...
9
1623
by: Tyno Gendo | last post by:
Hi I'm trying to learn patterns, which I hope to use in my PHP code, although I'm finding it hard to get any real impression of how patterns fit in properly, I've done the following test code for Decorator pattern and want to know: a) is it correct, this is decorator pattern? b) how would i use this in practice with a database, eg. how...
0
7698
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7612
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language...
1
7673
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For...
0
7970
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the...
1
5513
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes...
0
3653
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in...
0
3640
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
1213
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
937
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.