VB.net设计模式之工厂方法,抽象工厂方法(Factory Method Pattern,Abstract Factory Method Pattern)

本文通过一个数据库连接类的例子,详细解析了工厂模式的演变过程,包括简单工厂、工厂方法及抽象工厂模式的应用场景与优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工厂方法属于创建型模式,用于解决创建对象时带来的问题,有如下几种:
  • 简单工厂(Simple Factory)模式--不属于Gof--23种设计模式
  • 工厂方法(Factory Method)模式
  • 抽象工厂(Abstract Factory)模式
概述:
本来想把上面几个工厂方法全部一次学习完,但发现三种设计模式还是有比较大的差别的,说清楚其中一个都量比较大,但全部独立了,又不利用相互比较。简单工厂方法相对来说比较简单,这里就通过对工厂方法的演变来说明简单工厂方法。其实我觉得,学习都应该是由浅入深的一个过程,如果一下子把最终的学习心得写出来,很多语言过于精辟,不利于理会,我在参考其他网站上的关于设计模式的文章就发现,一下子把设计模式的解决问题,意图,结构等很简洁精辟的描述了,往往让初学者一头雾水。同时缺少深入的Why的说明。容易囫囵吞枣,不容易内化成自己所学的知识,更谈不是应用了。所以,在学习的过程中,我将尽量的多用列子,谈到列子的需求发生改变的时候,就是模式演变的过程。本文将结合一个数据库链接类的例子,一个网站要求能使用Access和Mssql两种数据库。可以在两者直接切换。
工厂方法的演变:
实现一:---一般实现
  1. Public Class MSSQLConn 
  2.   Public Function GetTopID as string 
  3.      Return "MSSQLConnTopID"
  4.   End Function 
  5. End Class
  6. Public Class AccessSQLConn 
  7.   Public Function GetTopID as string 
  8.      Return "AccessSQLConnTopID"
  9.   End Function 
  10. End Class
  11. '客户端
  12.  Protected Sub Page_Load(ByVal sender As ObjectByVal e As System.EventArgs) Handles Me.Load
  13.    
  14.         Dim sql = New DataAccessLayer.MSSQLConn 
  15.         Dim access = New DataAccessLayer.AccessSQLConn
  16.  
  17.     End Sub
简单说就是:Dim ProductA=New ProductA()
评述:
这是我们常见的做法:当需要使用一个对象的时候直接New 一个对象来使用。
那么他有个很大的问题就在于如果需要由SQL数据库转换到Access数据库的时候全部的Dim SQL=New DataAccessLayer.MSSQLConn就必须改成Dim access=new DataAccessLayer.AccessSQLconn 工程量很大,特别是项目中遍地使用这样的New的话。
(当然如果整个项目拥有不会出现Mssqlconn转换成AccessSqlconn的情况就不需要用实现二的改变,这就是设计模式常强调的封装变化点,如果不是变化点,就没有必要封装。)
为了解决这个问题,将代码改进如下:
实现二:--简单封装
  1. Public Class SQLFactory
  2.     Public Shared Function GetSQLConn() As DataAccessLayer.MSSQLConn
  3.         Return New DataAccessLayer.MSSQLConn
  4.     End Function
  5. End Class
  6. Dim SQLConn=SQLFactory.GetSQLconn
评述:如上改动后,如果需要转换则可以只该SQLFactory这个类的方法即可。不必整个站点去全部换,这个看起来很简单。其实已经包含了一个很重要的设计模式原则,就是封装变化点。可以看出来,New的对象是变化的。我们就将这种变化集中在了一个地方。但这个实现还是有问题,问题在于,就算是改了SQLFactory这个类,客户端还是要跟着重新编译一次(Dim SQLConn=SQLFactory.GetSQLconn其实为Dim SQLConn as DataAccessLayer.MSSQLConn =SQLFactory.GetSQLconn,改动后应变成Dim SQLConn as DataAccessLayer.AccessConn =SQLFactory.GetSQLconn,只是vs2008编译器帮我们做了这件事。vs2008之前版本需要我们直接改)。同时客户端依赖于一个具体的对象。换成第二种对象的时候,方法必须一样。
我们希望当改动的时候客户端不需要改变就改一个地方就可以了。
如果考虑到这点,就会发现接口,抽象类正好用来解决这个问题,因此我们做如下改动:
实现三:--工厂方法
  1. '新增接口
  2. Public Interface ISQLConn
  3. Function GetTopID as string 
  4. End Interface 
  5. Public Class MSSQLConn 
  6.     Implements ISQLConn
  7.    Public Function GetTopID as string 
  8.       Return "MSSQLConnTopID"
  9.    End Function 
  10.  End Class
  11.  Public Class AccessSQLConn
  12.     Implements ISQLConn
  13.    Public Function GetTopID as string 
  14.       Return "AccessSQLConnTopID"
  15.    End Function 
  16.  End Class
  17. Public Class SQLFactory
  18.     Public Shared Function GetSQLConn() As ISQLConn
  19.         Return New DataAccessLayer.MSSQLConn
  20.     End Function
  21. End Class
  22.  '客户端
  23.   Protected Sub Page_Load(ByVal sender As ObjectByVal e As System.EventArgs) Handles Me.Load
  24.     
  25.          Dim sql as ISQLConn =SQLFactory.GetSQLConn
  26.         Dim access as ISQLConn =SQLFactory.GetSQLConn
  27.   
  28.     End Sub
