Labels

Tuesday, November 16, 2010

Bridge Design Pattern - Structural

1.1          Intent

Decouple an abstraction from its implementation so that the two can vary independently.
Also known as Handle/Body.

1.2          Motivation

·         Usually we use inheritance to accommodate an abstraction that can have one of several possible implementations.
·         An abstract class defines the interface to the abstraction, and concrete subclasses implement it in different ways.
·         But this approach isn't always flexible enough.
·         Inheritance binds an implementation to the abstraction permanently, making it difficult to modify, extend, and reuse abstractions and implementations independently.



Consider implementing a portable Window abstraction in a user interface toolkit. This abstraction should enable us to write applications that work on both the X Window System and IBM's Presentation Manager (PM), for example. Using inheritance, we could
define an abstract class Window and subclasses XWindow and PMWindow that implement the Window interface for the different platforms. But this approach has two drawbacks:
1. It's inconvenient to extend the Window abstraction to cover different kinds of windows or new platforms. Imagine an IconWindow subclass of Window that specializes the Window abstraction for icons. To support IconWindows for both platforms, we have to implement two new classes, XIconWindow and PMIconWindow. Worse, we'll have to define two classes for every kind of window. Supporting a third platform requires yet another new Window subclass for every kind of window.
2. It makes client code platform-dependent. Whenever a client creates a window, it instantiates a concrete class that has a specific implementation. This, in turn, makes it harder to port the client code to other platforms.

1.3          Structure





1.4          Participants

 The classes and/or objects participating in this pattern are:

Abstraction   
·       Defines the abstraction's interface.
·       Maintains a reference to an object of type Implementor.

RefinedAbstraction   
extends the interface defined by Abstraction.

Implementor   
defines the interface for implementation classes. This interface doesn't have to correspond exactly to Abstraction's interface; in fact the two interfaces can be quite different. Typically the Implementation interface provides only primitive operations, and Abstraction defines higher-level operations based on these primitives.

ConcreteImplementor   
implements the Implementor interface and defines its concrete implementation.

1.5          Applicability

Use the Bridge pattern when:
·         you want to avoid a permanent binding between an abstraction and its implementation, for example, when the implementation must be selected or switched at run-time.
·         both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently.
·         changes in the implementation of an abstraction should have no impact on clients; i.e. their code should not have to be recompiled.
·         you want to hide the implementation of an abstraction completely from clients.
·         you have a proliferation of classes; such a class hierarchy indicates the need for splitting an object into two parts.

1.6          Consequences

The Bridge pattern is intended to keep the interface to your client program constant while allowing you to change the actual kind of class you display or use. This can prevent you from recompiling a complicated set of user interface modules and only require that you recompile the bridge itself and the actual end display class.

You can extend the implementation class and the Bridge class separately, and usually without much interaction with each other.
You can hide implementation details from the client program much more easily.

1.7          Sample Code

This code demonstrates the Bridge pattern which separates (decouples) the interface from its implementation. The implementation can evolve without changing clients which use the abstraction of the object.


class Abstraction
  {
    public  Implementor implementor;

    public virtual void Operation()
    {
      implementor.Operation();
    }
  }
class RefinedAbstraction : Abstraction
  {
    public override void Operation()
    {
      implementor.Operation();
    }
  }


abstract class Implementor
  {
    public abstract void Operation();
  }

class ConcreteImplementorA : Implementor
  {
    public override void Operation()
    {
      Console.WriteLine("ConcreteImplementorA ");
    }
  }
class ConcreteImplementorB : Implementor
  {
    public override void Operation()
    {
      Console.WriteLine("ConcreteImplementorB ");
    }
  }




class MainApp
  {
    static void Main()
    {
      Abstraction ab = new RefinedAbstraction();

      // Set implementation and call
      ab.Implementor = new ConcreteImplementorA();
      ab.Operation();

      // Change implemention and call
      ab.Implementor = new ConcreteImplementorB();
      ab.Operation();   
   }
  }

Output

ConcreteImplementorA Operation
ConcreteImplementorB Operation

1.8          Sample Code2

This real-world code demonstrates the Bridge pattern in which a BusinessObject abstraction is decoupled from the implementation in DataObject. The DataObject implementations can evolve dynamically without changing any clients.





using System;
using System.Collections;

namespace Xpanxion.Bridge.RealWorld
{

    // MainApp test application

    class MainApp
    {
        static void Main()
        {
            // Create RefinedAbstraction
            Customers customers =
              new Customers("Chicago");

            // Set ConcreteImplementor
            customers.Data = new CustomersData();

            // Exercise the bridge
            customers.Show();
            customers.Next();
            customers.Show();
            customers.Next();
            customers.Show();
            customers.New("Henry Velasquez");

            customers.ShowAll();

            // Wait for user
            Console.Read();
        }
    }

    // "Abstraction"

    class CustomersBase
    {
        private DataObject dataObject;
        protected string group;

        public CustomersBase(string group)
        {
            this.group = group;
        }

        // Property
        public DataObject Data
        {
            set { dataObject = value; }
            get { return dataObject; }
        }

        public virtual void Next()
        {
            dataObject.NextRecord();
        }

        public virtual void Prior()
        {
            dataObject.PriorRecord();
        }

