面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc

上传人:scccc 文档编号:11256318 上传时间:2021-07-18 格式:DOC 页数:13 大小:169.50KB
返回 下载 相关 举报
面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc_第1页
第1页 / 共13页
面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc_第2页
第2页 / 共13页
面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc_第3页
第3页 / 共13页
面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc_第4页
第4页 / 共13页
面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc》由会员分享,可在线阅读,更多相关《面向对象的设计模式系列之二:工厂方法模式(FactoryMethod).doc(13页珍藏版)》请在三一文库上搜索。

1、在上一篇文章中(Singleton设计模式)曾谈到了工厂模式,那究竟什么是工厂模式呢?它又能解决什么样的问题呢?在知晓这个问题之前,让我们先了解一个概念:对象耦合。经常在很多技术书籍上听到松耦合,高内聚。那什么是松耦合呢?既然有松耦合,那应该存在相对的紧耦合吧。两者之间到底有什么联系呢? 带着这些问题,我们可以展开为什么需要工厂模式的缘由。松耦合即在软件系统中,模块与模块之间在应对软件变化(主要来自需求变化)时,一些模块很容易被替换或更改,而能使其他模块保持不变。相对的,紧耦合即软件应对变化时,一些软件的改变将会使相关模块发生改变。先看看下面的两幅图: 图1紧耦合 图2松耦合我们在这里姑且说明

2、一下,两图中的每根线代表系统的模块,图2中的圆圈代表接口。从图1中我们可以看出,各个系统模块之间相互依赖,当我们面对需求改变时,也就是说图1中有几根线需要改变时,与之相关的线将因此随之而改变,导致整个系统处于不稳定状态之中。而图2中,我们假设中间的那根长线为系统的主模块,而在主模块上附加的那些分支为次模块,并且此模块与主模块之间是以接口来建立依赖关系,各此模块之间彼此相对独立,这样当其中几个次模块改变时,不会影响其他的次模块。从两图中我们不难发现,图2较图1更优越,能够保持整个软件系统的稳定性和可维护性。由此我们总结:当我们在设计软件系统时,首先必须分清系统模块的主次,并建立起主模块和多个次模

3、块,主模块与次模块之间以接口的方式相互依赖,各次模块之间通过主模块相互贯通。也即是:高层模块(主模块)不直接依赖于低层模块(次模块),低层模块的变动不会影响到高层模块和其他相关次模块的变动。相信到这里,大家对这个概念应该有充分的认识了,接下来我们开始工厂模式的讲解了,首先以简单工厂开始吧。 设计意图:定义一个用于创建对象的接口(或抽象类),让子类决定实例化哪一个类,客户端却不知道具体的实现类。 应用场景: 在软件系统中,经常面临着某个对象的创建工作,但由于需求的变化导致这个对象的创建方式剧烈的变化,但是它却拥有相对稳定的接口。如何应对这种变化呢?需要提供一种封装机制来隔离这个易变对象的变化,而

4、使依赖于该易变对象的其他对象不随着需求改变而改变。 实例案例:现在存在一套Web系统是在SQL Server数据库环境下工作,而且也运行良好。但突然有一天由于公司业务量的增长,数据量大为剧增可能需要往Oracle迁移,实现数据库的完全切换,可能原有的支持SQL Server数据库环境的数据访问底层已经无法满足现在业务的需要,http:/ 但又不能去直接改变底层访问的代码,可能以后还有其他数据库来支撑。我们就会想到使用统一的数据库访问接口,让SQL Server,Oracle以及其他数据库都支持这套接口。这样做固然解决支持多数据库问题,但问题在于客户端调用时,怎样去实例化该接口对应的数据库类型,

5、在这种情况下简单工厂就能很好解决这个创建获取数据库类型的问题了。/ / 定义数据库提供者基类/ public abstract class DBProvider protected string connectionString = String.Empty; protected DbConnection Connection = null; public DBProvider(string connectionString) this.connectionString = connectionString; public abstract void Connect(); public abs

6、tract bool CheckConect(); public abstract DataTable ExecuteDataSet(string selectSql); public abstract bool ExecuteInsert(string insertSql); public abstract bool ExecuteUpdate(string updateSql); public abstract bool ExecuteDelete(string deleteSql);接下来是Sql Server提供者实现类:Sql Server提供者实现 / / Sql Server提供

7、者实现/ public class SqlServerProvider : DBProvider public SqlServerProvider(string connectionString) : base(connectionString) Connection = new SqlConnection(connectionString); public override void Connect() /SqlServer Connect http:/ public override bool CheckConect() return true; public override DataT