评述:
上面的实现就是简单工厂方法。当客户端需要使用一个SQLconn对象的时候,只要去New 就可以了。具体返回的是MSSQL还是Access由SQLFactory负责。这又体现了设计模式的原则之一,单一职责,职责分明问题。客户端,负责使用这些对象去处理事情,SQLFactory负责创建客户端所需的对象,Mssqlconn做直接分内的事情,和MsSql数据库打交道。彼此不妨碍。简单工厂模式并不是Gof23种设计模式之一。
上面的客户端中,sql于access其实是同一个对象,而有时候我们希望创建很多个对象,这样我们进行如下改进:
实现四:工厂方法
  1. Public Interface IDBHelperFactory
  2.     Function CreateDBHelper() As DBHelper
  3. End Interface
  4. Public Class SQLDBHelperFactory
  5.     Implements IDBHelperFactory
  6.     Public Function CreateDBHelper() As DALProfile.DBHelper Implements IDBHelperFactory.CreateDBHelper
  7.         Return New SQLDBHelper
  8.     End Function
  9. End Class
  10. Public Class AccessDBHelperFactory
  11.     Implements IDBHelperFactory
  12.     Public Function CreateDBHelper() As DALProfile.DBHelper Implements IDBHelperFactory.CreateDBHelper
  13.         Return New AccessDBHelper
  14.     End Function
  15. End Class
  16. Public Class SQLDBHelper
  17.     Inherits DBHelper
  18. End Class
  19. Public Class AccessDBHelper
  20.     Inherits DBHelper
  21. End Class
  22. Public Class Client
  23.     Public Sub Test()
  24.         Dim AccessDBHelper = New AccessDBHelperFactory().CreateDBHelper
  25.         Dim SQLDBHelper = New SQLDBHelperFactory().CreateDBHelper
  26.     End Sub
  27. End Class
评述:
这样在客户端就可以创建不同的需要的对象。
而有的时候,我们希望能返回一系列相互依赖的对象。简单工厂方法只提供了一个简单的SQLconn的对象返回。我们知道数据库链接的时候Connection,Command,DataAdapter是相互依赖的对象,彼此互相调用。同时SQL下,Access下又其对象又不一样,所以可以称SQL系列,Access系列对象,就一系列相互依赖的对象的创建。为了满足这个需求,我们做如下改进:
实现五:--静态工厂
  1. Public Class DBHelperFactory
  2.     Public Shared Function CreateConnection() As DbConnection
  3.         Return New SqlConnection
  4.     End Function

  5.     Public Shared Function CreateCommand() As DbCommand
  6.         Return New SqlCommand
  7.     End Function
  8.     Public Shared Function CreateDataAdapter() As DbDataAdapter
  9.         Return New SqlDataAdapter
  10.     End Function

  11. End Class