        public virtual void New(string name)
        {
            dataObject.NewRecord(name);
        }

        public virtual void Delete(string name)
        {
            dataObject.DeleteRecord(name);
        }

        public virtual void Show()
        {
            dataObject.ShowRecord();
        }

        public virtual void ShowAll()
        {
            Console.WriteLine("Customer Group: " + group);
            dataObject.ShowAllRecords();
        }
    }

    // "RefinedAbstraction"

    class Customers : CustomersBase
    {
        // Constructor
        public Customers(string group)
            : base(group)
        {
        }

        public override void ShowAll()
        {
            // Add separator lines
            Console.WriteLine();
            Console.WriteLine("------------------------");
            base.ShowAll();
            Console.WriteLine("------------------------");
        }
    }

    // "Implementor"

    abstract class DataObject
    {
        public abstract void NextRecord();
        public abstract void PriorRecord();
        public abstract void NewRecord(string name);
        public abstract void DeleteRecord(string name);
        public abstract void ShowRecord();
        public abstract void ShowAllRecords();
    }

    // "ConcreteImplementor"

    class CustomersData : DataObject
    {
        private ArrayList customers = new ArrayList();
        private int current = 0;

        public CustomersData()
        {
            // Loaded from a database
            customers.Add("Jim Jones");
            customers.Add("Samual Jackson");
            customers.Add("Allen Good");
            customers.Add("Ann Stills");
            customers.Add("Lisa Giolani");
        }

        public override void NextRecord()
        {
            if (current <= customers.Count - 1)
            {
                current++;
            }
        }

        public override void PriorRecord()
        {
            if (current > 0)
            {
                current--;
            }
        }

        public override void NewRecord(string name)
        {
            customers.Add(name);
        }

        public override void DeleteRecord(string name)
        {
            customers.Remove(name);
        }

        public override void ShowRecord()
        {
            Console.WriteLine(customers[current]);
        }

        public override void ShowAllRecords()
        {
            foreach (string name in customers)
            {
                Console.WriteLine(" " + name);
            }
        }
    }
}




1.9          Bridge Design Pattern in .NET

Windows forms as Bridges

The .NET visual control is itself an ideal example of a Bridge pattern implementation. A Control is a reusable software component that can be manipulated visually in a builder tool. The entire C# Controls support a query interface that enables builder programs to enumerate their properties and display them for easy modification. Figure below is a screen from Visual Studio.NET displaying a panel with a text field and a check box. The builder panel to the right shows how you can modify the properties of either of those components using a simple visual interface.



In other words, all ActiveX controls have the same interface used by the Builder program, and you can substitute any control for any other and still manipulate its properties using the same convenient interface. The actual program you construct uses these classes in a conventional way, each having its own rather different methods, but from the builder's point of view, they all appear to be the same.

Database Drivers as Bridges

A driver is an object that operates a computer system or an external device according to a well-specified interface. Drivers provide the most common example of the Bridge pattern.
Applications that use drivers are abstractions. The effect of running the application depends on which driver is in place. Each driver is an instance of the Adapter pattern, providing the interface a client expects using the services of a class with a different interface. An overall design that uses drivers is an instance of Bridge [4]. The design separates application development from the development of the drivers that implement the abstract operations on which the applications rely. A driver-based design forces us to create a common, abstract model of the machine or system that will be driven. This has the advantage of letting code on the abstraction side apply to any of the drivers that it might execute against. Defining a common set of methods for the drivers may also incur the disadvantage of eliminating behavior that one driver entity might support.

These tradeoffs in having a broad or narrow interface in an implementation of Bridge also appear in database drivers. For eg., in .NET, we can use the OledbDataReader class to work with almost any database, including SQL Server. However, .NET also provides SqlDataReader that works only with SQL server and is faster than the OleDbDataReader. In addition, the SqlDataReader class offers methods that the OleDbDataReader class does not, such as the GetSqlMoney() method.

Adaptive Rendering Functionality – ASP.NET Server Controls as Bridges

In the .NET Framework Version 2.0, ASP.NET provides a unified control architecture that enables a single set of server controls to work with many different browsers for desktop and  obile devices. This functionality is made possible by pluggable adapter classes, which render markup based on the capabilities of the browser that accesses a page.

In the earlier versions of .NET Framework, ASP.NET mobile Web Forms pages were created using mobile controls. These controls implemented rendering for mobile-device browsers and were distinct from the set of standard ASP.NET server controls. In the new ASP.NET adaptive rendering architecture [16], a separate set of mobile controls does not exist. ASP.NET server controls encapsulate core user interface logic and provide default rendering, which is HTML for desktop browsers. Rendering for other browsers is provided by separate adapter classes, which are not controls. Adapters bind to controls at run time and adapt the output of controls to the requesting browser. Adapters call markup writer classes for creating WML, XHTML, cHTML, and other kinds of markup. Mappings between controls and adapters are specified in configuration files.

An important feature of adapters is that they operate behind the scenes without any work on the part of the page developer. A page developer can use a control without any knowledge of adapters, assuming that the control itself adapts its rendering for different browsers. The ASP.NET page framework enables page developers to use filters to customize control properties for different browsers and devices.

Hope this helps.

Thanks & Regards,
Arun Manglick

No comments:

Post a Comment