Dot Net设计模式—桥接模式
1.概述
1.1意图
“将抽象部分与它的实现部分分离,使它们都可以独立地变化”,这是GOF在《设计模式》一书中的解释。这里的抽象和实现并不一定是同一层次的概念,例如数据库操作可以归结为“增加、删除和修改”。很多业务过程都是通过对数据库的操作实现的,例如“库存管理”中的“入库”,这个业务动作的软件实现可以描述为“在库存表中增加一条记录”,而“入库”和“插入记录”处于不同的业务层次。
1.2使用场合
在如下情况下可以使用桥接模式。
(1)不希望在业务和业务的软件实现之间存在因定的绑定关系。例如,不希望“入库”业务过程和具体的数据库访问技术或数据库管理系统有过于密切的关系。最好是数据库访问技术的升级,或数据库管理系统的改变对业务模块没有影响,甚至在运行期间可以通过动态绑定来选择不同数据库技术或数据库管理系统。
(2)希望类的抽象和实现部分可以扩充,进而实现不同的抽象接口和实现部分的组合。
(3)修改实现部分对用户不产生影响,即代码无须重新编译。
(4)复用实现部分。由于实现部分所处的层次较低,因此可以被多种业务模块复用。例如,数据库访问模块可以用在多种业务单元中。
1.3结构
桥接模式的结构如图所示。
(1)Abstraction:定义抽象类的接口并维护指向Implementor类的对象指针。
(2)RefinedAbstraction:扩充Abstraction定义的接口。
(3)Implementor:定义实现类的接口,该接口不一定要与Abstraction的接口完全一致。
事实上这两个接口可以完全不同。一般而言,Implementor接口仅提供基本操作,而Abstraction则定义了基于操作的较高层次的操作。
(4) ConcreteImplementor:实现Implementor接口并定义它的具体实现。
为了更深入了解这个模式,我们来看一个具体的例子。在应用系统中经常有很多业务单元在很多业务模块中类似,隐含在背后的业务域模型相同。例如,“设备管理”中的“零部件管理”,“物资管理”中的“库房管理”,“实验室管理”中的“试剂管理”等都是“库存管理”。但由于业务场景的不同,导致业务中的细节不同,因此不能简单地复用。另外,“库存管理”软件的实现主要通过操作关系数据库来完成,我们希望保持软件系统与数据库访问技术以及数据库管理系统的独立性。
为了实现以上要求,我们可以以桥接模式。即将抽象的“库存管理”和“数据库”操作作为具体的“库存管理”应用和具体的“数据库访问技术”的桥梁, 如下图所示。
从图中可以看出,我们将库存管理的抽象业务域模型封装在“抽象的库存管理”中。具体的库存管理业务如“生产车间库存管理”可以通过继承库存管理获得一般性的业务过程,然后根据具体的业务特点进行相应的扩展。
我们将数据库操作封装在dbOperation类中,具体执行访问数据库的各个指令,针对具体数据库的操作由该类的子类实现。
1.4效果
采用桥接模式可以获得以下好处。
(1)将接口与实现分离:一个接口可以有若干实现,一个实现也可以为若干对象服务,表示逻辑的对象可以动态地与实现功能的对象组合。
(2)提高可扩充性:逻辑和实现都可以通过类层次的扩展进行扩充。
需要注意的是,逻辑和实现被封装在不同的对象中,逻辑对实现的引用是动态进行的。在实际中,需要分别用不同的工厂创建逻辑和实现,然后组装。
2.多种数据源与多种数据显示方式的组合
桥接模式同样可用在多种数据采集方式的场合,在有生产过程实时数据采集时,经常遇到多种数据采集的接口。为适应这种情况,可以采用桥接模式,其结构如图所示。
针对不同的数据源有不同的数据采集方式,有的采集系统需要直接从文件中读取数据,有的需要从远程Web服务器读取数据,有的需要从实时数据库读取数据,有的需要从关系数据库读取数据等。数据采集系统是低层平台,在上层应用中需要各种方式的显示形式,例如流程图、表格、曲线图和趋势图等。每种显示方式模块都可能从不同的数据源接收数据,即不同类型的数据源和数据显示方式应该可以任意组合。
以一个来源于实际项目的例子说明。我们需要在Web页面上显示流程图,并且需要生成XML格式的流程图文件。其中采用简单工厂获得数据来源,数据来源标志保存在Web.config文件中。
下图所示为应用的局部结构,其中采用了桥接模式。
下面是部分代码,限于篇幅,仅列出结构部分。
定义clsAbstractReadData类,其中定义了需要获得数据的结构:
具体的读取类实现ReadData方法。下面是从文本文件中读取数据,其中给出了读取用逗号分隔数据的方法:
下面是产生读取对象的简单工厂:
1.1意图
“将抽象部分与它的实现部分分离,使它们都可以独立地变化”,这是GOF在《设计模式》一书中的解释。这里的抽象和实现并不一定是同一层次的概念,例如数据库操作可以归结为“增加、删除和修改”。很多业务过程都是通过对数据库的操作实现的,例如“库存管理”中的“入库”,这个业务动作的软件实现可以描述为“在库存表中增加一条记录”,而“入库”和“插入记录”处于不同的业务层次。
1.2使用场合
在如下情况下可以使用桥接模式。
(1)不希望在业务和业务的软件实现之间存在因定的绑定关系。例如,不希望“入库”业务过程和具体的数据库访问技术或数据库管理系统有过于密切的关系。最好是数据库访问技术的升级,或数据库管理系统的改变对业务模块没有影响,甚至在运行期间可以通过动态绑定来选择不同数据库技术或数据库管理系统。
(2)希望类的抽象和实现部分可以扩充,进而实现不同的抽象接口和实现部分的组合。
(3)修改实现部分对用户不产生影响,即代码无须重新编译。
(4)复用实现部分。由于实现部分所处的层次较低,因此可以被多种业务模块复用。例如,数据库访问模块可以用在多种业务单元中。
1.3结构
桥接模式的结构如图所示。
(1)Abstraction:定义抽象类的接口并维护指向Implementor类的对象指针。
(2)RefinedAbstraction:扩充Abstraction定义的接口。
(3)Implementor:定义实现类的接口,该接口不一定要与Abstraction的接口完全一致。
事实上这两个接口可以完全不同。一般而言,Implementor接口仅提供基本操作,而Abstraction则定义了基于操作的较高层次的操作。
(4) ConcreteImplementor:实现Implementor接口并定义它的具体实现。
为了更深入了解这个模式,我们来看一个具体的例子。在应用系统中经常有很多业务单元在很多业务模块中类似,隐含在背后的业务域模型相同。例如,“设备管理”中的“零部件管理”,“物资管理”中的“库房管理”,“实验室管理”中的“试剂管理”等都是“库存管理”。但由于业务场景的不同,导致业务中的细节不同,因此不能简单地复用。另外,“库存管理”软件的实现主要通过操作关系数据库来完成,我们希望保持软件系统与数据库访问技术以及数据库管理系统的独立性。
为了实现以上要求,我们可以以桥接模式。即将抽象的“库存管理”和“数据库”操作作为具体的“库存管理”应用和具体的“数据库访问技术”的桥梁, 如下图所示。
从图中可以看出,我们将库存管理的抽象业务域模型封装在“抽象的库存管理”中。具体的库存管理业务如“生产车间库存管理”可以通过继承库存管理获得一般性的业务过程,然后根据具体的业务特点进行相应的扩展。
我们将数据库操作封装在dbOperation类中,具体执行访问数据库的各个指令,针对具体数据库的操作由该类的子类实现。
1.4效果
采用桥接模式可以获得以下好处。
(1)将接口与实现分离:一个接口可以有若干实现,一个实现也可以为若干对象服务,表示逻辑的对象可以动态地与实现功能的对象组合。
(2)提高可扩充性:逻辑和实现都可以通过类层次的扩展进行扩充。
需要注意的是,逻辑和实现被封装在不同的对象中,逻辑对实现的引用是动态进行的。在实际中,需要分别用不同的工厂创建逻辑和实现,然后组装。
2.多种数据源与多种数据显示方式的组合
桥接模式同样可用在多种数据采集方式的场合,在有生产过程实时数据采集时,经常遇到多种数据采集的接口。为适应这种情况,可以采用桥接模式,其结构如图所示。
针对不同的数据源有不同的数据采集方式,有的采集系统需要直接从文件中读取数据,有的需要从远程Web服务器读取数据,有的需要从实时数据库读取数据,有的需要从关系数据库读取数据等。数据采集系统是低层平台,在上层应用中需要各种方式的显示形式,例如流程图、表格、曲线图和趋势图等。每种显示方式模块都可能从不同的数据源接收数据,即不同类型的数据源和数据显示方式应该可以任意组合。
以一个来源于实际项目的例子说明。我们需要在Web页面上显示流程图,并且需要生成XML格式的流程图文件。其中采用简单工厂获得数据来源,数据来源标志保存在Web.config文件中。
下图所示为应用的局部结构,其中采用了桥接模式。
下面是部分代码,限于篇幅,仅列出结构部分。
定义clsAbstractReadData类,其中定义了需要获得数据的结构:
1Imports System.IO
2Imports System.Net
3 Imports System.Text
4Public MustInherit Class clsAbstractReadData
5 Public MustOverride Function ReadData(ByVal strDate As strDate As DataSet
6 Protected mySourceDs As DataSet
7 Public Sub New( )
8 MySourceDS = New DataSet
9 Dim dt As DataTable
10 ‘这里定义数据结构,省略
11 End Sub
12End Class
13
2Imports System.Net
3 Imports System.Text
4Public MustInherit Class clsAbstractReadData
5 Public MustOverride Function ReadData(ByVal strDate As strDate As DataSet
6 Protected mySourceDs As DataSet
7 Public Sub New( )
8 MySourceDS = New DataSet
9 Dim dt As DataTable
10 ‘这里定义数据结构,省略
11 End Sub
12End Class
13
具体的读取类实现ReadData方法。下面是从文本文件中读取数据,其中给出了读取用逗号分隔数据的方法:
1Public Class clsReadDataFromFile
2 Inherits ClsAbstractReadData
3 Private datafilepath As String
4
5 Public Sub New(ByVal strpath As String)
6 MyBase.New( )
7 Datafilepath = strpath
8 End Sub
9 Public Overrides Function ReadData(ByVal strDate As String) As
10System.Date.DataSet
11‘调用ReadDataFromFile从具体的文件中读取数据,省略
12Return mysourceds
13 End Function
14 Private Sub ReadDataFromFile(ByVal fn As String,,ByVal dt As DataTable,ByVal
15 cn As Integer )
16 ‘fn 数据文件名
17 ‘dt DtatTable
18 ‘cn 字段数
19 Dim fileNumber As Integer
20 FileNumber = FreeFile( )
21 FileOpen(fileNumber, fn, OpenMode.Input)
22 Dim NextLine As String
23 I = 1
24 Do Until EOF(fileNumber)
25 NextLine = LineInput(fileNumber)
26 If I > 1 And Trim(NextLine) <> “ ” Then
27 Dim j As Integer
28 Dim r As DataRow = dt.NewRow
29 Dim s As String = “ ,”
30 Dim sp As Char( ) = s.ToCharArray
31 Dim rs As String( ) = NextLine.Split(sp)
32 For j = 0 To cn – 1
33 Dim v As String
34 Try
35 V = rs ( j )
36 r.Item( j ) = Trim( v )
37 Catch ex As Exception
38 r.Item( j ) = “ ”
39 End Try
40 Next
41 Dt.Rows.Add(r)
42 End If
43 I = I + 1
44 Loop
45 FileClose(fileNumber)
46End Sub
47Private Function IsFileExist (ByVal fn As String) As Boolean
48 If FileSystem.Dir (fn) = “ ” Then Return False
49 Return True
50End Function
51End Class
52
2 Inherits ClsAbstractReadData
3 Private datafilepath As String
4
5 Public Sub New(ByVal strpath As String)
6 MyBase.New( )
7 Datafilepath = strpath
8 End Sub
9 Public Overrides Function ReadData(ByVal strDate As String) As
10System.Date.DataSet
11‘调用ReadDataFromFile从具体的文件中读取数据,省略
12Return mysourceds
13 End Function
14 Private Sub ReadDataFromFile(ByVal fn As String,,ByVal dt As DataTable,ByVal
15 cn As Integer )
16 ‘fn 数据文件名
17 ‘dt DtatTable
18 ‘cn 字段数
19 Dim fileNumber As Integer
20 FileNumber = FreeFile( )
21 FileOpen(fileNumber, fn, OpenMode.Input)
22 Dim NextLine As String
23 I = 1
24 Do Until EOF(fileNumber)
25 NextLine = LineInput(fileNumber)
26 If I > 1 And Trim(NextLine) <> “ ” Then
27 Dim j As Integer
28 Dim r As DataRow = dt.NewRow
29 Dim s As String = “ ,”
30 Dim sp As Char( ) = s.ToCharArray
31 Dim rs As String( ) = NextLine.Split(sp)
32 For j = 0 To cn – 1
33 Dim v As String
34 Try
35 V = rs ( j )
36 r.Item( j ) = Trim( v )
37 Catch ex As Exception
38 r.Item( j ) = “ ”
39 End Try
40 Next
41 Dt.Rows.Add(r)
42 End If
43 I = I + 1
44 Loop
45 FileClose(fileNumber)
46End Sub
47Private Function IsFileExist (ByVal fn As String) As Boolean
48 If FileSystem.Dir (fn) = “ ” Then Return False
49 Return True
50End Function
51End Class
52
下面是产生读取对象的简单工厂:
1Public Class clsReaDataFactory
2 Pbulic Shared Function getDataReader ( ) As clsAbstractReadData
3 Dim datafilepath As String =
4ConfigurationSettings.AppSettings (“DataFilePath”)
5 Dim strDataType As String = ConfigurationSettings.AppSettings (“DataType”)
6Dim strHttp As String = ConfigurationSettings.AppSettings (“DataURL”)
7Select Case strDataType
8 Case “http”
9 Dim mrd As clsReadDataFromRemoteFile
10 Mrd = New clsReadDataFromRemoteFile (strHttp)
11 Return mrd
12 Case Else
13 Dim mrd As clsReadDataFromFile
14 Mrd = New clsReadDataFromFile(datafilepath)
15 Return mrd
16 End Select
17 End Function
18end class
2 Pbulic Shared Function getDataReader ( ) As clsAbstractReadData
3 Dim datafilepath As String =
4ConfigurationSettings.AppSettings (“DataFilePath”)
5 Dim strDataType As String = ConfigurationSettings.AppSettings (“DataType”)
6Dim strHttp As String = ConfigurationSettings.AppSettings (“DataURL”)
7Select Case strDataType
8 Case “http”
9 Dim mrd As clsReadDataFromRemoteFile
10 Mrd = New clsReadDataFromRemoteFile (strHttp)
11 Return mrd
12 Case Else
13 Dim mrd As clsReadDataFromFile
14 Mrd = New clsReadDataFromFile(datafilepath)
15 Return mrd
16 End Select
17 End Function
18end class