Introduction
Decorator Design Patterns is a well established way to offer an alternative to subclassing. While subclassing adds behavior at compile time, and the added actions affects all instances of the original class; with Decorator it is possible to add additional functionality to a particular object during run time on an effective usage basis without affecting other objects. Going forward the next step may be to revisit the pattern implementation. In this pattern responsibilities can be added or removed at run time. For this purpose I will take the Decorator Pattern and implement it using lambda expressions shorthand, that allows you to write new behavior in the same place you are going to return a decorated class.
Background
We’ll take an easy and fictional example of the Decorator Pattern: describing a typical sandwich assembly. For a classical and already known real world example, you may also see how this pattern is incorporated inth the I/O Streams implementations of both Java and the .NET Framework.
Using the code
This pattern is designed so that multiple decorators are bundled together, in order to add a new functionality each time one or more methods is overridden. Decorator Design Pattern is a structural design pattern. It is also known as wrapper. Fundamentally the pattern foresee two components where the first is abstract and the second is concrete.
First of all we have the interface that defines our component:
public interface ISandwich
{
string Assemble();
}
Implementing the interface we have a concrete component:
public class BasicSandwich: ISandwich
{
public string Assemble()
{
return "Basic sandwich";
}
}
Then we decide that we have to decorate our component in different ways. And here is where the Decorator Pattern magic happen. We can add new functionality to an existing object without changing its structure.
This is the class acting as base class for all decorators for components:
public abstract class SandwichDecorator: ISandwich
{
protected ISandwich _sandwich;
public SandwichDecorator(ISandwich sandwich)
{
_sandwich = sandwich;
}
public virtual string Assemble()
{
return _sandwich.Assemble();
}
}
This is a couple of classes which inherit from Decorator class and provide a decorator for components:
public class LettuceSandwich: SandwichDecorator
{
public LettuceSandwich(ISandwich sandwich): base(sandwich)
{
}
public override string Assemble()
{
return base.Assemble() + " with lettuce";
}
}
public class HamSandwich: SandwichDecorator
{
public HamSandwich(ISandwich sandwich): base(sandwich)
{
}
public override string Assemble()
{
return base.Assemble() + " with ham";
}
}
And finally here is the way to use the new descriptor classes:
ISandwich sandwich = new LettuceSandwich(new BasicSandwich());
string finishedSandwich = sandwich.Assemble();
sandwich = new LettuceSandwich(new HamSandwich(new BasicSandwich()));
finishedSandwich = sandwich.Assemble();
Similarilly, in .NET BufferedStream
decorates, or wraps, another stream with buffering capability. Buffering improves performance by reducing round trips to the backing store. Here’s how for example we wrap a FileStream in a 20 KB BufferedStream:
File.WriteAllBytes ("myFile.bin", new byte [100000]);
using (FileStream fs = File.OpenRead ("myFile.bin"))
using (BufferedStream bs = new BufferedStream (fs, 20000))
{
bs.ReadByte();
Console.WriteLine (fs.Position);
}
The whole structure depiction of the Descriptor Pattern may be summarized at glance as follow.
Component: ISandwich
is an interface containing members that will be implemented by ConcreteClass and Decorator.
ConcreteComponent: BasicSandwich
is a class which implements the Component interface.
Decorator: SandwichDecorator
is an abstract class which implements the Component interface and contains the reference to a Component instance. This class also acts as base class for all decorators for components.
ConcreteDecorator: LettuceSandwich
and HamSandwich
are classes which inherits from Decorator class and provides a decorator for components.
Now lets see how we can do the same thing using lambdas, and instead of declaring an abstract
class that will provide the template for decorations, we will create the decorator that asks the user for functions with Func<ISandwich, ISandwich>
signature used to decorate the component.
public static class SandwichDecorator
{
public static ISandwich Decorate(ISandwich sandwich, params Func<ISandwich, ISandwich>[] funcs)
{
ISandwich res = sandwich;
foreach (Func<ISandwich, ISandwich> f in funcs)
{
res = f.Invoke(res);
}
return res;
}
}
Ok, now where can we define our decorations? You can add them as static methods in Decorator class or even in the ISandwich
interface writing two extension methods:
public class SandwichFactory
{
private sealed class SandwichImpl : ISandwich
{
internal Func<string> _assemble = null;
public string Assemble()
{
return _assemble();
}
}
public ISandwich Create() {
return new SandwichImpl() { _assemble = () => String.Empty };
}
public ISandwich Create(Func<string> bakeFunc) {
return new SandwichImpl() { _assemble = bakeFunc };
}
}
public static class SandwichDecorators
{
public static ISandwich WithLettuce(this ISandwich @this, ISandwich sandwich = null)
{
return (new SandwichFactory()).Create(() => (sandwich ?? @this).Assemble() + " with lettuce");
}
public static ISandwich WithHam(this ISandwich @this, ISandwich sandwich = null)
{
return (new SandwichFactory()).Create(() => (sandwich ?? @this).Assemble() + " with ham");
}
}
As you may note I have used a factory pattern in order to create a new ISandwich
object instance, this approach is required to workaround a known C# limitation, and this fundamental pattern that belong in every OO developer's vocabulary help us to cope with it in a very elegant way.
Critics says that Decorator Pattern uses a lot of little object of similar type. Trying to reduce poilerplate code, these are the lines that constructs the chain of decorations to be applied:
return (new SandwichFactory()).Create(() => (sandwich ?? @this).Assemble() + " with lettuce");
return (new SandwichFactory()).Create(() => (sandwich ?? @this).Assemble() + " with ham");
Those two lambdas are employed as arguments for the Create
method part of the SandwichFactory
class.
And now, here is how this pattern gets to be used:
var mySandwich = new BasicSandwich();
var mySandwich2 = mySandwich.WithLettuce();
var mySandwich3 = mySandwich.WithHam(mySandwich);
var mySandwich4 = SandwichDecorator.Decorate(mySandwich, mySandwich.WithLettuce, mySandwich.WithHam);
Console.WriteLine(mySandwich.Assemble());
Console.WriteLine(mySandwich2.Assemble());
Console.WriteLine(mySandwich3.Assemble());
Console.WriteLine(mySandwich4.Assemble());
Points of Interest
As you can see, the code got more clear and more concise, and we didn’t use inheritance to build our decorators.
This is just one of the many design pattern that can be improved using lambda expressions. There are more functional features that can be used to improve the solution and many other known patterns as well; in C# functional programming is combined with object oriented programming and you can use a function object as any other object type.
I hope you will enjoy the Decorator Pattern while designing your software. Your valuable feedback, questions, or comments about this article are always welcome.
Bibliography
The fictional Decorator example contained in this article was inspired by this article: Gang of Four – Decorate with Decorator Design Pattern
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)