| |
|
By
: Roy Saberon (MCSD) |
| |
|
Microsoft
Practice Manager |
|
It's an amazing fact that once
you get to understand the Concept of Designing
your software using Patterns, you will be accustomed
to think in patterns even in your encoding/programming
development style. Design Patterns
are like recurring solutions to software design problems
you find again and again in real-world application
development. Design Patterns
just simply work ! |
|
While messing around with the Decorator,
I found that I can provide additional behaviors
to my objects without resorting
to deep hierarchical subclassing. It's like
bestowing Objects with additional
power in your Controls.
|
If
super heroes, put their unique abilities in commercial
market, I'll be shopping around for Spiderman's skill
of traveling with his power of webs; but of course,
without using a car that can save me lot of money. |
|
Superman
may also have something in market too, and will definitely
be going after his bullet repelling power for a safe
vacation to beautiful destinations. Having the power
of congress to make laws is also desirable; if with
such power, I want to make sure that no one in this
planet sells Kryptonite. After all these imaginary
explanation of powers, let's get back to Decorators.
They are also known as Wrappers.
Imagine having a simple guy wrapped with some webs
and impenetrable powers; it is like having an invincible
Super Spider, isn't it! Really going back to decorators
now.... |
|
The GOF (Gang of Four) says that Decorators
allow you to attach additional responsibilities (think
powers) to an object dynamically. Decorators
provide a flexible alternative to subclassing
for extending functionality. |
|
Here is the diagram: |
| |
|
|
 |
| |
Click
Image to view the full size |
| |
Now
let us dig into the Participants of this
Pattern : |
 |
The Component defines the interface
for objects that can have responsibilities added to
them dynamically |
| |
 |
The Concrete Component defines
an object to which additional responsibilities can
be attached |
 |
The Decorator maintains a reference
to a Component object and defines an interface that
conforms to Component's interface |
| |
 |
The Decorator maintains a reference
to a Component object and defines an interface that
conforms to Component's interface |
| |
 |
The
Concrete Decorator adds responsibilities to the component |
| |
Note that the Decorator
is a Component, and holds
or wraps and instance of another Component. Decorator
forwards requests to its Component object, and may
optionally perform additional operations before and
after forwarding the request |
| |
References: |
| |
* Design Patterns Elements of Reusable
Object Oriented Software by Gamma, Helm, Johnson,
Vlissides |
| |
Implementation |
| |
The
Control requirements are as follows: |
 |
Simple
rectangular box control |
 |
Background
color power |
 |
Text displaying power |
 |
Icon showing power |
| |
Now
the solution is, we can actually make a simple control
and provide properties for the second, third and fourth
requirements, but we do not want to have those powers
attached when its not need. So the solution should
be such that it will allow the user to select a few
or all of those powers according to the requirement
and wish of the user. |
| |
There are times even when the invincible
Super Spider just wants to live a life like a simple
guy; he just wants to travel with his feet and get
a feel of those smuggled Kryptonites. Here I mean
to say, when you have more power, your responsibility
also increases. |
| |
Thus
we designed our solution using the Decorator
Pattern in such a manner to balance
the usage of power and responsibility at the same
time: |
| |
|
|
 |
