Chapter 15. The Bridge Pattern

At first sight, the Bridge pattern looks a lot like the Adapter pattern in that a class is used to convert one kind of interface to another. However, the intent of the Adapter pattern is to make one or more classes’ interfaces look the same as that of a particular class. The Bridge pattern is designed to separate a class’s interface from its implementation so you can vary or replace the implementation without changing the client code.

The participants in the Bridge pattern are the Abstraction, which defines the class’s interface; the Refined Abstraction, which extends and implements that interface; the Implementor, which defines the interface for the implementation classes; and the ConcreteImplementors, which are the implementation classes.

Suppose we have a program that displays a list of products in a window. The simplest interface for that display is a simple ListBox. But once a significant number of products have been sold, we may want to display the products in a table along with their sales figures.

Since we have just discussed the Adapter pattern, you might think immediately of the class-based Adapter, where we adapt the interface of the ListBox to our simpler needs in this display. In simple programs, this will work fine, but as we’ll see, there are limits to that approach.

Let’s further suppose that we need to produce two kinds of displays from our product data: a customer view that is just the list of products we’ve mentioned and an executive view that also shows the number of units shipped. We’ll display the product list in an ordinary ListBox and the executive view in a DataGrid table display. These two displays are the implementations of the display classes, as shown in Figure 15-1.

Figure 15-1. Two displays of the same information using a Bridge pattern

image

The Bridger Interface

Now we want to define a single interface that remains the same regardless of the type and complexity of the actual implementation classes. We’ll start by defining a Bridger interface.


//Bridge interface to display list classes
      public interface Bridger   {
             void addData(ArrayList col);
      }

This class just receives an ArrayList of data and passes it on to the display classes.

We also define a Product class that holds the names and quantities and parses the input string from the data file.


public class Product : IComparable      {
      private string quantity;
      private string name;
      //-----
      public Product(string line)              {
             int i = line.IndexOf ("--");
             name =line.Substring (0, i).Trim ();
             quantity = line.Substring (i+2).Trim ();
      }
      //-----
      public string getQuantity() {
             return quantity;
      }
      //-----
      public string getName() {
             return name;
      }
}

On the other side of the bridge are the implementation classes, which usually have a more elaborate and somewhat lower-level interface. Here we’ll have them add the data lines to the display one at a time.


public interface VisList  {
      //add a line to the display
      void addLine(Product p);
      //remove a line from the display
      void removeLine(int num);
}

The bridge between the interface on the left and the implementation on the right is the ListBridge class, which instantiates one or the other of the list display classes. Note that it implements the Bridger interface for use of the application program.


public class ListBridge : Bridger      {
      protected VisList vis;
      //------
      public ListBridge(VisList v)           {
             vis = v;
      }
      //-----
      public virtual void addData(ArrayList ar) {
             for(int i=0; i< ar.Count ; i++) {
                    Product p = (Product)ar[i];
                    vis.addLine (p);
             }
      }
}

Note that we make the VisList  variable protected and the addData method virtual so we can extend the class later. At the top programming level, we just create instances of a table and a list using the ListBridge class.


private void init() {
      products = new ArrayList ();
      readFile(products); //read in the data file
      //create the product list
      prodList = new ProductList(lsProd);
      //Bridge to product VisList
      Bridger lbr = new ListBridge (prodList);
      //put the data into the product list
      lbr.addData (products);
      //create the grid VisList
      gridList = new GridList(grdProd);
      //Bridge to the grid list
      Bridger gbr = new ListBridge (gridList);
      //put the data into the grid display
      gbr.addData (products);
}

The VisList Classes

The two VisList classes are really quite similar. The customer version operates on a ListBox and adds the names to it.


//A VisList class for the ListBox
public class ProductList : VisList     {
      private ListBox list;
      //-----
      public ProductList(ListBox lst)          {
             list = lst;
      }
      //-----
      public void addLine(Product p) {
             list.Items.Add (p.getName() );
      }
      //-----
      public void removeLine(int num) {
             if(num >=0 && num < list.Items.Count ){
                    list.Items.Remove (num);
             }
      }
}

The ProductTable version of the VisList is quite similar except that it adds both the product name and quantity to the two columns of the grid.


