开篇还是引用的那篇经典的文章《》。这个真是太经典了,没有比这个例子能更好的阐明桥接模式了,这里我就直接盗来用了。
现在市面上卖的蜡笔很多,各种型号,各种颜色种类繁多, 假如一盒蜡笔有24种颜色,那么它能涂抹出24种不同的颜色来,蜡笔型号是固定的,如果想画出各种线条那么就要购买不同型号的蜡笔,假如我们要涂抹出粗,中,细三种线条,那么我们就要买3盒粗,中,细型号的蜡笔才能满足需求,那么就是3盒*24色=72只蜡笔。假如使用毛笔来作画,我们需要准备3只粗,中,细的毛笔和24种颜料就好了, 那么就是3只毛笔+24种颜料。使用毛笔和使用蜡笔的差别是:用毛笔只需要3+24=27,用蜡笔需要准备3*24=72. 为什么会出现这么大的差别呢?仔细分析就会发现,画画的时候不仅对笔的型号有要求,而且还对颜色有要求,也就是说有两个引起变化的点或者说有两个变化的维度。蜡笔的型号和颜色直接绑定在一起了,他们二者完全融合(耦合)在一起了,他们的属性从生产出来就已经固化了(是静态的),不能被改变了。 而毛笔的型号和颜料的颜色是毫无关系(解耦),毛笔厂家生产不同型号的毛笔,颜料厂家生产不同颜色的颜料,二者互不相干,只有在使用的时候用户决定用什么型号的毛笔蘸什么颜色的颜料(动态设置)来作画,这个时候毛笔和颜料才动态发生关系,如果用户想使用一个型号的毛笔画不同颜色的画,毛笔可以洗掉再蘸不同的颜色就可以。
在看看蜡笔和毛笔在应对变化的优劣比较, 如果用户需要画一个加粗线条,蜡笔需要买一盒(24),毛笔只需要买一支就可以了。如果要加一种颜色,蜡笔需要增加4支(加粗,粗,中,细),而毛笔仅仅只需要增加一种颜色就够了。 从数学的角度来讲蜡笔不管是颜色或者型号的变化都会形成:型号数*颜色数,毛笔却是:型号数+颜色数。这样看来毛笔更有优势,更容易应对变化的需求。
那么在软件开发的过程中也会碰到类似的问题,怎么来解决这类问题呢?这就是我们将要探讨的桥接模式(brigde)。
桥接模式(bridge pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(handle and body)模式或接口(interface)模式。
用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个implementor(实现类接口)类型的对象并可以维护该对象,它与implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
扩充由abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在abstraction中声明的抽象业务方法,在refinedabstraction中可以调用在implementor中定义的业务方法。
定义实现类的接口,这个接口不一定要与abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,implementor接口仅提供基本操作,而abstraction定义的接口可能会做更多更复杂的操作。implementor接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在abstraction中不仅拥有自己的方法,还可以调用到implementor中定义的方法,使用关联关系来替代继承关系。
具体实现implementor接口,在不同的concreteimplementor中提供基本操作的不同实现,在程序运行时,concreteimplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。
public abstract class implementor { public abstract void operation(); } public abstract class abstraction { protected implementor implementor; public implementor implementor { set { this.implementor = value; } } public virtual void operation() { implementor.operation(); } } public class concreteimplementora : implementor { public override void operation() { console.writeline(this.gettype().name + " operation"); } } public class concreteimplementorb : implementor { public override void operation() { console.writeline(this.gettype().name + " operation"); } } public class refinedabstraction : abstraction { public override void operation() { base.operation(); } }
客户端调用:
static void main(string[] args) { abstraction abstraction = new refinedabstraction(); implementor implementor = new concreteimplementora(); abstraction.implementor = implementor; abstraction.operation(); implementor = new concreteimplementorb(); abstraction.implementor = implementor; abstraction.operation(); console.readkey(); }
输出结果:
我们分别模拟实现开头提到的蜡笔和毛笔。我们只选择三种型号(large,middle,small)和三种颜色(red,green, blue).
蜡笔类结构图
蜡笔类代码:
public abstract class crayon { protected string size; protected string color; protected abstract void setsize(); protected abstract void setcolor(); public void display() { setsize(); setcolor(); console.writeline(this.gettype().name + ": [size]=" + size + "[color]=" + color); } } public abstract class largecrayon : crayon { protected override void setsize() { size = "large"; } } public abstract class middlecrayon : crayon { protected override void setsize() { size = "middle"; } } public abstract class smallcrayon : crayon { protected override void setsize() { size = "small"; } } public class redlargecrayon : largecrayon { protected override void setcolor() { color = "red"; } } public class greenlargecrayon : largecrayon { protected override void setcolor() { color = "green"; } } public class bluelargecrayon : largecrayon { protected override void setcolor() { color = "blue"; } } public class redmiddlecrayon : middlecrayon { protected override void setcolor() { color = "red"; } } public class greenmiddlecrayon : middlecrayon { protected override void setcolor() { color = "green"; } } public class bluemiddlecrayon : middlecrayon { protected override void setcolor() { color = "blue"; } } public class redsmallcrayon : smallcrayon { protected override void setcolor() { color = "red"; } } public class greensmallcrayon : smallcrayon { protected override void setcolor() { color = "green"; } } public class bluesmallcrayon : smallcrayon { protected override void setcolor() { color = "blue"; } }
客户端调用:
static void main(string[] args) { crayon.crayon redlargecrayon, greenlargecrayon, bluelargecrayon, redmiddlecrayon, greenmiddlecrayon, bluemiddlecrayon, redsmallcrayon, greensmallcrayon, bluesmallcrayon; redlargecrayon = new redlargecrayon(); greenlargecrayon = new greenlargecrayon(); bluelargecrayon = new bluelargecrayon(); redmiddlecrayon = new redmiddlecrayon(); greenmiddlecrayon = new greenmiddlecrayon(); bluemiddlecrayon = new bluemiddlecrayon(); redsmallcrayon = new redsmallcrayon(); greensmallcrayon = new greensmallcrayon(); bluesmallcrayon = new bluesmallcrayon(); redlargecrayon.display(); greenlargecrayon.display(); bluelargecrayon.display(); redmiddlecrayon.display(); greenmiddlecrayon.display(); bluemiddlecrayon.display(); redsmallcrayon.display(); greensmallcrayon.display(); bluesmallcrayon.display(); console.readkey(); }
输出:
蜡笔是一种典型的多层继承结构, 型号和颜色实在继承体系中得以实现,在程序编译的时候型号和颜色就已经绑定好了,在运行时无法再动态改变,并且类非常多,如果增加型号或者颜色将非常难以维护。
毛笔类结构图
毛笔类代码:
public abstract class brush { private color color; protected string size; public void setcolor(color color) { this.color = color; } protected abstract void setsize(); public void draw() { setsize(); console.writeline(this.gettype().name + ": [size]=" + this.size + "->[color]=" + this.color.currentcolor); } } public abstract class color { protected string color; public string currentcolor { get { return color; } } } public class redcolor:color { public redcolor() { this.color = "red"; } } public class greencolor:color { public greencolor() { this.color = "green"; } } public class bluecolor : color { public bluecolor() { this.color = "blue"; } } public class largebrush : brush { protected override void setsize() { this.size = "large"; } } public class middlebrush : brush { protected override void setsize() { this.size = "middle"; } } public class smallbrush : brush { protected override void setsize() { this.size = "small"; } }
客户端调用:
static void main(string[] args) { brush largebrush, middlebrush, smallbrush; color red, green, blue; red = new redcolor(); green = new greencolor(); blue = new bluecolor(); largebrush = new largebrush(); middlebrush = new middlebrush(); smallbrush = new smallbrush(); largebrush.setcolor(red); largebrush.draw(); largebrush.setcolor(green); largebrush.draw(); largebrush.setcolor(blue); largebrush.draw(); middlebrush.setcolor(red); middlebrush.draw(); middlebrush.setcolor(green); middlebrush.draw(); middlebrush.setcolor(blue); middlebrush.draw(); smallbrush.setcolor(red); smallbrush.draw(); smallbrush.setcolor(green); smallbrush.draw(); smallbrush.setcolor(blue); smallbrush.draw(); console.readkey(); }
输出结果:
largebrush: [size]=large->[color]=red largebrush: [size]=large->[color]=green largebrush: [size]=large->[color]=blue middlebrush: [size]=middle->[color]=red middlebrush: [size]=middle->[color]=green middlebrush: [size]=middle->[color]=blue smallbrush: [size]=small->[color]=red smallbrush: [size]=small->[color]=green smallbrush: [size]=small->[color]=blue
毛笔类之间的结构发生了一些变化,将蜡笔的深度继承关系变成了一个平行的关联关系,这样带来的好处是毛笔的型号和颜色可以在两个体系中独立的变化而互不影响。这样就降低了耦合度,提高了扩展性,和可维护性,使得类的数量也急剧减少,降低了复杂度。
某软件公司欲开发一个数据转换工具,可以将数据库中的数据转换成多种文件格式,例如txt、xml、pdf等格式,同时该工具需要支持多种不同的数据库。试使用桥接模式对其进行设计。
可以使用桥接模式做一个简单的实现如下:
public abstract class database { public abstract string getdata(); } public abstract class exportor { private database database; public void setdatabase(database database) { this.database = database; } public void export() { var data = this.database.getdata(); var filetype = this.getfiletype(); console.writeline(this.gettype().name + "[database] is [" + data + "] [filetype] is [" + filetype + "]"); } protected abstract string getfiletype(); } public class sqldatabase : database { public override string getdata() { return "sqldatabase"; } } public class oracaldatabase : database { public override string getdata() { return "oracaldatabase"; } } public class sqlitedatabase : database { public override string getdata() { return "sqlitedatabase"; } } public class dbasedatabase : database { public override string getdata() { return "dbasedatabase"; } } public class excelexportor : exportor { protected override string getfiletype() { return "excel"; } } public class txtexportor : exportor { protected override string getfiletype() { return "txt"; } } public class xmlexportor : exportor { protected override string getfiletype() { return "xml"; } } public class pdfexportor : exportor { protected override string getfiletype() { return "pdf"; } }
客户端调用:
static void executeexport() { exportor exportexcel, exportpdf, exportxml, exporttxt; database sql, sqlite, dbase, oracal; sql = new sqldatabase(); sqlite = new sqlitedatabase(); dbase = new dbasedatabase(); oracal = new oracaldatabase(); exportexcel = new excelexportor(); exportpdf = new pdfexportor(); exporttxt = new pdfexportor(); exportxml = new xmlexportor(); exportxml.setdatabase(sql); exportxml.export(); exportxml.setdatabase(oracal); exportxml.export(); }
输出:
xmlexportor[database] is [sqldatabase] [filetype] is [xml] xmlexportor[database] is [oracaldatabase] [filetype] is [xml]
抽出一个泛型执行器,使客户端调用代码更优雅一点,泛型执行器的代码如下:
public interface iexportorexcutor<in t, in v> where t : exportor where v : database { void execute(); } public class exportorexcutor<t, v> : iexportorexcutor<t, v> where t : exportor, new() where v : database, new() { public static iexportorexcutor<t, v> of() { return new exportorexcutor<t, v>(); } public void execute() { var export = new t(); var database = new v(); export.setdatabase(database); export.export(); } }
客户端调用代码:
static void main(string[] args) { exportorexcutor<excelexportor, sqlitedatabase>.of().execute(); exportorexcutor<excelexportor, oracaldatabase>.of().execute(); exportorexcutor<excelexportor, sqldatabase>.of().execute(); exportorexcutor<excelexportor, dbasedatabase>.of().execute(); exportorexcutor<pdfexportor, sqlitedatabase>.of().execute(); exportorexcutor<pdfexportor, oracaldatabase>.of().execute(); exportorexcutor<pdfexportor, sqldatabase>.of().execute(); exportorexcutor<pdfexportor, dbasedatabase>.of().execute(); exportorexcutor<txtexportor, sqlitedatabase>.of().execute(); exportorexcutor<txtexportor, oracaldatabase>.of().execute(); exportorexcutor<txtexportor, sqldatabase>.of().execute(); exportorexcutor<txtexportor, dbasedatabase>.of().execute(); exportorexcutor<xmlexportor, sqlitedatabase>.of().execute(); exportorexcutor<xmlexportor, oracaldatabase>.of().execute(); exportorexcutor<xmlexportor, sqldatabase>.of().execute(); exportorexcutor<xmlexportor, dbasedatabase>.of().execute(); console.readkey(); }
输出:
excelexportor[database] is [sqlitedatabase] [filetype] is [excel] excelexportor[database] is [oracaldatabase] [filetype] is [excel] excelexportor[database] is [sqldatabase] [filetype] is [excel] excelexportor[database] is [dbasedatabase] [filetype] is [excel] pdfexportor[database] is [sqlitedatabase] [filetype] is [pdf] pdfexportor[database] is [oracaldatabase] [filetype] is [pdf] pdfexportor[database] is [sqldatabase] [filetype] is [pdf] pdfexportor[database] is [dbasedatabase] [filetype] is [pdf] txtexportor[database] is [sqlitedatabase] [filetype] is [txt] txtexportor[database] is [oracaldatabase] [filetype] is [txt] txtexportor[database] is [sqldatabase] [filetype] is [txt] txtexportor[database] is [dbasedatabase] [filetype] is [txt] xmlexportor[database] is [sqlitedatabase] [filetype] is [xml] xmlexportor[database] is [oracaldatabase] [filetype] is [xml] xmlexportor[database] is [sqldatabase] [filetype] is [xml] xmlexportor[database] is [dbasedatabase] [filetype] is [xml]
桥接模式就探讨到这里。
如对本文有疑问, 点击进行留言回复!!
网友评论