| |
Click
Image to view the full size |
| |
Matching
the Decorator Pattern Participants,
we have : |
| |
|
|
CellBase |
Component |
An
abstract class |
TableCell |
ConcreteComponent |
|
| CellDecoratorBase |
Decorator |
An
abstract class |
| FillableCellDecorator |
ConcreteDecorator |
Additional
Behavior |
| BorderedCellDecorator |
ConcreteDecorator |
Additional
Behavior |
| TextCellDecorator |
ConcreteDecorator |
Additional
Behavior |
| IconCellDecorator |
ConcreteDecorator |
Additional
Behavior |
|
| |
To
implement our control in C#.Net, we first define our
abstract class Component, CellBase. |
| |
|
public abstract class CellBase
{
#region Properties
#endregion
// Provide basic painting
of control.
public virtual void Draw(System.Drawing.Graphics graphics,
int cellWidth, int cellHeight)
{
// Get the rectangle bounds.
Rectangle r = GetRectangle(cellWidth, cellHeight);
// Fill the rectangle
with default silver color.
graphics.FillRectangle(Brushes.Silver, r);
// Draw a simple border.
graphics.DrawRectangle(Pens.Black, r);
}
// Helper method.
Use to get the bounds of this control
protected virtual Rectangle GetRectangle(int cellWidth,
int cellHeight)
{
Rectangle r = new Rectangle(columnNo * cellWidth,
rowNo * cellHeight, cellWidth, cellHeight);
return r;
}
// Helper method.
Use to check whether this control
// has been clicked.
public bool Intersects(int x, int y)
{
Rectangle r = GetRectangle(100, 50);
Rectangle r1 = new Rectangle(x, y, 1, 1);
return (r1.IntersectsWith(r));
}
}
|
| |
TableCell,
the ConcreteComponent class,
implements the CellBase |
| |
|
public class TableCell : CellBase
{
public override string ToString()
{
return "I am a simple TableCell";
}
}
|
| |
Then
we define CellDecoratorBase,
which is the abstract Decorator class. |
| |
|
public abstract class CellDecoratorBase
: CellBase
{
protected CellBase cell;
public CellDecoratorBase(CellBase cell)
{
this.cell = cell;
this.TableNo = cell.TableNo;
this.RowNo = cell.RowNo;
this.ColumnNo = cell.ColumnNo;
}
// Forward requests
to contained component.
public override void Draw(System.Drawing.Graphics
graphics,
int cellWidth, int cellHeight)
{
cell.Draw(graphics, cellWidth, cellHeight);
}
// Forward requests to contained component.
public override int TableNo
{
get{return cell.TableNo;}
set{cell.TableNo = value;}
}
// Forward requests
to contained component.
public override int ColumnNo
{
get{return cell.ColumnNo;}
set{cell.ColumnNo = value;}
}
// Forward requests
to contained component.
public override int RowNo
{
get{return cell.RowNo;}
set{cell.RowNo = value;}
}
}
|
| |
Here
now is the FillableCellDecorator,
the power to fill a background color |
| |
|
public class FillableCellDecorator
: CellDecoratorBase
{
private Color backColor;
public FillableCellDecorator(Color backColor, CellBase
cell)
: base(cell)
{
this.backColor = backColor;
}
public override void Draw(System.Drawing.Graphics
graphics, int cellWidth, int cellHeight)
{
// Call base method
base.Draw(graphics, cellWidth, cellHeight);
// Fill rectangle.
Rectangle r = base.GetRectangle(cellWidth, cellHeight);
// Make sure we don't
fill the borders.
r.Inflate(-1, -1);
r.Width += 1;
r.Height += 1;
SolidBrush brush = new SolidBrush(backColor);
graphics.FillRectangle(brush,
r);
brush.Dispose();
}
public override string ToString()
{
string str = cell.ToString();
str += "\nBestowed with FillableCellDecorator
power,";
str += "\n i can now hide myself in a background
color.";
return str;
}
}
|
| |
Then
our TextCellDecorator, the
power to draw text, will be like this. |
| |
|
public class TextCellDecorator
: CellDecoratorBase
{
private Color foreColor;
private string text;
public TextCellDecorator(string text, Color foreColor,
CellBase cellBase)
: base(cellBase)
{
this.text = text;
this.foreColor = foreColor; }
public override void Draw(Graphics
graphics, int cellWidth, int cellHeight)
{
// Call contained cell's Draw method.
base.Draw(graphics, cellWidth, cellHeight);
RectangleF r = (RectangleF)GetRectangle(cellWidth,
cellHeight);
r.Inflate(-3, -3);
SolidBrush brush = new SolidBrush(foreColor);
graphics.DrawString(text, new Font("Arial",
14f), brush, r);
brush.Dispose();
}
public override string ToString()
{
string str = cell.ToString();
str += "\nIt's nice to have a TextCellDecorator
power,";
str += "\n can you read my text?";
return str;
}
}
|
| |
|
| |
|
|
|
|
| |
|
|
|
|
| |
|
|