Prerequisite
Before reading this article, you should read the first and second part of this series.
Delegation
When a client object X
requests some service from an object Y
, then Y
may decide to pass this request to another object Z
instead of handling the request by itself. When this happens, we say that receiver
object Y
has delegated its operation to its delegate Z
. This process is known as delegation and it’s a way of making object composition as powerful for reuse as inheritance.
Let us now consider an example from this famous book on Design Patterns. Suppose that we want to create a class which will generate a window. The code for this class may look something like:
public class Window
{
public void drawWindow()
{
}
}
This approach is a naive one. A better approach would be to create a Rectangle
class which will contain the logic to generate a simple rectangle and then inherit this Window
class from this Rectangle
and thereby include its functionality.
However, there is another approach. Instead of inheriting this Window
class from Rectangle
, we may delegate the responsibility from this class to another class called DrawRectangleShape
, like this:
public interface IDrawWindow
{
public void drawWindow(Window X);
}
public class DrawRectangleShape implements IDrawWindow
{
public void drawWindow(Window X)
{
}
}
public class Window
{
IDrawWindow windowDrawer;
public Window()
{
this.windowDrawer = new DrawRectangleShape();
}
public void setWindowDrawer(IDrawWindow windowDrawer)
{
this.windowDrawer = windowDrawer;
}
public void drawWindow()
{
this.windowDrawer.drawWindow(this);
}
public void redrawWindow()
{
}
}
Now if you look at the code, you’ll see that the Window
object is passing itself to the delegate so that the delegate can properly generate a rectangular window. This is always the case when the delegate requires a reference to the receiver.
Now why should we use delegation?
The main advantage of delegation is that it makes it easy to compose behaviors at run-time. Consider another implementation of the IDrawWindow
interface:
public class DrawCircularShape implements IDrawWindow
{
public void drawWindow(Window X)
{
}
}
Now consider the following piece of code:
Window newWindow = new Window();
newWindow.drawWindow();
IDrawWindow newWindowDrawer = new DrawCircularShape();
newWindow.setWindowDrawer(newWindowDrawer);
newWindow.redrawWindow();
The above piece of code first generates a rectangular-shaped window. After that, it replaces its default rectangular shape drawer with a circular shape drawer and then redraws itself. As a result, the window will become circular from rectangular at run-time!!!
So using delegation, you can change the behaviors of objects at run-time in this way. A lots of Design Patterns make use of this technique.
Unfortunately, delegation has some disadvantages too. Dynamic, highly parameterized software is typically harder to understand than more static software. If you use delegation, then you will have code that changes behaviors at run-time. Understanding these varying behaviors may prove to be difficult. There are also some run-time inefficiencies since you have to create new objects and transfer responsibilities to them, but this is negligible because most of the modern processors are pretty fast and because of the flexibility it brings to a software design.
So when using delegation, always make sure that it simplifies the design more than it complicates it. Only then will you be able to leverage the true power of delegation.
Conclusion
At this point, you should have a pretty good understanding of what Design Patterns are and what they accomplish. You will find various other definitions on various books and on the web. I think you will now understand all of them. :). Read them on your own. For the time being, I am going to include the definition of Design Patterns given by Christopher Alexander who first introduced the idea:
The elements of this language are entities called patterns. Each pattern describes a problem that occurs over and over again in our environment, and then describes the core of the solution to that problem in such a way that you can use this solution a million times over, without ever doing it the same way twice.
I hope someone finds this series useful :).