评述:
上述就是静态工厂,全部方法都是静态方法,返回所需的一系列相互依赖的对象。他是简单工厂的扩展,解决了,创建多个对象的问题,当需要改版多个对象的时候,直接变化这个Factory类就可以了。客户端程序不需要改变。但是当需求发生变化,比如需要增加一个系列的时候比如增加一个Access数据库的时候,此时,如果改动这个类,则将不能使用SQL了,要使用则又必须改变Factory类,麻烦!如果添加一个AccessDBHelper,则客户端又必须改变,所以为了避免这个问题,采用如下改进:
实现六:抽象工厂

  1. Public Interface IDBHelperFactory
  2.     Function CreateConnection() As DbConnection
  3.     Function CreateCommand() As DbCommand
  4.     Function CreateDataAdapter() As DbDataAdapter
  5. End Interface


  6. Public Class SQLHelperFactory
  7.     Implements IDBHelperFactory
  8.     Public Function CreateCommand() As System.Data.Common.DbCommand Implements IDBHelperFactory.CreateCommand
  9.         Return New SqlCommand
  10.     End Function

  11.     Public Function CreateConnection() As System.Data.Common.DbConnection Implements IDBHelperFactory.CreateConnection
  12.         Return New SqlConnection
  13.     End Function

  14.     Public Function CreateDataAdapter() As System.Data.Common.DbDataAdapter Implements IDBHelperFactory.CreateDataAdapter
  15.         Return New SqlDataAdapter
  16.     End Function
  17. End Class

  18. Public Class AccessHelperFactory
  19.     Implements IDBHelperFactory

  20.     Public Function CreateCommand() As System.Data.Common.DbCommand Implements IDBHelperFactory.CreateCommand
  21.         Return New OleDb.OleDbCommand
  22.     End Function

  23.     Public Function CreateConnection() As System.Data.Common.DbConnection Implements IDBHelperFactory.CreateConnection
  24.         Return New OleDb.OleDbConnection
  25.     End Function

  26.     Public Function CreateDataAdapter() As System.Data.Common.DbDataAdapter Implements IDBHelperFactory.CreateDataAdapter
  27.         Return New OleDb.OleDbDataAdapter
  28.     End Function
  29. End Class
  30. Public Class Client
        Public Sub Test(ByVal HelperFactory As IDBHelperFactory)
            Dim Conn As DbConnection = HelperFactory.CreateConnection
        End Sub
    End Class
评述:
这就是传说中的抽象工厂,有人也许会问:你的AbstractProduct呢?其实是.Net代劳了,就是上面的DBConnection DBCommand DBDataAdapter.否则还需要抽象产品。看看客户端时只要对参数HelperFactory在配置文件中配置就可以实现多种数据库的切换了。
每一种模式都有相应的应用场景,在这个例子中,如果相互依赖的对象经常发生改变就不适合采用改模式,同样,只有出现一系列相互依赖的对象的时候,并且需求常发生变化(需要多种系列)。当出现一系列相互依赖关系的对象时,也有可能采用静态工厂就完全满足了。对于上面的抽象工厂已经能够解决一系列相互依赖关系的对象的创建及其系列需求增加的问题。但在切换使用系列对象的时候还是要修改客户端代码。如何解决呢?我们可以利用C#语言的反射机制,做如下改进:
实现七:反射抽象工厂
  1. Public Class Client
  2.     Public Sub Test(ByVal HelperFactory As IDBHelperFactory)
  3.         ' 通过修改配置文件, 得到不同的工厂
  4.         Dim DBHelperFactoryName As String = Configuration.ConfigurationSettings.AppSettings("DBHelperFactoryName")
  5.         Dim DBHelperFactory = CType(Reflection.Assembly.Load("ReflectFactory").CreateInstance("ReflectFactory." _
  6.                         & DBHelperFactoryName), IDBHelperFactory)
  7.         Dim Conn As DbConnection = HelperFactory.CreateConnection
  8.     End Sub
  9. End Class
评述:此处利用了C#的语言机制。反射是需要一定的资源的。所以有时候我们可以用缓存方式来解决这个资源消耗问题。
实现八:泛型工厂



工厂方法
结构图:

意图:
定义一个用户创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。


抽象工厂
结构图:


实例结构图

意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
动机:
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作
工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

理解要点:

主要是为了Client程序保证隔离变化点,客户称许只是以来抽象类或者接口,而具体实现类不被依赖;

出现相互依赖的对象系列,这些对香直接有依赖性或者交互性(比如同样的表现风格,比如同样的规则基础,但是这个系列不是指这些对香有同样的功能(或者科研用同样的基类),这个系列在途中表现为ProductA与ProductB);

客户需求中出现多个系列的对象,(就是ProductA1与ProductB1,还有ProductA2和ProductB2等等)

抽象工厂变化中心点是系列多变,而不是product种类变化。也就是说这是应对多系列对象变化(新系列)的需求而不是新对象变化的需求。

经常与Factory Method组合使用。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值