public class GridList:VisList    {
      private DataGrid grid;
      private DataTable dtable;
      private GridAdapter gAdapter;
      //-----
      public GridList(DataGrid grd) {
             grid = grd;
             dtable = new DataTable("Products");
             DataColumn column = new DataColumn("ProdName");
             dtable.Columns.Add(column);
             column = new DataColumn("Qty");
             dtable.Columns.Add(column);
             grid.DataSource = dtable;
             gAdapter = new GridAdapter (grid);
      }
      //-----
      public void addLine(Product p) {
             gAdapter.Add (p);
      }

The Class Diagram

The UML diagram in Figure 15-2 for the Bridge class shows the separation of the interface and the implementation quite clearly. The Bridger class on the left is the Abstraction, and the ListBridge class is the implementation of that abstraction. The VisList interface describes the public interface to the list classes ProductList and GridList. The VisList interface defines the interface of the Implementor, and the Concrete Implementors are the ProductList and GridList classes.

Figure 15-2. The UML diagram for the Bridge pattern used in the two displays of product information

image

Note that these two Concrete Implementors are quite different in their specifics even though they both support the VisList interface.

Extending the Bridge

Now suppose we need to make some changes in the way these lists display the data. For example, maybe you want to have the products displayed in alphabetical order. You might think you’d need to either modify or subclass both the list and table classes.  This can quickly get to be a maintenance nightmare, especially if more than two such displays are needed at some point. Instead, we simply derive a new SortBridge class similar to the ListBridge class.

In order to sort Product objects, we have the Product class implement the IComparable interface, which means it has a CompareTo method.


public class Product : IComparable     {
      private string quantity;

private string name;
//-----
public Product(string line)           {
       int i = line.IndexOf ("--");
       name =line.Substring (0, i).Trim ();
       quantity = line.Substring (i+2).Trim ();
}
//-----
public string getQuantity() {
       return quantity;
}
//-----
public string getName() {
       return name;
}
//-----
public int CompareTo(object p) {
       Product prod =(Product) p;
       return name.CompareTo (prod.getName ());
}

With that change, sorting of the Product objects is much easier.


public class SortBridge:ListBridge     {
       //-----
       public SortBridge(VisList v):base(v){
       }
       //-----
       public override void addData(ArrayList ar) {
              int max = ar.Count ;
              Product[] prod = new Product[max];
              for(int i=0; i< max ; i++) {
                     prod[i] = (Product)ar[i];
              }
              for(int i=0; i < max ; i++) {
                     for (int j=i; j < max; j++) {
                            if(prod[i].CompareTo (prod[j])>0)  {
                                   Product pt = prod[i];
                                   prod[i]= prod[j];
                                   prod[j] = pt;
                            }
                     }
              }
              for(int i = 0; i< max; i++) {
                     vis.addLine (prod[i]);
              }
      }
      }

You can see the sorted result in Figure 15-3.

Figure 15-3. The sorted list generated using the SortBridge class

image

This clearly shows that you can vary the interface without changing the implementation. The converse is also true. For example, you could create another type of list display and replace one of the current list displays without any other program changes as long as the new list also implements the VisList interface. Here is the TreeList class.


public class TreeList:VisList    {
      private TreeView tree;
      private TreeAdapter gAdapter;
      //-----
      public TreeList(TreeView tre) {
             tree = tre;
             gAdapter = new TreeAdapter (tree);
      }
      //-----
      public void addLine(Product p) {
             gAdapter.Add (p);
      }

Note that we take advantage of the TreeAdapter we wrote in the previous chap-ter and modify it to work on Product objects.


public class TreeAdapter  {
      private TreeView tree;
      //------
      public TreeAdapter(TreeView tr)        {
             tree=tr;
      }
      //------
      public void Add(Product p) {
             TreeNode nod;
       //add a root node
      nod = tree.Nodes.Add(p.getName());
       //add a child node to it
       nod.Nodes.Add(p.getQuantity ());
       tree.ExpandAll ();
      }

In Figure 15-4, we have created a tree list component that implements the VisList interface and replaced the ordinary list without any change in the public interface to the classes.

Figure 15-4. Another display using a Bridge to a tree list

image

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. All of the C# Controls support a query interface that enables builder programs to enumerate their properties and display them for easy modification. Figure 15-5 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.

Figure 15-5. A screen from Visual Studio.NET showing a properties interface

image

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.

Consequences of the Bridge Pattern

  1. 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.
  2. You can extend the implementation class and the Bridge class separately, and usually without much interaction with each other.
  3. You can hide implementation details from the client program much more easily.

Thought Question

In plotting a stock’s performance, you usually display the price and price-earnings ratio over time, whereas in plotting a mutual fund, you usually show the price and the earnings per quarter. Suggest how you can use a Bridge to do both.

Programs on the CD-ROM

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset