使用工厂方法模式来解决问题
定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method 使一个类的实例化延迟到其子类。
应用工厂方法式来解决问题的思路
工厂方法模式的解决思路很有意思,那就是不解决,采取无为而治的方式:不是需要接口对象吗,那就定义一个方法来创建;可是事实上它自己是不知道如何创建这个接口对象的,没有关系,定义成抽象方法就可以了,自己实现不了,那就让子类来实现,这样这个对象本身就可以只是面向接口编程,而无需关心到底如何创建接口对象了。
工厂方法模式的结构和说明
- Product: 定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。
- ConcreteProduct:具体的 Product 接口的实现对象。
- Creator:创建器,声明工厂方法,工厂方法通常会返回一个 Product 类型的实例对象,而且多是抽象方法。也可以在 Creator 里面提供工厂方法的默认实现,让工方法返回一个缺省的 Product 类型的实例对象。
- ConcreteCreator:具体的创建器对象,覆盖实现 Creator 定义的工厂方法,返回具体的 Product实例。
示例代码
namespace NetCore3Console.FactoryMethod
{
/// <summary>
/// 工厂方法所创建的对象的接口
/// </summary>
public interface IProduct
{
//可以定义Product的属性和方法
}
/// <summary>
/// 具体的Product对象
/// </summary>
public class ConcreteProduct : IProduct
{
//实现Product要求的方法
}
/// <summary>
/// 创建器,生命工厂方法
/// </summary>
public abstract class Creator
{
/// <summary>
/// 创建Product的工厂方法
/// </summary>
/// <returns></returns>
protected abstract IProduct FactoryMethod();
public void SomeOperation()
{
//通常在这些方法实现中需要调用工厂方法来获取Product对象
IProduct product = FactoryMethod();
}
}
public class ConcreteCreator : Creator
{
protected override IProduct FactoryMethod()
{
//重新定义工厂方法,返回一个具体的Product对象
return new ConcreteProduct();
}
}
}
认识工厂方法模式
工厂方法模式的功能
工厂方法模式的主要功能是让父类在不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现。
这样在设计的时候,不用去考虑具体的实现,需要某个对象,把它通过工厂方法返回就好了,在使用这些对象实现功能的时候还是通过接口来操作,这类似于 IOC/DI的思想。
实现成抽象类
工厂方法的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法这些抽象方法就是工厂方法。
父类里面,通常会有使用这些产品对象来实现一定的功能的方法,而且这些方法所实现的功能通常都是公共的功能,不管子类选择了何种具体的产品实现,这些方法的功能总是能正确执行。(也就是说抽象类实现公共功能)
实现成具体的类
也可以把父类实现成为一个具体的类。这种情况下,通常是在父类中提供获取所需对象的默认实现方法,这样即使没有具体的子类,也能够运行。
通常这种情况还是需要具体的子类来决定具体要如何创建父类所需要的对象。也把这种情况称为工厂方法为子类提供了挂钩。通过工厂方法,可以让子类对象来覆盖父类的实现,从而提供更好的灵活性。
工厂方法的参数和返回
工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现。也就是说通过在抽象方法里面传递参数,在子类实现的时候根据参数进行选择,看看究竟应该创建哪一个具体的实现对象。
一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体的类的实例。
谁来使用工厂方法创建的对象
在工厂方法模式里面,应该是 Creator 中的其他方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给 Creator 外部使用,但工厂方法模式的本意,是由 Creator 对象内部的方法来使用工厂方法创建的对象,也就是说,工厂方法一般不提供给 Creator 外部使用。
客户端应该使用 Creator 对象,或者是使用由 Creator 创建出来的对象。对于客户端使用 Creator 对象,这个时候工厂方法创建的对象,是 Creator 中的某些方法使用,对于使用那些由 Creator 创建出来的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。
小结一下:在工厂方法模式里面,客户端要么使用 Creator 对象,要么使用 Creator创建的对象,一般客户端不直接使用工厂方法。当然也可以直接把工厂方法暴露给客户端操作,但是一般不这么做。
平行的类层次结构
平行的类层次结构的含义
简单点说,假如有两个类层次结构,其中一个类层次中的每个类在另一个类层次中都有一个对应的类的结构,就被称为平行的类层次结构。
参数化工厂方法(不再用抽象方法)
所谓参数化工厂方法指的就是:通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象,这种情况就被称为参数化工厂方法。当然工厂方法创建的不同的产品必须是同一个 Product 类型的。
public interface IExportFile
{
bool Export(string data);
}
public class ExportOperate
{
/// <summary>
/// 导出文件
/// </summary>
/// <param name="type"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool Export(int type, string data)
{
//使用工厂方法
IExportFile api = FactoryMethod(type);
return api.Export(data);
}
protected IExportFile FactoryMethod(int type)
{
IExportFile api = null;
//根据类型选择创建那种对象
if (type == 1)
api = new ExportTxtFile();
else if (type == 2)
api = new ExportDB();
return api;
}
}
思考工厂方法模式
本质
工厂方法模式的本质:延迟到子类来选择实现。
对设计原则的体现
工厂方法模式很好地体现了“依赖倒置原则”。
依赖倒置原则告诉我们“要依赖抽象,不要依赖于具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。
何时选择工厂方法模式
- 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
- 如果一个类本身就希望由它的子类来创建所需的对象的时候,应该使用工厂方法模式。
抽象工厂模式和DAO
,采用抽象工厂模式来实现 DAO 的时候,DAOFactory就相当于抽象工厂,里面定义一系列创建相关对象的方法,分别是创建订单主记录的DAO 对象和创建订单子记录的 DAO 对象此时 OrderMainDAO 和 OrderDetailDAO 就相当于被创建的产品,RdbDAOFactory 和XmIDAOFactory 就相当于抽象工厂的具体实现在它们里面会选择相应的具体的产品实现来创建对象。
namespace NetCore3Console.AbstractMethod
{
/// <summary>
/// 抽象工厂,创建订单主、自己录对应的DAO对象
/// </summary>
public abstract class DaoFactory
{
/// <summary>
/// 创建订单主记录对应的DAO对象
/// </summary>
/// <returns></returns>
public abstract IOrderMainDao CreateOrderMainDao();
/// <summary>
/// 创建订单子记录对应的DAO对象
/// </summary>
/// <returns></returns>
public abstract IOrderDetailDao CreateOrderDetailDao();
}
/// <summary>
/// 订单主记录的DAO操作接口
/// </summary>
public interface IOrderMainDao
{
/// <summary>
/// 示意方法,保存订单主记录
/// </summary>
void SaveOrderMain();
}
public interface IOrderDetailDao
{
/// <summary>
/// 示意方法,保存订单子记录
/// </summary>
void SaveOrderDetail();
}
/// <summary>
/// 实现主记录、自己录Dao
/// </summary>
public class RdbMainDao : IOrderMainDao
{
public void SaveOrderMain()
{
throw new System.NotImplementedException();
}
}
public class RdbDetailDao : IOrderDetailDao
{
public void SaveOrderDetail()
{
throw new System.NotImplementedException();
}
}
public class RdbDaoFactory : DaoFactory
{
public override IOrderMainDao CreateOrderMainDao()
{
return new RdbMainDao();
}
public override IOrderDetailDao CreateOrderDetailDao()
{
return new RdbDetailDao();
}
}
}
抽象工厂模式的优点
- 分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。 - 使得切换产品簇变得容易
因为一个具体的工厂实现代表的是一个产品簇。客户端选用不同的工厂实现,就相当于是在切换不同的产品簇。
思考抽象工厂模式
抽象工厂模式的本质:选择产品簇的实现。
抽象工厂模式的本质
工厂方法是选择单个产品的实现,虽然一个类里面可以有多个工厂方法,但是这些方法之间一般是没有联系的,即使看起来像有联系。
但是抽象工厂着重的就是为一个产品簇选择实现,定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分或者是相互依赖的。如果抽象工厂里面只定义一个方法,直接创建产品,那么就退化成为工厂方法了。
何时选择抽象工厂模式
- 如果希望一个系统独立于它的产品的创建、组合和表示的时候。换句话说,希望一个系统只是知道产品的接口,而不关心实现的时候。
- 如果一个系统要由多个产品系列中的一个来配置的时候。换句话说,就是可以动态地切换产品簇的时候。
- 如果要强调一系列相关产品的接口,以便联合使用它们的时候。