




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
在上一篇文章中(Singleton设计模式)曾谈到了工厂模式,那究竟什么是工厂模式呢?它又能解决什么样的问题呢?在知晓这个问题之前,让我们先了解一个概念:对象耦合。经常在很多技术书籍上听到松耦合,高内聚。那什么是松耦合呢?既然有松耦合,那应该存在相对的紧耦合吧。两者之间到底有什么联系呢? 带着这些问题,我们可以展开为什么需要工厂模式的缘由。松耦合即在软件系统中,模块与模块之间在应对软件变化(主要来自需求变化)时,一些模块很容易被替换或更改,而能使其他模块保持不变。相对的,紧耦合即软件应对变化时,一些软件的改变将会使相关模块发生改变。先看看下面的两幅图: 图1紧耦合 图2松耦合我们在这里姑且说明一下,两图中的每根线代表系统的模块,图2中的圆圈代表接口。从图1中我们可以看出,各个系统模块之间相互依赖,当我们面对需求改变时,也就是说图1中有几根线需要改变时,与之相关的线将因此随之而改变,导致整个系统处于不稳定状态之中。而图2中,我们假设中间的那根长线为系统的主模块,而在主模块上附加的那些分支为次模块,并且此模块与主模块之间是以接口来建立依赖关系,各此模块之间彼此相对独立,这样当其中几个次模块改变时,不会影响其他的次模块。从两图中我们不难发现,图2较图1更优越,能够保持整个软件系统的稳定性和可维护性。由此我们总结:当我们在设计软件系统时,首先必须分清系统模块的主次,并建立起主模块和多个次模块,主模块与次模块之间以接口的方式相互依赖,各次模块之间通过主模块相互贯通。也即是:高层模块(主模块)不直接依赖于低层模块(次模块),低层模块的变动不会影响到高层模块和其他相关次模块的变动。相信到这里,大家对这个概念应该有充分的认识了,接下来我们开始工厂模式的讲解了,首先以简单工厂开始吧。 设计意图:定义一个用于创建对象的接口(或抽象类),让子类决定实例化哪一个类,客户端却不知道具体的实现类。 应用场景: 在软件系统中,经常面临着某个对象的创建工作,但由于需求的变化导致这个对象的创建方式剧烈的变化,但是它却拥有相对稳定的接口。如何应对这种变化呢?需要提供一种封装机制来隔离这个易变对象的变化,而使依赖于该易变对象的其他对象不随着需求改变而改变。 实例案例:现在存在一套Web系统是在SQL Server数据库环境下工作,而且也运行良好。但突然有一天由于公司业务量的增长,数据量大为剧增可能需要往Oracle迁移,实现数据库的完全切换,可能原有的支持SQL Server数据库环境的数据访问底层已经无法满足现在业务的需要, 但又不能去直接改变底层访问的代码,可能以后还有其他数据库来支撑。我们就会想到使用统一的数据库访问接口,让SQL Server,Oracle以及其他数据库都支持这套接口。这样做固然解决支持多数据库问题,但问题在于客户端调用时,怎样去实例化该接口对应的数据库类型,在这种情况下简单工厂就能很好解决这个创建获取数据库类型的问题了。/ / 定义数据库提供者基类/ 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 abstract 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提供者实现/ public class SqlServerProvider : DBProvider public SqlServerProvider(string connectionString) : base(connectionString) Connection = new SqlConnection(connectionString); public override void Connect() /SqlServer Connect public override bool CheckConect() return true; public override DataTable 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提供者实现/ public 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(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; 最后是获取提供者工厂类:/ / 数据库类型枚举/ public enum DataBaseType SqlServer, Oracle/ / 获取数据库提供者工厂/ 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); 在客户端访问: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=MyHost)(PORT=MyPort)(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=*);User Id=*;Password=*;); 此时发现如何获取数据库提供者的职责完全交给工厂去做了,客户端只需要传入对应的数据库类型和连接字符串就可以获取到对应的数据库提供者,是不是非常简单呢?在这里,我们不妨总结一下:按松耦合思路,客户端只依赖于数据库提供者工厂,而不依赖于具体的数据库提供者,在一定程度上实现了从SQL Server向Oracle迁移的过渡。对于示例而言,主模块是客户端获取数据库提供者接口CreateDBProvider(),只要保持该接口不发生变化,即使其中某个具体的数据库提供者发生变化也不会影响到其他的数据库提供者,这正是松耦合的主旨所在。从这个例子中也能总结出简单工厂的适用范围:要实例化的子类的数量有限且无复杂层级关系,并且可以在编译之前就可以提前预知,例如前面的数据库类型枚举。如果要增加新的类型,势必导致子类的类型增加和工厂的变动(增加枚举标识和switch判断),这无疑与我们的松耦合设计理念相违背(由于次模块的改变导致主模块随之变动),因此我们提出了工厂方法模式来解决这个问题。 首先,我们要明确工厂模式所面临的问题,简单工厂通过传递参数的方式来决定实例化哪个子类,这种方式的弊端在于子类的增加可能是动态的,在编译之前不可预知到。而我们的工厂创建方法属于主模块,不应该随着子类的增加而发生变动。在前面意图中已经说了,定义一个用于创建对象的接口(或抽象类),让子类决定实例化哪一个类,既然要增加子类,而工厂又不想发生改变,那我们想是否可以讲工厂的创建操作提取出来,成为一个工厂基类呢?将具体创建的工作放到工厂的子类中去完成对应的工作呢?这正是工厂方法模式的由来。所以常有人说:简单工厂其实只是参数化的工厂方法模式,只是工厂方法的一个特例,因此GOF也没有将其列入23种设计模式中来。但即便如此,简单工厂还是在我们的项目应用中起到非常关键的作用,只是有它的适用范围罢了。我举个很形象的例子来说明一下工厂模式。大家都知道鸟生蛋这个简单的例子,鸟和蛋是两个虚拟的概念,只有实例化到具体的鸟才能生具体的蛋,脱离了具体的鸟的对象,蛋也就无从谈起了。那假设目前有鸡、鸭、鹅三种鸟,那对应的就有鸡蛋、鸭蛋、鹅蛋,而下蛋的动作是每种鸟必备的,我们只需要创建生成鸟的工厂方法即可。如果采用以前的简单工厂将无法满足要求,随着后面鸟类的增加,就必须改动创建鸟这个工厂方法,因此只能为每一种鸟产生一个具体的创建工厂。 接下来我一个具体的示例来说明工厂方法模式的应用。为了能给大家一个更好展示模式应用过程,我采用迭代式编程的方式向大家逐步介绍。假设目前有一个视频播放器工厂,需要生产一批既可以支持RealPlayer,又可以支持MS Media Player,可能以后还有其他形式的。首先我们定义视频播放器基类。/ / 视频播放器基类(可启动,播放,暂停以及停止)/ 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 : VideoPlayer 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.WriteLine(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.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的播放器。然后客户端只需要将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 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 MSMediaPLayerFactory public static MSMediaPLayer CreateMSMediaPLayer() return new MSMediaPLayer(); TestMethodpublic void TestMSMediaPlayerFactory() var msmediaPlayer = MSMediaPLayerFactory.CreateMSMediaPLayer(); Assert.IsNotNull(msmediaPlayer); msmediaPlayer.StartUp();/MSMediaPLayer is starting. msmediaPlayer.Play();/MSMediaPLayer is playing. msmediaPlayer.Pause(3);/MSMediaPLayer is paused by 3 minutes. msmediaPlayer.Stop();/MSMediaPLayer has stopped.细心的朋友可能会发现,我增加支持MS Media Player,只需要添加一种播放器和创建工厂,然后在客户端将创建工厂修改为新工厂即可。值得关注的是,我并未对原有的支持RealPlayer的播放器以及创建工厂做任何更改。接下来,用户可能觉得要同时支持RealPlayer和MSMediaPlayer的播放器,就必须确切知道有对应的RealPlayerFactory和MSMediaPlayerFactory,如果以后有其他的播放器需要支持,那岂不是让用户为难了吗?从前面的例子,很快让我们萌发了一种了提炼工厂的想法。我们现在将创建播放器这个动作提炼到一个抽象类中(其实也可为接口)。经常会在工厂方法内部定义其他的方法,例如模板模式中就会定义很多模板方法。/ / 视频播放器创建工厂/ public abstract class VideoPlayerFactory public abstract VideoPlayer CreateVideoPlayer();/ / 创建RealPlayer的视频播放器工厂/ public class RealPlayerFactory : VideoPlayerFactory public override VideoPlayer CreateVideoPlayer() return new RealPlayer(); / / 创建MSMediaPLayer的视频播放器工厂/ public class MSMediaPLayerFactory : VideoPlayerFactory public override VideoPlayer CreateVideoPlayer() return new MSMediaPLayer(); 建立单元测试类:TestMethodpublic 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 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();/MSMediaPLayer has stopped.现在两种播放器都支持了,特别注意抽象工厂类VideoPlayerFactory,以及对应的创建方法public abstract VideoPlayer CreateVideoPlayer();发现返回值不再是具体的哪种播放器而是VideoPlayer抽象播放器,并且各自有各自的创建工厂,实现了一定程度上的松耦合,工厂模式将创建实例的工作推迟到子类工厂中完成,从而实现扩展而不更改,这也符合对更改封闭,对扩展开放的原则(OCP原则),可能有的朋友会说,我的客户端依然依赖于具体的创建工厂。怎么办呢?要是有一种机制可以拿我们刚才创建的VideoPlayerFactory类来完成这个工作该多好,但是大家都知道在这里new VideoPlayerFactory()是不现实的,那我们何不采用配置文件的方式来实现呢?我们在配置文件中加入当前需要支持的播放器标识,结合反射机制来动态读取当前播放器标识,不就解决了这个棘手的问题了。 再次建立单元测试类:TestMethodpublic void TestRealPlayerFactoryByConfig() var factory = GetVedioPlayerFactoryByConfig(RealPlayerFormat); Assert.IsNotNull(factory); var player = 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(); 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.LoadFrom(Path.Combine(appPath, DesignPattern.FactoryMethodPattern.dll); /创建RealPlayerFactory var type = DesignPattern.FactoryMethodPattern.FactoryMethod. + format + Factory; var factory = (VideoPlayerFactory)assembly.CreateInstance(type); return factory;现在来看客户端已经不再依赖于任何具体的创建工厂了,而只能抽象创建工厂类VideoPlayerFactory有关,完全实现了解耦。如需要增加新的播放器,只需要添加新的播放器以及创建工厂,然后在配置文件中加入新的播放器类型,然后就可以利用反射工厂动态读取要支持的播放器类型了。是不是给人一种全身释然的感觉了,到这里,可能很多朋友觉得已经很完美了。如果说这样做,那也无可厚非。不过有的时候一想,我创建一种播放器就必须加入一个新的工厂来创建,这样到后来的维护量可能很大,尽管已经解耦了。是否有一种泛型工厂的方式来实现呢?我们继续探讨。/ / 泛型视频播放器工厂/ public abstract class GenericVideoFactory where T : VideoPlayer, new() public 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.FactoryMethodPattern.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.AppSettingsMSMediaPlayerFormat; var appPath = AppDomain.CurrentDomain.BaseDirectory; var assembly = Assembly.LoadFrom(Path.Combine(appPath, DesignPattern.FactoryMethodPattern.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(MSMediaPlayer);现在所有的工厂创建动作移入到抽象的泛型工厂中,子类工厂只需要继承即可,这样从一定程度上减轻了创建实例的负担。在实际项目中,可能还存在这样的场景:经常需要加工一批对象,这时如果按照Singleton-N模式来生产势必效率非常低下,最好有能独立进行生产的批量工厂。试想一下,除了造船、造卫星或造航空母舰等,很难想象造啤酒时要等待工厂进行一系列处理完后,才能产生一瓶啤酒,如果客户要求马上配送5000瓶时,要进行送货那简直就是一件短时间无法完成的事了。继续拿上面的视频播放器为例来说明吧,现在系统是支持了RealPlayer和MS MediaPlayer播放器了,但顾客有天要求他需要3000部支持RealPlayer的,2000部支持MS Media Player的播放器,此时我们就需要批量工厂了。我们来看看下面这张图代表批量生产的流程图。 从图中我们发觉共5个步骤,其中1,4,5都是我们原有工厂方法具有的过程,现在增加了2,3两个步骤,依据职责单一原则,我们通过建立辅助类来完成: 抽象生产指导者类(Director):它告诉客户端程序在保持VideoPlayer抽象播放器接口不变的情况下,生产某类播放器的数量,同时由于生产某类产品是由具体的创建工厂决定的,因此还需要告诉客户端当生产完3000部RealPlayer的播放器后,将创建工厂切换到MSMediaPlayerFactory继续生产2000部MSMediaPlayer。 步骤决策者类(Decision):它包含两个信息,当决策生产3000部RealPlayer播放器时,生产的数量(3000)和实际创建工厂(RealPlayerFactory)。 批量播放器集合类(VideoPlayerList):采用泛型集合类型,同时修改具体创建工厂的返回值,变为单独返回VideoPlayer的集合。 接下来,我们看看如何实现批量工厂。视频播放器集合类 / / 视频播放器集合类/ public class VideoPlayerList private IList players = new List(); #region 插入、删除、清除集合 public void Insert(VideoPlayer item) players.Add(item); public void Insert(VideoPlayer items) if (items = null | items.Length = 0) return; foreach (var item in items) Insert(item); public void Remove(VideoPlayer item) players.Remove(item); public void Clear() players.Clear(); #endregion /获取所有视频播放器的对象 public VideoPlayer Items get if (players = null | players.Count = 0) return null; var items = new VideoPlayerplayers.Count; players.CopyTo(items, 0); return items; public int Count get return players.Count; /为了便于操作,这里重载运算符+ public static VideoPlayerList operator +(VideoPlayerList players, VideoPlayer items) var result = new VideoPlayerList(); if (players != null & players.Count 0) result.Insert(players.Items); if (items != null & items.Length 0) result.Insert(items); return result; public static VideoPlayerList operator +(VideoPlayerList source, VideoPlayerList target) var result = new VideoPlayerList(); if (source != null & source.Count 0) result.Insert(source.Items); if (target != null & target.Count 0) result.Insert(target.Items); return result; 批量工厂接口 / / 批量加工接口/ public interface IBatchFactory VideoPlayerList CreateVideoPlayers(int quantity);/ / 批量加工的抽象基类/ public abstract class BatchVideoPlayerFactory : IBatchFactory where T : VideoPlayer, new() public virtual VideoPlayerList CreateVideoPlayers(int quantity) if (quantity = 0) return null; var players = new VideoPlayerList(); for (var i = 0; i quantity; i+) players.Insert(new T(); return players; /两个具体批量创建工厂public class BatchRealPlayerFactory : BatchVideoPlayerFactory public class BatchMSMediaPlayerFactory : BatchVideoPlayerFactory 步骤决策者 / / 步骤决策者基类(包含创建工厂和数量)/ public abstract class Decision protected IBatchFactory factory; protected int quantity; public Decision(IBatchFactory factory, int quantity) this.factory = factory; this.quantity = quantity; pub
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工厂安全培训知识课件
- 2025四川绵阳市游仙区医疗卫生辅助岗招募26人备考考试题库附答案解析
- 2025河南新乡经济技术开发区经四路小学教师招聘2人备考考试题库附答案解析
- 2025山东东营市垦利区董集镇城乡公益性岗位招聘22人备考考试题库附答案解析
- 2025黑龙江人才周校园引才活动绥化职业技术教育中心下属事业单位绥化市职业技术学校招聘专业技术人员3人考试参考试题及答案解析
- 广安市广安区2025年下半年“小平故里英才计划”引进急需紧缺专业人才(17人)备考考试题库附答案解析
- 2025年蚌埠慕远学校招聘临聘教师7人备考考试题库附答案解析
- 开关插座招商活动策划方案
- 2025年甘肃省酒泉市敦煌藏医医院招聘考试备考试题及答案解析
- 2025年龙江银行股份有限公司校园招聘100人备考考试题库附答案解析
- 建筑设计数字化协同工作方案
- 新入行员工安全教育培训课件
- 原生家庭探索课件
- 人教版音乐八年级上册-《学习项目二探索旋律结构的规律》-课堂教学设计
- 《中国人民站起来了》课件 (共50张)2025-2026学年统编版高中语文选择性必修上册
- 2025-2030滑雪培训行业市场发展分析及前景趋势预测与投资可行性评估报告
- 中国企业供应链金融白皮书(2025)-清华五道口
- 2025年陕西省专业技术人员继续教育公需课答案
- 2025年土方坍塌应急预案演练脚本
- 医院常用消毒液的使用及配置方法
- 2022英威腾MH600交流伺服驱动说明书手册
评论
0/150
提交评论