设计模式之工厂方法(三)
前面一篇已经说了简单工厂,那就趁热打铁,开始讲述工厂方法吧。
一、引出模式
开发场景:实现一个导出数据的应用框架,让客户来选择导出数据的方式,并执行真正的数据导出。
导出数据的方式可以是导出到文本文件,导出到XML,导出到Excel,导出到数据库等。
好了,我们大概来想一下思路,对于上面的应用框架,导出来的都是一个数据文件,系统并不知道导出的是哪种文件,所以我们可以定义一个统一的导出数据的接口(IDataExport),对于实现导出数据的业务功能对象来说(Client),它应该根据需求来创建相应的IDataExport的实现对象,可是对于实现导出数据的业务功能对象(Client)而已,它并不知道应该创建哪一个IDataExport的实现对象,也不知道如何创建。
也就是说,客户端是需要创建IDataExport具体的实例对象的,但是客户端只知道IDataExport接口,并不知道具体的实现,那怎么办呢?
二、认识模式
1.模式定义
定义一个创建对象的接口,让子类决定去实例化哪个类,工厂方法使一个类的实例延迟到子类。
2.解决思路
在实现数据导出业务对象(Client)里,我们根本不知道该使用哪种导出方式,因此这个对象就不能和具体数据导出对象耦合在一起,它需面对数据导出的接口,但是接口不能直接使用的,需要用到具体的实现对象的实例。
这样不就矛盾了吗?那怎么办?
说白了,客户端不就是需要一个具体实现类的对象吗,那好我就用个方法来帮你创建这个实例对象,这个方法自己也不知道它将被用来干嘛,我们可以将这个方法定义成抽象方法,它就当成摆设吧,具体的创建实例的工作就交给它的子类吧,那么这个对象本身就可以只是面对接口编程,而无需关系具体类的创建工作。
3.模式结构
IDataExport:定义工厂方法所创建的对象的接口,也就是client需要使用的对象的接口。
ExportToTxt:具体的IDataExport接口的实现对象。
IFactoryMethod:创建器,声明工厂方法,工厂方法通常会返回一个IDataExport类型的实例对象,里面大多是抽象方法。也可以提供默认实现。
CreatForTxt:具体的创建器对象,实现IFactoryMethod定义的工厂方法,返回具体的IFactoryMethod实例。
4.示例代码
class Program { static void Main(string[] args) { //最关键一步,改变了new的对象,也就改变了具体的实现 IFactoryMethod factory = new CreateForSql(); IDataExport export = factory.Create(); export.Export(); Console.ReadKey(); } } #region 工厂 public interface IFactoryMethod { IDataExport Create(); } public class CreateForTxt : IFactoryMethod { public IDataExport Create() { return new ExportToTxt(); } } public class CreateForSql : IFactoryMethod { public IDataExport Create() { return new ExportToSql(); } } public class CreateForXml : IFactoryMethod { public IDataExport Create() { return new ExportToXml(); } } #endregion #region 产品 public interface IDataExport { void Export(); } public class ExportToTxt : IDataExport { public ExportToTxt() { } public void Export() { Console.WriteLine("数据导出到txt"); } } public class ExportToSql : IDataExport { public void Export() { Console.WriteLine("数据导出到数据库"); } } public class ExportToXml : IDataExport { public void Export() { Console.WriteLine("数据导出到xml"); } } #endregion
三、理解模式
1.模式功能
工厂方法模式主要功能是让父类不知道具体实现情况下,完成自身的功能调用;而具体的实现则延迟到子类来实现。
2.谁来使用工厂方法创建对象
事实上,在工厂方法模式里面,应该是IFactoryMethod中的其他方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给IFactoryMethod外部使用,但工厂方法的本意,是由IFactoryMethod对象内部的方法来使用工厂方法创建的对象,也就是说,工厂方法一般不提供给IFactoryMethod外部使用。
1)客户端使用IFactoryMethod
对象的情况
客户端:
class Program { static void Main(string[] args) { #region 客户端使用Creator对象(推荐) IFactoryMethod factory = new CreateForSql(); factory.export(); #endregion Console.ReadKey(); } }
抽象工厂类
public abstract class IFactoryMethod { /// <summary> /// 抽象方法 /// </summary> /// <returns></returns> public abstract IDataExport Create(); /// <summary> /// 依赖注入 setter方法 /// </summary> public void export() { IDataExport export = Create(); export.Export(); } }
2)客户端使用由IFactoryMethod
创建出来的对象
这比较好理解,示例代码用的就是这种情况
客户端
class Program { static void Main(string[] args) { #region 客户端使用Creator创建的对象(不推荐) IFactoryMethod factory = new CreateForSql(); IDataExport export = factory.Create(); export.Export(); #endregion Console.ReadKey(); } }
抽象工厂类
public interface IFactoryMethod { IDataExport Create(); }
小结:在工厂方法模式中,客户端要么使用IFactoryMethod对象,要么使用IFactoryMethod创建的对象,一般客户端不直接使用工厂方法。当然也可以直接把工厂方法暴露给客户端操作,但是一般不这么做。
3.模式优点
1)
可以在不知具体实现的情况下编程
工厂方法模式可以让你在实现功能时,如果需要某个产品对象,只需要使用产品的接口即可,而无需关系具体的实现。选择具体的实现的任务延迟到子类去完成。
2)
更容易扩展对象的新版本
只需要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。
3) 连接平行的类层次结构
4.模式缺点
具体产品对象和工厂方法的耦合
5.模式本质
工厂方法模式的本质就是延迟到子类来选择实现。
与简单工厂的区别:从本质上讲,在具体实现上都是“选择实现”,但是简单工厂是直接在工厂类中进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面的工厂方法是依赖于抽象而不是具体的实现。
6.模式选择
1)
如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
2) 如果一个类本身希望由它的子类来创建所需的对象的时候,应该使用工厂方法模式。