Contents
Adaptor Pattern
The purpose of Adaptor is to fit a class with one interface
into a system, which expects a different interface. As an example, consider an
AddIn to the VB6 IDE. This is a DLL, which exposes a given Interface. We wish
to use that as an AddIn to VS.NET � which requires that it expose a different
interface. The solution is to provide a class, which exposes the Interface
required by VS.NET and delegates calls to an internally created instance of the
original VB6 AddIn.
In general, when integrating a pre-existing object into a
pre-existing system it is unlikely that the interfaces exposed by the object
are the interface(s) required by the system. In this case, you use an Adaptor
pattern.
The GoF book has two ways of implementing this pattern � what
it calls �Class Adaptor� and �Instance Adaptor�. The Class Adaptor requires
multiple inheritance and so I�ll leave it out. The Instance Adaptor has an
internal instance of the object you want to adapt, exposes the interface
required by the system, which wants to use the object, and translates between
the two. Adaptor could also be called �Intelligent Wrapper�.
One example of the Adaptor pattern is the .NET DataAdaptor.
This adapts a DataReader and allows the easy filling and updating of DataSets.
Filling a DataSet does not extend the functionality of DataReader � it
simplifies its interface (in a way that Mediator might). Updating via a
DataAdaptor extends the functionality of DataReader; it can do this only by
knowing the �client� � the DataSet � it is extending it for.
Adaptor, then, takes an existing interface and adapts it for
use in a specific context. In this new context, the existing interface is
either inappropriate or inaccessible, and the pattern makes it available in an
appropriate form to consumers in the new context.
Facade
The purpose of a Facade is to limit interface of a
sub-system to a single interface, which translates calls to it into possibly
multiple calls to objects within the sub-system. This is usually done to
protect a consumer from a complex or large set of objects, which are easy to
misuse, and to protect the sub-system from that misuse.
Facades can also be used to concentrate �orthogonal�
functionality, for example security checks. If all calls to a sub-system go
through one facade then security checks can be concentrated in the facade.
The larger and more complex the sub-system is, the more its
clients depend on it being stable. Using a facade allows the actual
implementation of the functionality to change without necessarily affecting the
interface to it, protecting the clients. This reduces the coupling of the
client to the sub-system and is thus a Good Thing�.
Facades can become all-knowing God-objects. This is not a
Good Thing� and must be guarded against. Perhaps the easiest way to avoid this
is to design and write the Facade object only when the sub-system is fully
written and unit tested. Sadly, I have seen very few facades, which did not end
up with considerable knowledge and intelligence about the sub-system they are
meant to be merely a doorway into.
The following is an example of a facade to a
memory-management sub-system.
public class MemoryFacade {
public MemoryFacade() {}
public int Free {
get { return MemorySingleton.Instance.Free; }
}
public int Used(int user) {
return MemorySingleton.Instance.UsedBy(user);
}
public void AssignRange(int length, int User) {
if (User <= 0) throw new Exception("Zero user");
if (User >= 10) throw new Exception("user out of range");
if (length == 0) throw new Exception("0 range? C'mon!");
MemoryManager.Assign(length, User);
return;
}
public byte[] Read(int user, int start, int length) {
if (user <= 0) throw new Exception("Zero user");
if (user >= 10) throw new Exception("user out of range");
if (length == 0) throw new Exception("0 range? C'mon!");
return MemoryManager.Read(user, start, length);
}
public void Write(int user, int start, byte[] content) {
if (user <= 0) throw new Exception("Zero user");
if (user >= 10) throw new Exception("user out of range");
MemoryManager.Write(user, start, content);
}
public void Dispose() {
MemorySingleton.Clear();
}
}