由浅入深学“工厂模式”_第1页
由浅入深学“工厂模式”_第2页
由浅入深学“工厂模式”_第3页
免费预览已结束,剩余14页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、由浅入深学 “工厂模式 ”(1)2007-03-25 17:131、传授设计模式中存在的问题我个人最近对设计模式中的工厂模式进行了比较深入的学习, 通过学习感悟 出,现在很多设计模式传道者, 在讲述设计模式的过程中存在一些问题, 使得设 计模式变得很难理解。设计模式本身很抽象,但是这些传道者在讲述的过程中, 将一个抽象的东西讲的更抽象, 从而使我们望而却步。 有些人在讲述的时候, 没 有考虑读者的注意力。比如我在看 C#设计模式的时候,在讲述抽象工厂模式 的时候,直接就进入了一个示例, 而且这个例子比较复杂, 涉及了比较多的概念、 术语,而且还有算法要处理。 但是这些和要讲述的核心内容无关,

2、我想要看懂他 的示例就要将这个东西都搞懂, 就分散了我的注意力。 我个人总结, 学习设计模 式的方法是,首先找到一个突破口,比如可以先学习构造型模式中简单的模式, 将它理解、熟练应用。通过对一、两个模式有一个深入的认识之后,再学习其它 比较复杂一点的模式就容易多了, 这是一种迭代的思想在学习中的应用。 另外学 习任何一种设计模式的过程应该是具体抽象再具体这个的一个过程。 这句话 的意思是首先通过一个比较具体一点的例子来帮助理解设计模式, 在理解之后将 你的理解扩展到解决这一类问题,上升到一定的理论高度。然后就是再到具体, 也就是应用设计模式,应用理论解决自己遇到的实际问题。2、学习工厂模式的预

3、备知识:首先声明这些预备知识并不是工厂模式仅仅需要,因为我先讲述工厂模式, 所以在学习工厂模式之前将这些问题提出。Upcasting:Upcasting 中文翻译有好几个,比如向上类型转换、向上转型、上溯造型。 我个人比较喜欢向上转型这个翻译, 即简单又含义明确。 向上转型这个概念, 我 在 Bruce Eckel的 Thinking in c+、 Thinking in Java中都看到过,我不是很确定这 个概念是否是他提出来的。 向上转型是把一个派生类当作它的基类使用。 我们将 一个更特殊的类型转换到一个更常规的类型, 这当然是安全的。 派生类是基类的 一个超集。 它可以包含比基类更多的方

4、法, 但它至少包含了基类的方法。 向上转 型给我们带来的好处就是我们可以将不同的派生通过一种统一的方式进行处理。 向上转型带来的弊端就是我们向上转型的过程会丢失派生类的接口。 既然有向上 转型,也就有向下转型即 DownCasting,我们在此不做详细讨论。下面使用一个 例子来示例向上转型。public class Base public void Test() ("OK"); public class Derive:Base private void button1_Click(object sender, e) Base b=new Derive();();多态我不敢想

5、象离开了多态后的设计模式是一个什么样子。 什么是多态, 我喜欢 总结这样一句话来回答这个问题, “一个接口,多种实现 ”。注意这里的接口不仅 仅表示 Interface 关键字,是广义上的接口。在 C#中实现接口我们有两种途径, 一种是借助继承来实现,一种是借助 Interface 来实现。3、工厂设计模式理论概述工厂模式具体包括了简单工厂、 工厂方法、抽象工厂, 它们是按照从简单到 复杂的顺序排列的,属于设计模式中的创建型,其中简单工厂并不属于GOF的23 中模式。但是它是理解其它的工厂模式的一个很好的基础,所以很多人在讲 述设计模式的时候会提到简单工厂模式。 创建型模式关注的是对象的创建,

6、 创建 型模式将创建对象的过程进行了抽象, 也可以理解为将创建对象的过程进行了封 装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑。不使用任何模式我们现在有这样的一个设计,影像家电( VideoWiring)包括了 DVD、VCD。 在基类 VideoWiring 中有 PlayVideo 方法,子类重载了这个方法。我们如何来调用 PlayVideo 进行播放呢。我们可以看到下面的代码可以实现。下面是调用对象的方法进行播放的代码:public abstract class VideoWiringpublic abstract string PlayVideo();public

7、 class VCD: VideoWiringpublic override string PlayVideo()return " 正在播放播放 VCD"public class DVD: VideoWiringpublic override string PlayVideo()return " 正在播放播放 DVD"();这样的语句。private void PlayVideo()DVD dvd=new DVD();();VCD vcd=new VCD();();但是我们在调 要写很多的类上面的代码可以实现功能但是不好, 为什么呢类实现了多态, 用的时

8、候并没有利用多态。 如果我们有很多的影像家电产品, 就 似面是使用多态完成播放功能的代码:private void PlayVideo() VideoWiring vw; vw=new DVD();Play(vw);vw=new VCD();Play(vw);private void Play(VideoWiring vw)string str=();(str);无论是什么影像家电产品, 我们都可以使用一个统一的方式进行播放, 即()我们再讨论一下, 上面的代码存在的问题。 虽然上的代码很短, 应该不会有 问题,但是我们定位的目标应该更高些, 应该考虑怎样达到良好的封装效果, 减 少错误修改的

9、机会。 我们自然的应该考虑对象创建的问题了, 能不能让不同的影 像家电产品的创建方式相同, 而且这个创建过程对使用者封装, 也就是说让对象的创建象播放功能那样简单、 可扩展性和尽量少的修改量。 厂模式,听说它能够实现 ”统一。 如果能够实现, 会给我们的系统带来更大的 “哇!那该多好呀 ”。“不要羡慕了,来看看简单工简单工厂模式我们使用简单工厂对上面的代码继续改进, 根据上面的分析我们考虑对对象 创建进行近一步的封装。 使用一个类专门来完成对对象创建的封装, 这个类我们 称为工厂, 因为它的作用很单一就生成出一个个的类。 下面是一个工厂类的示例 代码:public class Createpu

10、blic static VideoWiring factory(string VideoName) switch(VideoName)case "DVD": return new DVD();case "VCD": return new VCD();return null;这样我们的客户端代码又可以更加有效简洁了:注意:在上面的两段代码示例中我们就已经使用了向上转型。首先注意在 Create类的 factory 方法中使用了 return new DVD();这样的语句,但是这个函数的 返回值却是 VideoWiring,它 DVD 类的基类。所以我们的

11、客户程序才可以使用 VideoWiring vw=("DVD")这样的语句。这样客户程序并不关心创建是如何完成的, 以及创建的对象是什么, 我们都可以调用基类统一的接口实现他们的功能。 使用 UML 表示如下图所示:private void PlayVideo()VideoWiring vw=("DVD");();vw=("VCD");();角色说明:工厂类( Creator):根据业务逻辑创建具体产品,由客户程序直接调用抽象产品( Product):作为具体产品的基类,提供统一的接口,也是工厂 类要返回的类型。具体产品( Concr

12、ete Product):工厂类真正要创建的类型。上图中仅仅展 示了一个具体产品,有多个产品的时候类似。下面我们对简单工厂模式进行总结。使用简单工厂的好处是:1、充分利用了多态性不管什么具体产品都返回抽象产品。2、充分利用了封装性,内部产品发生变化时外部使用者不会受到影响。缺点是:如果增加了新的产品,就必须得修改工厂 (Factory)抽象工厂模式可以向客户端提供一个接口, 使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。这就是抽象工厂模式的用意我们将工厂模式推广到一般的情况,它的类图如下所示:在有名的 OOD 的设计原则中有一个叫做里氏代换原则 (Liskov Sub

13、stitution Principle, LSP。) 它的实质也就是讲向上转型。它的内容是:任何接收父类型的地 方,都应当能够接收子类型, 换句话说如果使用的是一个基类的话, 那么一定适 用于其子类,而且程序察觉不出基类对象和子类对象的区别。 LSP是继承复用的 基石,只有当派生类可以替换掉基类, 软件的功能不受到影响时, 基类才能真正 被复用。工厂方法有了简单工厂模式后,已经给我们带来了一些好处,但是还存在一些问题, 如果我们又多了一个影像家电产品 MP4 之后,我们可以使 MP4 类从 VideoWiring 派生,但是却要修改 Create类的代码使它能够生产出 MP4 这个产品来。不好