8、able ExecuteDataSet(string selectSql) return null; public override bool ExecuteInsert(string insertSql) return true; public override bool ExecuteUpdate(string updateSql) return true; public override bool ExecuteDelete(string deleteSql) return true; 接下来是Oracle提供者实现类:Oracle提供者实现 / / Oracle提供者实现/ publi

9、c class OracleProvider : DBProvider public OracleProvider(string connectionString) : base(connectionString) Connection = new OracleConnection(connectionString); public override void Connect() /Oracle Connect public override bool CheckConect() return true; public override DataTable ExecuteDataSet(str

10、ing selectSql) return null; public override bool ExecuteInsert(string insertSql) return true; public override bool ExecuteUpdate(string updateSql) return true; public override bool ExecuteDelete(string deleteSql) return true; 最后是获取提供者工厂类:/ / 数据库类型枚举/ public enum DataBaseType SqlServer, Oracle/ / 获取数

11、据库提供者工厂/ public class DBFactory public static DBProvider CreateDBProvider(DataBaseType dbType, string connectionString) switch (dbType) case DataBaseType.SqlServer: default: return new SqlServerProvider(connectionString); case DataBaseType.Oracle: return new OracleProvider(connectionString); 在客户端访问:

12、public class Program public static void Main() var sqlserverProvider = DBFactory.CreateDBProvider(DataBaseType.SqlServer, server=*;database=*;uid=*;password=*); var oracleProvider = DBFactory.CreateDBProvider(DataBaseType.Oracle, Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=My

13、Host)(PORT=MyPort)(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=*);User Id=*;Password=*;); 此时发现如何获取数据库提供者的职责完全交给工厂去做了,客户端只需要传入对应的数据库类型和连接字符串就可以获取到对应的数据库提供者,是不是非常简单呢?在这里,我们不妨总结一下:按松耦合思路,客户端只依赖于数据库提供者工厂,而不依赖于具体的数据库提供者,在一定程度上实现了从SQL Server向Oracle迁移的过渡。对于示例而言,主模块是客户端获取数据库提供者接口CreateDBProvider(),只要保持该接口不

14、发生变化,即使其中某个具体的数据库提供者发生变化也不会影响到其他的数据库提供者,这正是松耦合的主旨所在。从这个例子中也能总结出简单工厂的适用范围:要实例化的子类的数量有限且无复杂层级关系,并且可以在编译之前就可以提前预知,例如前面的数据库类型枚举。如果要增加新的类型,势必导致子类的类型增加和工厂的变动(增加枚举标识和switch判断),这无疑与我们的松耦合设计理念相违背(由于次模块的改变导致主模块随之变动),因此我们提出了工厂方法模式来解决这个问题。 首先,我们要明确工厂模式所面临的问题,简单工厂通过传递参数的方式来决定实例化哪个子类,这种方式的弊端在于子类的增加可能是动态的,在编译之前不可预

15、知到。而我们的工厂创建方法属于主模块,不应该随着子类的增加而发生变动。在前面意图中已经说了,定义一个用于创建对象的接口(或抽象类),让子类决定实例化哪一个类,既然要增加子类,而工厂又不想发生改变,那我们想是否可以讲工厂的创建操作提取出来,成为一个工厂基类呢?将具体创建的工作放到工厂的子类中去完成对应的工作呢?这正是工厂方法模式的由来。所以常有人说:简单工厂其实只是参数化的工厂方法模式,只是工厂方法的一个特例,因此GOF也没有将其列入23种设计模式中来。但即便如此,简单工厂还是在我们的项目应用中起到非常关键的作用,只是有它的适用范围罢了。我举个很形象的例子来说明一下工厂模式。大家都知道鸟生蛋这个

16、简单的例子,鸟和蛋是两个虚拟的概念,只有实例化到具体的鸟才能生具体的蛋,脱离了具体的鸟的对象,蛋也就无从谈起了。那假设目前有鸡、鸭、鹅三种鸟,那对应的就有鸡蛋、鸭蛋、鹅蛋,而下蛋的动作是每种鸟必备的,我们只需要创建生成鸟的工厂方法即可。如果采用以前的简单工厂将无法满足要求,随着后面鸟类的增加,就必须改动创建鸟这个工厂方法,因此只能为每一种鸟产生一个具体的创建工厂。 接下来我一个具体的示例来说明工厂方法模式的应用。为了能给大家一个更好展示模式应用过程,我采用迭代式编程的方式向大家逐步介绍。假设目前有一个视频播放器工厂,需要生产一批既可以支持RealPlayer,又可以支持MS Media Pla

