抽象与实现
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
动机:
事实上由于Tank类型的固有逻辑,使得Tank类型具有了两个变化的维度——一个变化的维度为“平台的变化”,一个变化的维度为“型号的变化”。
如何应对这种“多维度的变化”?如何利用面向对象技术来使得Tank类型可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度?
意图:
将抽象部分与实现部分分离,使它们都可以独立地变化。
Bridge模式的几个要点:
l Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度来变化。
l 所谓抽象和实现沿着各自维度的变化,即“子类化”它们,比如不同的Tank型号子类,和不同的平台子类。得到各个子类之后,便可以任意组合它们,从而获得不同平台上的不同型号。
l Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
l Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。
桥接接口
Bridger.cs代码示例:
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.cs代码示例:
namespace BasicBridge
{
/// <summary>
/// Summary description for Product.
/// </summary>
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 ());
}
}
}
VisList.cs代码示例:
namespace BasicBridge
{
/// <summary>
/// Summary description for VisList.
/// </summary>
public interface VisList {
//add a line to the display
void addLine(Product p);
//remove a line from the display
void removeLine(int num);
}
}
ListBridge.cs代码示例:
using System.Collections ;
namespace BasicBridge
{
/// <summary>
/// Summary description for ListBridge.
/// </summary>
public class ListBridge : Bridger {
private VisList vis;
//------
public ListBridge(VisList v) {
vis = v;
}
//-----
public void addData(ArrayList ar) {
for(int i=0; i< ar.Count ; i++) {
Product p = (Product)ar[i];
vis.addLine (p);
}
}
}
}
事件代码:
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);
}
csFile fl = new csFile("products.txt");
fl.OpenForRead ();
string s = fl.readLine ();
while(s != null ) {
Product p = new Product(s);
products.Add (p);
s = fl.readLine ();
}
fl.close();
}
ProductList.cs代码示例:
using System.Windows.Forms;
namespace BasicBridge
{
/// <summary>
/// Summary description for ProductList.
/// </summary>
//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);
}
}
}
}
GridList.cs代码示例:
using System.Windows.Forms;
using System.Data;
namespace BasicBridge
{
/// <summary>
/// Summary description for GridList.
/// </summary>
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);
}
//-----
public void removeLine(int num) {
}
}
}
扩展Bridge
Product.cs代码示例:
namespace BasicBridge
{
/// <summary>
/// Summary description for Product.
/// </summary>
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 ());
}
}
}
SortBridge.cs代码示例:
using System.Collections ;
namespace BasicBridge
{
/// <summary>
/// Summary description for SortBridge.
/// </summary>
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]);
}
}
}
}
TreeList.cs代码示例:
using System.Windows.Forms;
using System.Data;
namespace BasicBridge
{
/// <summary>
/// Summary description for GridList.
/// </summary>
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);
}
//-----
public void removeLine(int num) {
}
}
}
TreeAdapter.cs代码示例:
using System.Windows.Forms;
namespace BasicBridge
{
/// <summary>
/// Summary description for TreeAdapter.
/// </summary>
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 ();
}
//------
public int SelectedIndex() {
return tree.SelectedNode.Index ;
}
//------
public void Clear() {
TreeNode nod;
for (int i=0; i< tree.Nodes.Count ; i++) {
nod = tree.Nodes [i];
nod.Remove ();
}
}
//------
public void clearSelection() {}
}
}
桥接模式的效果:
1. 桥接模式可以保持客户端程序的接口不变,而允许读者修改显示类或要使用的类。这可以防止重新编译一系列复杂的用户接口模式,而只需要重新编译Bridge和实际的最终显示类。
2. 可以分别扩展实现类和Bridge类,二者之间通常不会有相互作用。
3. 对客户端程序很容易隐藏实现细节。