14、的 地方就在于,我们每次多一个产品的时候都需要修改 Create 而不是保持原来的 代码不修改仅仅进行一种扩展。 在 Create类中修改不是每次都简单的多一个 Case 语句就能够解决问题。因为 Create 类中还封装了创建对象的逻辑,有可能还需 要修改这些逻辑。这就违反了面向对象设计中一个很重要的原则“开-闭 ”原则。“开-闭”原则( the Open Closed Principle OC)P : 在面向对象设计中,如何通过很小的设计改变就可以应对设计需求的变化, 这是令设计者极为关注的问题。 开闭原则就是 一个软件实体在扩展性方面应该是 开放的而在更改性方面应该是封闭的。 这个原则说

15、的是, 在设计一个模块的时候, 应当使这个模块可以在不被修改的前提下被扩展。 通过扩展已有的软件系统, 可 以提供新的行为, 以满足对软件的新需求, 使变化中的软件系统有一定的适应性 和灵活性。 已有的软件模块, 特别是最重要的抽象层模块不能再修改, 这就使得 变化中的软件系统有一定的稳定性和延续性。 因此在进行面向对象设计时要尽量 考虑接口封装机制、抽象机制和多态技术。前边设计(简单工厂) 中存在的问题就是它分装了创建不同对象的逻辑, 当 有新的产品的时候不易扩展。 在开闭原则的指导下我们考虑如何重新修改前边的 设计,我们要尽量使用抽象机制和多态技术。 我们放弃对创建不同对象的逻辑的 封装,

16、也采用类似产品的方式,抽象出抽象工厂,具体工厂,具体工厂从抽象工 厂派生,每个具体工厂中生产一种具体的产品。 “太棒了,告诉你,你的这个想 法就是工厂方法模式 ”。面使用工厂方法模式修改前边的设计:VideoWiring、DVD、VCD三个类的代码和前边的相同,下面我们看看在客户端如 何使用。public abstract class Createpublic abstract VideoWiring factory();public class DVDCreate: Createpublic override VideoWiring factory()return new DVD();pub

17、lic class VCDCreate: Createpublic override VideoWiring factory()return new VCD();MP4 的时候如何处理。下面我们考虑需要扩展一个新的产品private void PlayVideo()VideoWiring dvd,vcd;Create dvdCreate,vcdCreate;dvdCreate=new DVDCreate();dvd=();Play(dvd);vcdCreate=new VCDCreate();vcd=();Play(vcd);我们来看看增加的代码:public class MP4Create:

18、 Createpublic override VideoWiring factory()return new MP4();public class MP4: VideoWiringpublic override string PlayVideo()return " 正在播放 MP4"我们再看看客户端代码:MP4 的时候没有修改原来的代码,而仅仅是对原来的功能进行扩展系统便有了 MP4 这个产品的功能。private void PlayVideo()VideoWiring dvd,vcd;Create dvdCreate,vcdCreate;dvdCreate=new DVD

19、Create();dvd=();Play(dvd);vcdCreate=new VCDCreate();vcd=();Play(vcd);/ 下面是新增的代码VideoWiring mp4;Create mp4Create;mp4Create=new MP4Create();mp4=();Play(mp4);我们可以看出使用了工厂方法模式后, 很好的满足了开闭原则, 当我们增加 了一个新的产品将工厂方法模式推广到一般情况:角色说明:抽象工厂( Creator):定义具体工厂的接口,所有的创建对象的工厂类都必 须实现这些接口。具体工厂 (ConcreteCreator):具体工厂包含与应用密切相