17、yer,可能以后还有其他形式的。首先我们定义视频播放器基类。/ / 视频播放器基类(可启动,播放,暂停以及停止)/ public abstract class VideoPlayer public abstract void StartUp(); public abstract void Play(); public abstract void Pause(int inteval); public abstract void Stop();支持RealPlayer的视频播放器以及创建工厂 / / 支持RealPlayer的视频播放器/ public class RealPlayer : Vid

18、eoPlayer public override void StartUp() Console.WriteLine(RealPlayer is starting.); public override void Play() Console.WriteLine(RealPlayer is playing.); public override void Pause(int inteval) Console.WriteLine(RealPlayer is paused by + inteval + minutes.); public override void Stop() Console.Writ

19、eLine(RealPlayer has stopped.); / / 创建RealPlayer的视频播放器工厂/ public class RealPlayerFactory public static RealPlayer CreateRealPlayer() return new RealPlayer(); TestMethodpublic void TestRealPlayerFactory() var realPlayer = RealPlayerFactory.CreateRealPlayer(); Assert.IsNotNull(realPlayer); realPlayer.

20、StartUp();/RealPlayer is starting. realPlayer.Play();/RealPlayer is playing. realPlayer.Pause(2);/RealPlayer is paused by 2 minutes. realPlayer.Stop();/RealPlayer has stopped.从单元测试类中,我们可以看出客户端依赖于RealPlayerFactory工厂,如果有一天用户需要支持MS Media Player播放器,势必要生成一个MSMediaPlayerFactory用于创建支持MS Media Player的播放器。然后

21、客户端只需要将RealPlayerFactory换成MSMediaPlayerFactory,视频播放器就会支持MS Media Player了。这就是与简单工厂无止境的实例类型注入和switch类型判断相区别。支持MS Media Player的视频播放器以及创建工厂 / / 支持MS Media Player的视频播放器/ public class MSMediaPLayer : VideoPlayer public override void StartUp() Console.WriteLine(MSMediaPLayer is starting.); public override

22、void Play() Console.WriteLine(MSMediaPLayer is playing.); public override void Pause(int inteval) Console.WriteLine(MSMediaPLayer is paused by + inteval + minutes.); public override void Stop() Console.WriteLine(MSMediaPLayer has stopped.); / / 创建MSMediaPLayer的视频播放器工厂/ public class MSMediaPLayerFact

23、ory public static MSMediaPLayer CreateMSMediaPLayer() return new MSMediaPLayer(); TestMethodpublic void TestMSMediaPlayerFactory() var msmediaPlayer = MSMediaPLayerFactory.CreateMSMediaPLayer(); Assert.IsNotNull(msmediaPlayer); msmediaPlayer.StartUp();/MSMediaPLayer is starting. msmediaPlayer.Play()

24、;/MSMediaPLayer is playing. msmediaPlayer.Pause(3);/MSMediaPLayer is paused by 3 minutes. msmediaPlayer.Stop();/MSMediaPLayer has stopped.细心的朋友可能会发现,我增加支持MS Media Player,只需要添加一种播放器和创建工厂,然后在客户端将创建工厂修改为新工厂即可。值得关注的是,我并未对原有的支持RealPlayer的播放器以及创建工厂做任何更改。接下来,用户可能觉得要同时支持RealPlayer和MSMediaPlayer的播放器,就必须确切知道有

25、对应的RealPlayerFactory和MSMediaPlayerFactory,如果以后有其他的播放器需要支持,那岂不是让用户为难了吗?从前面的例子,很快让我们萌发了一种了提炼工厂的想法。我们现在将创建播放器这个动作提炼到一个抽象类中(其实也可为接口)。经常会在工厂方法内部定义其他的方法,例如模板模式中就会定义很多模板方法。/ / 视频播放器创建工厂/ public abstract class VideoPlayerFactory public abstract VideoPlayer CreateVideoPlayer();/ / 创建RealPlayer的视频播放器工厂/ publi

26、c class RealPlayerFactory : VideoPlayerFactory public override VideoPlayer CreateVideoPlayer() return new RealPlayer(); / / 创建MSMediaPLayer的视频播放器工厂/ public class MSMediaPLayerFactory : VideoPlayerFactory public override VideoPlayer CreateVideoPlayer() return new MSMediaPLayer(); 建立单元测试类:TestMethodpu

27、blic void TestVedioPlayerFactory() var realPlayer = new RealPlayerFactory().CreateVideoPlayer(); Assert.IsNotNull(realPlayer); realPlayer.StartUp();/RealPlayer is starting. realPlayer.Play();/RealPlayer is playing. realPlayer.Pause(2);/RealPlayer is paused by 2 minutes. realPlayer.Stop();/RealPlayer

