Structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.
In this tutorial you will learn about Structural Patterns – Adapter, Bridge, Composite, Decorator, Facade, Flyweight and Proxy.
Structural Patterns
- Adapter:-Match interfaces of different classes.
- Bridge:-Separates an object’s abstraction from its implementation.
- Composite:-A tree structure of simple and composite objects.
- Decorator:-Add responsibilities to objects dynamically.
- Facade:-A single class that represents an entire subsystem.
- Flyweight:-A fine-grained instance used for efficient sharing.
- Proxy:-An object representing another object.
Many times two classes are incompatible because of incompatible interfaces. Adapter pattern helps us to wrap a class around the existing class and make the classes compatible with each other. There are two way of implementing adapter pattern one is by using aggregation (this is termed as the object adapter pattern) and the other inheritance (this is termed as the class adapter pattern).
Example :
// Existing class.. this may be your legacy code or third party code class Adaptee { // validates the email public bool IsEmail(string email) { return System.Text.RegularExpressions.Regex.IsMatch(email, @"email validation reg expression here"); } } // Required standard implementing through interface interface ITarget { // Rough estimate required bool ValidateEmail(string email); } // Implementing the required standard via Adaptee class Adapter : Adaptee, ITarget { public bool ValidateEmail(string email) { return IsEmail(email); } } //Application // Showing the Adaptee in standalone mode Adaptee first = new Adaptee(); bool valid = first.IsEmail("rajneeshnoonia@gmail.com")); // What the client really wants // Note that Adaptee (3rd party or legacy code) is not used here,and Adapter is wraper for // Adaptee accessed via standard interface ITarget.If in future we found better adaptee we would not be require // to change client code. ITarget second = new Adapter(); valid = second.ValidateEmail("rajneeshnoonia@gmail.com"));
Bridge pattern helps to decouple abstraction from implementation. With this if the implementation changes it does not affect abstraction and vice versa. The switch is the abstraction and the electronic equipments are the implementations. The switch can be applied to any electronic equipment, so the switch is an abstract thinking while the equipments are implementations.
interface IDraw { void DrawCircle(double x, double y, double radius); } class DrawGDI : IDraw { public void DrawCircle(double x, double y, double radius) { //Draw Circle using GDI API } } class DrawDirectX : IDraw { public void DrawCircle(double x, double y, double radius) { //Draw Circle using DirectX API } } interface Shape { void Draw(); } class Circle:Shape { private double x, y, radius; private IDraw display; public Circle(double x, double y, double radius, IDraw display) { this.x = x; this.y = y; this.radius = radius; this.display = display; } public void Draw() { this.display.DrawCircle(this.x, this.y, this.radius); } } //Application List<Shape> shapes = new List<Shape>(); shapes.Add(new Circle(10,10,20,new DrawGDI())); shapes.Add(new Circle(40,10,20,new DrawDirectX())); foreach (Shape shape in shapes) { shape.Draw(); }
Composite pattern allows treating different objects in a similar fashion. In order to treat objects in a uniformed manner we need to inherit them from a common interface.The Composite Design pattern allows a client object to treat both single components and collections of components identically.
Example :
interface IGraphics { void Draw(); } class Circle : IGraphics { public void Draw() { //Draw Circle } } class Composite : IGraphics { private List<IGraphics> childs = new List<IGraphics>(); public Composite(IEnumerable<IGraphics> composite) { childs.AddRange(composite); } public void Draw() { //Draw composite graphics foreach (IGraphics child in this.childs) { child.Draw(); } } } //Application //Build tree of objects Composite CGraphics1 = new Composite(new IGraphics[]{new Circle(),new Circle()}); Composite CGraphics2 = new Composite(new IGraphics[]{CGraphics1,new Circle(),new Circle()}); Composite CompositeGraphic = new Composite(new IGraphics[]{CGraphics2,new Circle()}); CompositeGraphic.Draw();
Decorator pattern is a design pattern that allows new/additional behaviour to be added to an existing object dynamically.
The decorator pattern can be used to make it possible to extend (decorate) the functionality of a certain object at runtime, independently of other instances of the same class, provided some groundwork is done at design time. This is achieved by designing a new decorator class that wraps the original class.
Decorator Patterns belong to the Structural Pattern category and it’s role is in providing a way of attaching new behavior to the object at run time. The object is unaware of the new behavior and this pattern is a good candidate for enhancing legacy applications. Decorator Pattern provides a way of adding functionality to an existing class without using inheritance.
Example :
Let us assume we are maintaining an application for a car manufacturing company. When the application was created, the company manufactured only one type of car – the normal car.
interface ICar { string Description {get;} double Price{ get;} } class NormalCar:ICar { public string Description { get { return "Normal Car"; } } public double Price { get { return 2000f; } } }
Over several years, the customers demanded that they be provided with different options to customize the car. The management decided to provide two different options. One to customize the paint based on the customer needs and the other an option to fit the car with a turbo charged engine.
To enhance the application to support customizations, we could extend the Normal Car’s functionality by creating derived classes such as CustomPaintCar or TurboEngineCar. In future, if the management decides to provide more options, it would be practically impossible to create classes that can be used for different combinations.
By using the Decorator pattern, we could combine different options at run time. First, we need to create a Decorator base class.
abstract class CarDecorator:ICar { private ICar car; public CarDecorator(ICar car) { this.car=car; } public virtual string Description { get { return this.car.Description; } } public virtual double Price { get { return this.car.Price; } } } class CustomPaintCar :CarDecorator { public CustomPaintCar(ICar car):base(car){} public override string Description { get { return base.Description + " with custom paint"; } } public override double Price { get { return base.Price + 1000f; } } } class TurboEngineCar :CarDecorator { public TurboEngineCar(ICar car):base(car){} public override string Description { get { return base.Description + " with turbo engine"; } } public override double Price { get { return base.Price + 2000f; } } }
//Application
//Normal or basic car with ICar basicCar = new NormalCar(); //exiting car = normal car + custom paint ICar exitingCar = new CustomPaintCar(new NormalCar()); //amazing car = normal car + custom paint + turbo engine ICar amazingCar = new TurboEngineCar(new CustomPaintCar(new NormalCar()));
Facade pattern sits on the top of group of subsystems and allows them to communicate in a unified manner.
The classes and/or objects participating in this pattern are:
Facade (MortgageApplication)
- knows which subsystem classes are responsible for a request.
- delegates client requests to appropriate subsystem objects.
Subsystem classes (Bank, Credit, Loan)
- implement subsystem functionality.
- handle work assigned by the Facade object.
- have no knowledge of the facade and keep no reference to it.
Example :
// The 'Subsystem ClassA' class class SubSystemOne { public void MethodOne() { } } // The 'Subsystem ClassB' class class SubSystemTwo { public void MethodTwo() { } } // The 'Subsystem ClassC' class class SubSystemThree { public void MethodThree() { } } // The 'Subsystem ClassD' class class SubSystemFour { public void MethodFour() { } } // The 'Facade' class class Facade { private SubSystemOne _one; private SubSystemTwo _two; private SubSystemThree _three; private SubSystemFour _four; public Facade() { _one = new SubSystemOne(); _two = new SubSystemTwo(); _three = new SubSystemThree(); _four = new SubSystemFour(); } public void MethodA() { _one.MethodOne(); _two.MethodTwo(); _four.MethodFour(); } public void MethodB() { _two.MethodTwo(); _three.MethodThree(); } } //Application Facade facade = new Facade(); facade.MethodA(); facade.MethodB();
Fly weight pattern is useful where we need to create many objects and all these objects share some kind of common data. Consider ‘Objects and common data’. We need to print visiting card for all employees in the organization. So we have two parts of data one is the variable data i.e. the employee name and the other is static data i.e. address. We can minimize memory by just keeping one copy of the static data and referencing the same data in all objects of variable data. So we create different copies of variable data, but reference the same copy of static data. With this we can optimally use the memory.
Example : Support you have a project to create employee ID card.The variable data here will be employee name and employee ID where as the common or shared data here will be Company address.
//abstract address public abstract class Address { public abstract string Address1 { get; set; } } //concreate address internal class CompanyAddress : Address { string address; public override string Address1 { get { return address; } set { address = value; } } } //singleton pattern to have one instance of address public sealed partial class Application { public static Address CompAddress { get; private set; } static Application() { CompAddress = new CompanyAddress(); CompAddress.Address1 = "Company Address"; } } //abstract IDCard public abstract class IDCard { public string Name { get; set; } public int EmpCode { get; set; } public abstract Address CompanyAddress { get;} } //Concreate implementation of IDCard public class EmployeeIDCard : IDCard { public override Address CompanyAddress { get { return Application.CompAddress; } } } //Application List cards = new List(); for (int empCode = 1; empCode <= 2000; empCode++) { cards.Add(new EmployeeIDCard { EmpCode=empCode,Name = String.Format("Emp{0} Name",empCode)}); }
Proxy pattern fundamentally is a class functioning as in interface which points towards the actual class which has data. This actual data can be a huge image or an object data which very large and can not be duplicated. So you can create multiple proxies and point towards the huge memory consuming object and perform operations. This avoids duplication of the object and thus saving memory. Proxies are references which points towards the actual object. The advantages of using proxy are security and avoiding duplicating objects which are of huge sizes. Rather than shipping the code we can ship the proxy, thus avoiding the need of installing the actual code at the client side. With only the proxy at the client end we ensure more security. Second point is when we have huge objects it can be very memory consuming to move to those large objects in a network or some other domain. So rather than moving those large objects we just move the proxy which leads to better performance.
Example :
A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.
interface IImage { void Display(); } class RealImage : IImage { public RealImage(string fileName) { FileName = fileName; LoadFromFile(); } private void LoadFromFile() { Console.WriteLine("Loading " + FileName); } public String FileName { get; private set; } public void Display() { Console.WriteLine("Displaying " + FileName); } } class ProxyImage : IImage { public ProxyImage(string fileName) { FileName = fileName; } public String FileName { get; private set; } private IImage image; public void Display() { if (image == null) image = new RealImage(FileName); image.Display(); } } //Application IImage image = new ProxyImage("HiRes_Image"); for (int i = 0; i < 10; i++) image.Display();
One thought on “Structural Design Patterns”