设计模式——工厂方法模式
来源:互联网 发布:小译软件 编辑:程序博客网 时间:2024/05/07 05:30
最近在看《软件设计精要与模式(第2版)》,先从第6章《.NET中的工厂方法模式》说起。
在这一章中,作者先举了一个例子,就是Car和Engine的关系,并以此说明了为何要引入工厂方法模式,对于这个例子,只是引出问题,然后就草草收场了。接着对.NET中WebRequest对象的创建过程中的工厂模式进行了详细的剖析,最后说明了“惯例优于模式”。
现在说说具体的问题引入,比如我有一个Engine的类,派生出SolarEngine和GaslineEngine分别表示太阳能发动机和汽油发动机,这些发动机应用到Car这个类上面。Car这个类不用太关注,关键在与Engine类上面。
假设我有几个模块,其中用到了Engine下面的具体的类。比如A模块中有一个创建对象的语句
Engineengine=new SolarEngine();
B模块中也有
Engineengine=new SolarEngine();
或者C、D、E…很多模块都是这么写的。
现在突然有要求,把上面的所有SolarEngine类用GaslineEngine类来代替,这样的话,就不得不把所有的模块中的上面这句话替换为
Engine engine= new GaslineEngine();
也就是说,一个需求变了,结果所有的模块的代码都要改写。
因此,现在希望的结果就是这些模块都不改动,它们会根据其他模块(比如只需更改一个模块)就会自动地创建出GaslineEngine对象出来;更进一步地,当我有新的Engine出现,比如核发动机(NuclearEngine)吧,这些模块不用任何更改就能自动创建NuclearEngine对象。
然后作者说明了用工厂模式可以解决这个问题,但只是一笔带过,直接转到对.NET设计中的工厂模式的剖析去了。
实际上,这里提出的问题,跟后面.NET中WebRequest的例子还是不同的,为什么这么说?
在这些A,B,C….模块里,我们最希望看到的就一句话
Engineengine=Engine.Create(); //Create是Engine的静态方法
就能创建需要实际需要的对象(不管你是GaslineEngine,还是SolarEngine,还是以后的NuclearEngine)。也就是说在A,B,C…这些模块里根本看不到诸如gasline,solar这样的字眼。
而后面.NET的例子跟这个Engine的例子还是有点不一样的,它不是说在什么模块里面,而是当我需要创建的时候,希望用WebRequest.Create(“http://”)就创建HttpWebRequest对象;用WebRequest.Create(“file://”)就创建FileWebRequest对象。如果要套到上面的例子里面,那应该是Engine.Create(“solar”),Engine.Create(“gasline”)…;也就是说,在这些模块里面还是出现了solar这样的信息性字眼。那如果要改成其它的,岂不是所有模块都要改成Engine.Create(“gasline”)?还是不完美吧。
下面通过逐渐完善这个例子来说一下我的理解吧(代码尽量简单点,如果看后面那个.NET例子,有很多代码干扰较大)。分为以下三个部分:
1.实现Engine.Create(“solar”)创建SolarEngine实例
2.通过Engine.Create(),不需要更改已经写好的模块自动创建所需对象
3.“惯例优于模式”
先来看代码:
namespace ConsoleApplication1{ class Class1 { [STAThread] static void Main(string[] args) { Engine engine = Engine.Create("solar"); engine.StartEngine(); Engine engine2 = Engine.Create("gasline"); engine2.StartEngine(); } } public class Engine { public virtual void StartEngine(){} public static Engine Create(string name) { Engine ret = null; //这一部分明显的很业余 if (name == "solar") ret = new SolarEngine(); else if (name == "gasline") ret = new GaslineEngine(); return ret; } } public class SolarEngine : Engine { public override void StartEngine() { Console.WriteLine("Solar Engine Start..."); } } public class GaslineEngine : Engine { public override void StartEngine() { Console.WriteLine("Gasline Engine Start..."); } } }
在这个例子中,实现了我们所说的Engine.Create(“solar”);创建SolarEngine对象,Engine.Create("gasline");创建GaslineEngine对象,而且没有用到工厂模式,如果要加入新的NuclearEngine,只需要从Engine派生一个NuclearEngine类,然后在Engine的静态方法Create里面再加一个”nuclear”判断就可以了。
上面这个就是简单工厂模式,但是这样做,对于一个追求完美的设计者来说,明显地很业余。因为你既然提供了Engine基类,可能就是开放的,如果别人派生出一个其它的新类,比如WaterEngine,你根本就不知道,而且别人还需要知道Create的内部实现,并且去更改这部分代码,岂不是很业余?
所以,这个地方我们放弃使用if…else或者switch这样的判断语句,引入一个ArrayList,保存”solar”,”gasline”,这样的关键词,并且当有人派生出NuclearEngine,WaterEngine这样的发动机时,可以修改这个ArrayList,加入”nuclear”,”engine”这样的关键词。
问题是,然后呢?怎么根据这些关键词创建具体的对象呢?
方法一:采用反射技术
namespace ConsoleApplication1{ class Class1 { [STAThread] static void Main(string[] args) {//注册Engine,如果需要添加新的Engine,只需要//Engine.RegisterNewEngine(“nuclear”,typeof(NuclearEngine)) Engine.RegisterNewEngine("gasline", typeof(GaslineEngine)); Engine.RegisterNewEngine("solar", typeof(SolarEngine)); Engine engine = Engine.Create("solar"); engine.StartEngine(); Engine engine2 = Engine.Create("gasline"); engine2.StartEngine(); } }//建立name与类型之间的隐射关系 public class EngineMaps { public string enginename = string.Empty; public Type enginetype = null; public EngineMaps(string _enginename, Type _enginetype) { enginename = _enginename; enginetype = _enginetype; } } public class Engine{//采用静态ArrayList来代替前面的if..else结构 public static ArrayList enginelist = new ArrayList();//如果需要添加新的Engine,只需要注册即可 public static void RegisterNewEngine(string _name, Type _type) { enginelist.Add(new EngineMaps(_name, _type)); } public virtual void StartEngine() { } public static Engine Create(string name) { Engine ret = null; Type t = null; foreach (EngineMaps map in enginelist) { if (string.Compare(map.enginename, name) == 0) { t = map.enginetype; break; } } if (t != null) { ret =(Engine)Activator.CreateInstance(t); } return ret; } } public class SolarEngine : Engine { public override void StartEngine() { Console.WriteLine("Solar Engine Start..."); } } public class GaslineEngine : Engine { public override void StartEngine() { Console.WriteLine("Gasline Engine Start..."); } }}
方法二:在没有反射技术的时候,采用工厂模式
这个也是在这本书的第一版剖析.NET的例子的时候应用的方法。
首先为每一个需要创建的Engine设置工厂类(GaslineEngineFactory,SolarEngineFactory),用于创建具体的实例,并且设置一个共同的接口IEngineFacory,与基类Engine对应。
namespace ConsoleApplication1{ class Class1 { [STAThread] static void Main(string[] args) { Engine.RegisterNewEngine("gasline",new GaslineEngineFactory()); Engine.RegisterNewEngine("solar", new SolarEngineFactory()); Engine engine = Engine.Create("solar"); engine.StartEngine(); Engine engine2 = Engine.Create("gasline"); engine2.StartEngine(); } } public class EngineMaps { //建立name与creator的隐射关系,这个地方用IEngineFactory统一了工厂类,如果不用工厂,这个地方传Engine那就无法创建具体的对象了,工厂模式主要就是在这个地方把具体的对象创建工作抽象为一个IEngineFactory了,掩盖了具体的类的名称。 public string enginename = string.Empty; public IEngineFactory creator; public EngineMaps(string _enginename, IEngineFactory _enginecreator) { enginename = _enginename; creator = _enginecreator; } public IEngineFactory Creator { get { return this.creator; } set { creator = value; } } } public class Engine { public static ArrayList enginelist = new ArrayList(); public static void RegisterNewEngine(string _name, IEngineFactory _creator) { enginelist.Add(new EngineMaps(_name, _creator)); } public virtual void StartEngine() { } public static Engine Create(string name) { Engine ret = null; foreach (EngineMaps map in enginelist) { if (string.Compare(map.enginename, name) == 0) { ret = map.Creator.MakeEngine(); break; } } return ret; } } public class SolarEngine : Engine { public override void StartEngine() { Console.WriteLine("Solar Engine Start..."); } } public class GaslineEngine : Engine { public override void StartEngine() { Console.WriteLine("Gasline Engine Start..."); } } public interface IEngineFactory { Engine MakeEngine(); } //太阳能发动机工厂创建太阳能发动机的实例 public class SolarEngineFactory : IEngineFactory { internal SolarEngineFactory() { } public Engine MakeEngine() { Console.WriteLine("Create Solar Engine..."); return new SolarEngine(); } } public class GaslineEngineFactory : IEngineFactory { internal GaslineEngineFactory() { } public Engine MakeEngine() { Console.WriteLine("Create Gasline Engine..."); return new GaslineEngine(); } }}
在采用工厂模式的情况下,如果需要扩展其他的Engine,需要
1)-------派生自Engine
public class NuclearEngine : Engine { public override void StartEngine() { Console.WriteLine("Nuclear Engine Start..."); } }
2)---------添加一个对应的EngineFactory,并实现IEngineFactory接口
public class NuclearEngineFactory : IEngineFactory { internal NuclearEngineFactory() { } public Engine MakeEngine() { return new NuclearEngine(); } }
3)--------通过Engine.RegisterNewEngine进行注册
Engine.RegisterNewEngine("nuclear", new NuclearEngineFactory());
4)-----具体调用过程
Engine engine3 = Engine.Create("nuclear");engine3.StartEngine();
好了,如果前面的都看懂了,那么第2部分“通过Engine.Create(),不需要更改已经写好的模块自动创建所需对象”就容易实现多了,主要就是将需要创建的具体的对象的信息保存在一个static变量里面就可以了。
staticstring Defaultenginename=”solar”;
这样A,B,C…这些模块不用更改,只需要更改
Engine.Defaultenginename=”gasline”;
或者static Type Defaultenginetype;
Engine.Defaultenginetype=typeof(GaslineEngine);
或者 static IEngineFactory DefaultFactory;
Engine.DefaultFactory= new GaslineEngineFactory();
然后定义一个不带参数的Create(),里面根据前面的静态设置实现就行了。
采用上述方法,就能够实现以后的A,B,C…模块不用做任何改动。如果需要更改具体类,只需要采用静态字段设置(调用的方式,而不是去更改已有模块的内部实现);或者如果需要加入新的Engine,也只是需要定义新类,调用RegisterNewEngine注册就可以了。
Ok,下面来说第三部分——“惯例优于模式”
这个主要是在使用反射进行创建的时候使用,我们在前面的“方法一”的例子当中,定义了一个EngineMaps来表示名称和类的Type之间的隐射关系,而获得Type可以用一个字符串(类名)来得到。这样只要我们在设计时统一规范,那么就能够省掉这个EngineMaps类。比如说表示一个新的Engine类,其必须在System.EngineCenter这个命名空间下面,且类名为
发动机的动力源+”Engine”,创建的时候,Create的参数名为 发动机的动力源(注意大小写)
下面来看具体的代码
using System;using System.Collections;using System.Reflection;namespace ConsoleApplication1{ class Class1 { [STAThread] static void Main(string[] args) {//注意大小写,要让参数能转化为//System.EngineCenter.SolarEngine System.EngineCenter.Engine engine = System.EngineCenter.Engine.Create("Solar"); engine.StartEngine(); System.EngineCenter.Engine engine2 = System.EngineCenter.Engine.Create("Gasline"); engine2.StartEngine(); } }}//定义一个新的命名空间namespace System.EngineCenter{ public class Engine { public virtual void StartEngine() { } public static Engine Create(string name) { Engine ret = null; Type t = null; //注意这个参数 t = Type.GetType("System.EngineCenter." + name+"Engine"); if (t != null) { ret = (Engine)Activator.CreateInstance(t); } return ret; } } public class SolarEngine : Engine { public override void StartEngine() { Console.WriteLine("Solar Engine Start..."); } } public class GaslineEngine : Engine { public override void StartEngine() { Console.WriteLine("Gasline Engine Start..."); } }}
既然是字符串决定的,那么就可以把这个字符串定义到配置文件当中,这样代码不需要任何更改和重新编译,只需要更改配置文件,就可以根据需要创建所需要的对象了。
- 设计模式—工厂方法
- 设计模式—工厂方法
- 设计模式——抽象工厂模式、工厂方法模式
- Java设计模式—工厂方法模式&抽象工厂模式
- Java设计模式—工厂方法模式&抽象工厂模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- 设计模式——工厂方法模式
- Android日记之2012/01/18
- 谷歌背后的数学
- 《java编程思想》之垃圾回收器如何工作
- 替换文件中字符串的bat脚本
- Webkit文本资源编码选择
- 设计模式——工厂方法模式
- 一维最接近点对的分治解法 .
- POJ1157 LITTLE SHOP OF FLOWERS DP
- weblogic EJB客户端使用NativeIO
- Poj 1163 The Triangle
- 恐怖绝伦,SOPA和PIPA捅了网站窝!
- C++ string类assign用法
- 为不带Lib的DLL制作Lib
- linux进程编程