28、 has stopped. var msmediaPlayer = new MSMediaPLayerFactory().CreateVideoPlayer(); Assert.IsNotNull(msmediaPlayer); msmediaPlayer.StartUp();/MSMediaPLayer is starting. msmediaPlayer.Play();/MSMediaPLayer is playing. msmediaPlayer.Pause(3);/MSMediaPLayer is paused by 3 minutes. msmediaPlayer.Stop();/M

29、SMediaPLayer has stopped.现在两种播放器都支持了,特别注意抽象工厂类VideoPlayerFactory,以及对应的创建方法public abstract VideoPlayer CreateVideoPlayer();发现返回值不再是具体的哪种播放器而是VideoPlayer抽象播放器,并且各自有各自的创建工厂,实现了一定程度上的松耦合,工厂模式将创建实例的工作推迟到子类工厂中完成,从而实现扩展而不更改,这也符合对更改封闭,对扩展开放的原则(OCP原则),可能有的朋友会说,我的客户端依然依赖于具体的创建工厂。怎么办呢?要是有一种机制可以拿我们刚才创建的VideoPla

30、yerFactory类来完成这个工作该多好,但是大家都知道在这里new VideoPlayerFactory()是不现实的,那我们何不采用配置文件的方式来实现呢?我们在配置文件中加入当前需要支持的播放器标识,结合反射机制来动态读取当前播放器标识,不就解决了这个棘手的问题了。 再次建立单元测试类:TestMethodpublic void TestRealPlayerFactoryByConfig() var factory = GetVedioPlayerFactoryByConfig(RealPlayerFormat); Assert.IsNotNull(factory); var play

31、er = factory.CreateVideoPlayer(); Assert.IsNotNull(player); Assert.IsTrue(player.GetType() = typeof(RealPlayer);TestMethodpublic void TestMSMediaPlayerFactoryByConfig() var factory = GetVedioPlayerFactoryByConfig(MSMediaPlayerFormat); Assert.IsNotNull(factory); var player = factory.CreateVideoPlayer

32、(); Assert.IsNotNull(player); Assert.IsTrue(player.GetType() = typeof(MSMediaPlayer);private VideoPlayerFactory GetVedioPlayerFactoryByConfig(string formatKey) var format = ConfigurationManager.AppSettingsformatKey; var appPath = AppDomain.CurrentDomain.BaseDirectory; var assembly = Assembly.LoadFro

33、m(Path.Combine(appPath, DesignPattern.FactoryMethodPattern.dll); /创建RealPlayerFactory var type = DesignPattern.FactoryMethodPattern.FactoryMethod. + format + Factory; var factory = (VideoPlayerFactory)assembly.CreateInstance(type); return factory;现在来看客户端已经不再依赖于任何具体的创建工厂了,而只能抽象创建工厂类VideoPlayerFactory

34、有关,完全实现了解耦。如需要增加新的播放器,只需要添加新的播放器以及创建工厂,然后在配置文件中加入新的播放器类型,然后就可以利用反射工厂动态读取要支持的播放器类型了。是不是给人一种全身释然的感觉了,到这里,可能很多朋友觉得已经很完美了。如果说这样做,那也无可厚非。不过有的时候一想,我创建一种播放器就必须加入一个新的工厂来创建,这样到后来的维护量可能很大,尽管已经解耦了。是否有一种泛型工厂的方式来实现呢?我们继续探讨。/ / 泛型视频播放器工厂/ public abstract class GenericVideoFactory where T : VideoPlayer, new() publ

35、ic virtual T CreateVideoPlayer() return new T(); 再次建立单元测试类:TestMethodpublic void TestGenericRealPlayerFactory() var format = ConfigurationManager.AppSettingsRealPlayerFormat; var appPath = AppDomain.CurrentDomain.BaseDirectory; var assembly = Assembly.LoadFrom(Path.Combine(appPath, DesignPattern.Fac

36、toryMethodPattern.dll); /创建RealPlayerFactory var type = DesignPattern.FactoryMethodPattern.FactoryMethod.Generic + format + Factory; var factory = (GenericVideoFactory)assembly.CreateInstance(type); Assert.IsNotNull(factory); var player = factory.CreateVideoPlayer(); Assert.IsNotNull(player); Assert.IsTrue(player.GetType() = typeof(RealPlayer);TestMethodpublic void TestGenericMSMediaPlayerFactory() var format = ConfigurationManager.App

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 社会民生


经营许可证编号:宁ICP备18001539号-1