设计模式 桥接模式(Bridge Pattern)
桥接模式有点像适配器模式,都是用一个类将一种接口转换成另一种接口。但是适配器模式的意图是:是一个或多个类的接口看起来像一个特定的接口。桥接模式将类的接口和它的实现分离,无需修改客户端代码就可以改变或替换实现过程。
桥接模式的参与这又Abstraction,他定义类的接口,Refined Abstraction,他扩展实现由Abstraction定义的接口;Implementor,它定义实现 的接口;
ConcreteImplementor,他是实现类。
假设我们有一个程序,要在窗口中显示一系列的产品。最简单的就是用ListBox,但在售出大量产品后,我们希望在产品中显示产品名称以及销售数目。桥接接接口 我们定义一个简单的接口,他保留着相同的部分而不管实际实现类的类型和复杂性。先定义Bridger接口,该类只接收数据ArrayList,并将其传递给现实类。
using System;
using System.Collections ;
namespace BasicBridge
{
/// <summary>
/// Summary description for Bridger.
/// </summary>
//Bridge interface to display list classes
public interface Bridger {
void addData(ArrayList col);
}
}
还要定义一个Product类,它包含产品的名字,数量,并分析来自于数据文件输入串。
1 using System;
2
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for Product.
7 /// </summary>
8 public class Product : IComparable {
9 private string quantity;
10 private string name;
11 //-----
12 public Product(string line) {
13 int i = line.IndexOf ("--");
14 name =line.Substring (0, i).Trim ();
15 quantity = line.Substring (i+2).Trim ();
16 }
17 //-----
18 public string getQuantity() {
19 return quantity;
20 }
21 //-----
22 public string getName() {
23 return name;
24 }
25 //-----
26 public int CompareTo(object p) {
27 Product prod =(Product) p;
28 return name.CompareTo (prod.getName ());
29 }
30 }
31 }
桥接模式的另一部分是实现类,他们常常都有一个更灵活,低层次的接口。这里让实现类向显示屏幕添加数据行,一次添加一行数据。
1 using System;
2
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for VisList.
7 /// </summary>
8 public interface VisList {
9 //add a line to the display
10 void addLine(Product p);
11 //remove a line from the display
12 void removeLine(int num);
13 }
14 }
左边的接口和右边的实现之间的桥梁ListBridge类,他实例化其中一个列表信息。注意,他实现了Bridger接口供应用程序使用。
1 using System;
2 using System.Collections ;
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for ListBridge.
7 /// </summary>
8 public class ListBridge : Bridger {
9 protected VisList vis;
10 //------
11 public ListBridge(VisList v) {
12 vis = v;
13 }
14 //-----
15 public virtual void addData(ArrayList ar) {
16 for(int i=0; i< ar.Count ; i++) {
17 Product p = (Product)ar[i];
18 vis.addLine (p);
19 }
20 }
21 }
22 }
注意,我们把VisList的变量设为保护的,把addData方法声明为虚函数,这样以后就可以扩展这个类了。在程序设计顶层,只是用ListBridge类创建一个表实例和一个列表实例。
1 private void init() {
2 products = new ArrayList ();
3 readFile(products); //read in the data file
4 //create the product list
5 prodList = new ProductList (lsProd);
6 //Bridge to product VisList
7 Bridger lbr = new SortBridge (prodList);
8 //put the data into the product list
9 lbr.addData (products);
10 //create the grid VisList
11 gridList = new GridList(grdProd);
12 //Bridge to the grid list
13 Bridger gbr = new SortBridge (gridList);
14 //put the data into the grid display
15 gbr.addData (products);
16 }
VisList类
两个VisList实际上非常类似,消费者版本是类对ListBox操作,并将名字加入其中。
1 using System;
2 using System.Windows.Forms;
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for ProductList.
7 /// </summary>
8 //A VisList class for the ListBox
9 public class ProductList : VisList {
10 private ListBox list;
11 //-----
12 public ProductList(ListBox lst) {
13 list = lst;
14 }
15 //-----
16 public void addLine(Product p) {
17 list.Items.Add (p.getName() );
18 }
19 //-----
20 public void removeLine(int num) {
21 if(num >=0 && num < list.Items.Count ){
22 list.Items.Remove (num);
23 }
24 }
25 }
26 }
除了要向表中的两个列表添加产品名称和数量外,VisList的GridList版本与上面的类似。
1 using System;
2 using System.Windows.Forms;
3 using System.Data;
4 namespace BasicBridge
5 {
6 /// <summary>
7 /// Summary description for GridList.
8 /// </summary>
9 public class GridList:VisList {
10 private DataGrid grid;
11 private DataTable dtable;
12 private GridAdapter gAdapter;
13 //-----
14 public GridList(DataGrid grd) {
15 grid = grd;
16 dtable = new DataTable("Products");
17 DataColumn column = new DataColumn("ProdName");
18 dtable.Columns.Add(column);
19 column = new DataColumn("Qty");
20 dtable.Columns.Add(column);
21 grid.DataSource = dtable;
22 gAdapter = new GridAdapter (grid);
23 }
24 //-----
25 public void addLine(Product p) {
26 gAdapter.Add (p);
27 }
28 //-----
29 public void removeLine(int num) {
30
31 }
32 }
33 }
扩展Bridge
现在假设,需要在列表显示数据的方式做了修改。例如以字母顺序显示产品。读者可能会想修改子列表和表格类,这很容易导致不易维护,特别是如果在某个时候需要两个以上这样的显示时。我们换中做法,派生一个类似于ListBridge的心类SortBridge。为了对Product对象进行排序,让Product类是实现IComparable接口,木的事让他拥有CompareTo方法。
1 using System;
2
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for Product.
7 /// </summary>
8 public class Product : IComparable {
9 private string quantity;
10 private string name;
11 //-----
12 public Product(string line) {
13 int i = line.IndexOf ("--");
14 name =line.Substring (0, i).Trim ();
15 quantity = line.Substring (i+2).Trim ();
16 }
17 //-----
18 public string getQuantity() {
19 return quantity;
20 }
21 //-----
22 public string getName() {
23 return name;
24 }
25 //-----
26 public int CompareTo(object p) {
27 Product prod =(Product) p;
28 return name.CompareTo (prod.getName ());
29 }
30 }
31 }
有了上述改动之后,Product对象的排序就很简单了。
1 using System;
2 using System.Collections ;
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for SortBridge.
7 /// </summary>
8 public class SortBridge:ListBridge {
9 //-----
10 public SortBridge(VisList v):base(v){
11 }
12 //-----
13 public override void addData(ArrayList ar) {
14 int max = ar.Count ;
15 Product[] prod = new Product[max];
16 for(int i=0; i< max ; i++) {
17 prod[i] = (Product)ar[i];
18 }
19 for(int i=0; i < max ; i++) {
20 for (int j=i; j < max; j++) {
21 if(prod[i].CompareTo (prod[j])>0) {
22 Product pt = prod[i];
23 prod[i]= prod[j];
24 prod[j] = pt;
25 }
26 }
27 }
28 for(int i = 0; i< max; i++) {
29 vis.addLine (prod[i]);
30 }
31 }
32 }
33 }
排序结果如下图
这清楚的表明,不用修改实现就能改变接口,反过来也一样。例如创建一个其他类型的类表显示类并替换当前的显示,只要新的列表也实现了VisList接口,就不需要改变其他程序了。下面是TreeList类。
1 using System;
2 using System.Windows.Forms;
3 using System.Data;
4 namespace BasicBridge
5 {
6 /// <summary>
7 /// Summary description for GridList.
8 /// </summary>
9 public class TreeList:VisList {
10 private TreeView tree;
11 private TreeAdapter gAdapter;
12 //-----
13 public TreeList(TreeView tre) {
14 tree = tre;
15 gAdapter = new TreeAdapter (tree);
16 }
17 //-----
18 public void addLine(Product p) {
19 gAdapter.Add (p);
20 }
21 //-----
22 public void removeLine(int num) {
23 }
24 }
25 }
注意,我们利用前一张编写的TreeAdapter类,并修改它使之能对Product对象操作。创建一个树形列表组件,他实现了VisList接口,并替换原来的列表而没有修改类的公共接口。
1 using System;
2 using System.Windows.Forms;
3 namespace BasicBridge
4 {
5 /// <summary>
6 /// Summary description for TreeAdapter.
7 /// </summary>
8 public class TreeAdapter {
9 private TreeView tree;
10 //------
11 public TreeAdapter(TreeView tr) {
12 tree=tr;
13 }
14 //------
15 public void Add(Product p) {
16 TreeNode nod;
17 //add a root node
18 nod = tree.Nodes.Add(p.getName());
19 //add a child node to it
20 nod.Nodes.Add(p.getQuantity ());
21 tree.ExpandAll ();
22 }
23 //------
24 public int SelectedIndex() {
25 return tree.SelectedNode.Index ;
26 }
27 //------
28 public void Clear() {
29 TreeNode nod;
30 for (int i=0; i< tree.Nodes.Count ; i++) {
31 nod = tree.Nodes [i];
32 nod.Remove ();
33 }
34 }
35 //------
36 public void clearSelection() {}
37 }
38 }
效果如下图
桥接模式的效果
1.桥接模式可以保持客户端程序接口不变,而允许读者修改显示类,或要使用的类。可以防止重新编译一系列复杂的用户接口模块,只需要重新编译Bridge和实际的最终显示类。
2.可以分别扩展实现类和Bridge类,二者之间通常不会有相互作用。
3.对客户端程序很容易隐藏实现细节。