20、关的逻辑。复杂创 建具体的产品。抽象产品 (Product):所有产品的基类。具体产品 (ConcreteProduct):实现抽象产品申明的接口。工厂方法模式所创 建的每个对象都是某个具体产品的实例。工厂方法模式的用意是定义一个创建产品对象的工厂接口, 将实际创建工作 推迟到子类中。 工厂方法模式是简单工厂模式的进一步抽象和推广。 由于使用了 多态性, 工厂方法模式保持了简单工厂模式的优点, 而且克服了它的缺点。 在工 厂方法模式中, 核心的工厂类不再负责所有的产品的创建, 而是将具体创建的工 作交给子类去做。 这个核心类则成为了一个抽象工厂角色, 仅负责给出具体工厂 子类必须实现的接口,

21、而不接触哪一个产品类应当被实例化这种细节。 这种进一 步抽象化的结果,使这种工厂方法模式可以用来允许系统在不修改具体工厂角色 的情况下引进新的产品。抽象工厂模式我们继续对影像家电产品的情形进行分析,我们已经可以使用工厂方法比 较好的实现了产品的创建, 但是在以前的分析中我们并没有考虑产品种类及生产 厂家这样的问题。 就拿 DVD来说 TCL可以生产、 LG也生产等等很多厂家都生产。 DVD 是产品种类中的一种, 产品种类这个概念在有些书上称为产品族。 从另外一 个角度来看 TCL可以生产 DVD、VCD等等很多产品, 这些产品在一起就可以构成 一个产品结构。当我们考虑了这些问题后,提出了两个概

22、念:产品种类、产品结 构。我们在工厂方法中讨论的是一个个单一的产品的创建, 如果我们对这个问题 进行进一步的研究、 拓展, 就应该从单一的产品过度到多个产品种类, 在工厂方 法中我们考虑 DVD 是一个单一的产品,现在我们认为 DVD 是一个产品种类,有 TCL生产的 DVD,有 LG生产的 DVD,VCD是另一个产品种类,有 TCL生产的 VCD, 有 LG 生产的 VCD。就这个问题我们重新分析, 有两个产品种类分别是 DVD、VCD, 有两个工厂是 TCL和 LG,它们分别生产 DVD 和 VCD。我们使用下面的类图来表 示:DVD 是抽象类它提供统一的接口, LGDVD、 TCLDVD

23、是两个具体的类。 VCD 和 DVD 类似。有一个抽象的工厂 Create,从它派生了两个具体的类 TCLCreate、 LGCreate。Create中提供了两个抽象方法 factoryDVD和 factoryVCD它们提供了两 个接口,用于创建 DVD 产品和 VCD产品。在 TCLCreate、LGCreate中实现这两个 方法。这样 TCLCreate就可以创建自己的 DVD、VCD,同样 LGCreate也可以传经 自己的产品。面是代码结构:public abstract class Create public abstract DVD factoryDVD();public abs

24、tract VCD factoryVCD();public class LGCreate: Create public override DVD factoryDVD() return new LGDVD();public override VCD factoryVCD() return new LGVCD();public class TCLCreate: Create public override DVD factoryDVD() return new TCLDVD();public override VCD factoryVCD() return new TCLVCD();public

25、 abstract class DVD public abstract string PlayVideo();public class LGDVD: DVD public override string PlayVideo() return "LG 的 DVD 在播放 "public class TCLDVD: DVD public override string PlayVideo() return "TCL 的 DVD 正在播放 "public abstract class VCD public abstract string PlayVideo();public class LGVCD: VCD public override string PlayVideo() return "LG 的 VCD正在播放 "public class TCLVCD: VCD public override string PlayVideo() return "TCL的 VCD 正在播放 "客户端使用抽象工厂代码如下:private void button1_Click(object sender, e)Create TCL,LG;TCL=new

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论