一、基础概念
工厂方法模式的本质是【延迟到子类来选择实现】;
工厂方法模式的定义: 定义一个用于创建对象的接口,让子类决定实例化哪一个类,FactoryMethod使一个类的实例化延迟到其子类 。
|----|------------------------------------------------------------------------------------------------------------|
| 序号 | 说明 |
| 0 | 工厂方法模式功能: 是指让父类在不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现 |
| 1 | 通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法(注意:子类在实现这些抽象方法的时候,通常并不是真正地由子类来实现具体的功能, 而是在子类的方法里面做选择,选择具体的产品实现对象) |
| 2 | 实现成具体的类(通常情况下需要具体的子类来决定要如何创建父类所需要的对象; 通过工厂方法,可以让子类对象来覆盖父类的实现,从而提供更好的灵活性) |
| 3 | 工厂方法的参数和返回(工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现; 一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体类的实例) |
| 4 | 谁来使用工厂方法创建对象(应该是Creator中的其他方法在使用工厂方法创建对象;客户端在使用Creator对象) |
[工厂方法模式的功能]
|----|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| 序号 | 说明 |
| 1 | 参与者都有谁?(一般有三方参与者:一个是某个对象(任意的普通对象);另一个是IOC/DI的容器(指用来实现IOC/DI功能的一个框架程序);最后一个是某个对象的外部资源) |
| 2 | **谁依赖于谁:**当然是某个对象依赖于IOC/DI的容器 |
| 3 | **为什么需要依赖:**对象需要IOC/DI的容器来提供对象需要的外部资源 |
| 4 | **谁注入于谁:**IOC/DI的容器注入控制对象; |
| 5 | **到底注入什么:**注入某个对象所需要的外部资源 |
| 6 | **谁控制谁:**当然是IOC/DI的容器来控制对象了 |
| 7 | **控制什么:**主要是控制对象实例的创建 |
| 8 | 为何叫反转:反转是相对于正向而言的,那什么算是正向的呢?(常规情况下的应用程序: 如果A里面要使用C,你会怎么做?当然是直接创建C的对象【这种情况就被称为正向的】; 【那什么是反向呢?就是A类中不再主动获取C,而是被动的等待,等待IOC/DI的容器获取一个C的实例, 然后反向地注入到A类中】) |
| 9 | 依赖注入和控制反转是同一概念吗? (依赖注入和控制反转是对同一件事情的不同描述; 【依赖注入】是从应用程序的角度描述,即【应用程序依赖容器创建并注入它所需要的外部资源】; 【控制反转】是从容器的角度描述,即【容器控制应用程序,由容器反向地向应用程序注入其所需的外部资源】) |
| 10 | 工厂方法与IOC/DI的关系【工厂方法与IOC/DI的思想是相似的,都是主动变被动,进行了主从换位,从而获得了更灵活的程序结构】 |
[如何理解IOC/DI【IOC---InversionofControl 控制反转】【DI---Dependency Injection 依赖注入】]
|----|----------------------------------------------------------------|--------------------------------------------------------------|
| 序号 | 工厂模式的优点 | 工厂模式的缺点 |
| 1 | 可以在不知具体实现的情况下编程 (若需要某个产品对象,只需要使用接口即可,无需关心具体实现,具体实现延迟到子类完成) | 具体产品对象与工厂方法的耦合性(在工厂方法模式中,工厂方法是需要创建具体产品对象的实例,因此具体产品对象与工厂方法耦合) |
| 2 | 更容易扩展对象的新版本 (工厂方法给子类提供了一个挂钩(钩子方法hook),使得扩展新对象版本变得非常容易) | 具体产品对象与工厂方法的耦合性(在工厂方法模式中,工厂方法是需要创建具体产品对象的实例,因此具体产品对象与工厂方法耦合) |
| 3 | 连接平行的类层次 (工厂方法除了可以创造产品对象外,在连接平行的类层次上也容易) | 具体产品对象与工厂方法的耦合性(在工厂方法模式中,工厂方法是需要创建具体产品对象的实例,因此具体产品对象与工厂方法耦合) |
[工厂模式的优缺点]
参数化工厂方法: 指通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象。
何时选用工厂方法模式?
1、如果一个类需要创建某个接口对象,但是又不知道具体的实现,可选择工厂方法模式,把创建对象工作延迟到子类实现;
2、如果一个类本身就希望由它的子类来创建所需对象的时候,就使用工厂方法
二、工厂方法模式示例
业务需求: 实现一个导出数据的应用框架,来让客户选择数据的导出方式,并真正的执行数据导出(通常这种框架系统在导出数据上会有一些约定的方式,如:导出为文本格式、数据库格式、xml格式等)。
需求知识点分析:
1、框架是什么? (框架是能完成一定功能的半成品软件)
2、框架能做什么? (能完成一定的功能,加快应用开发进度;给我们一个精良的程序架构)
3、对框架的理解(基于框架的开发,事情还是那些事情,这是看谁做的问题;虽然基于框架开发, 可以不去做框架所做的事情,但是应该明白框架在做什么,以及框架是如何实现相应功能的);
4、框架和设计模式的关系:
《1》设计模式比框架更抽象(框架是已经实现出来的软件了,虽是半成品,但是已经实现了;但设计模式的重心是在解决问题的方案上,即思想层面);
《2》设计模式是比框架更小的体系结构元素(框架的目标很明确,就是要解决某个领域的某些问题,是很具体的功能;不同领域实现出来的框架不一样);
《3》框架比设计模式更加特例化(框架总是针对特定领域; 设计模式更加注重从思想是哪个、方法上解决问题,更加通用化);
问题分析:
1、需要实现导出数据的应用框架(即不管用户选择什么样的导出格式,最后打出的都是对应的文件;且系统并不知道究竟要导出成为什么样的文件;因此应该有一个统一的接口来描述系统最后生成的对象, 并操作输出的文件) 【所以第一步就是设计一个导出文件的接口并定义导出方法】 。
2、对于实现导出数据的业务功能对象,它应该根据需要来创建相应的导出接口实现对象,因此特定的导出实现与具体的业务相关的;但是对于实现导出数据的业务功能对象来说,它并不知道应该创建哪一个导出接口对象, 也不知道如何创建【即:对于实现导出数据业务的对象,它需要创建导出接口的具体对象,但它只知道导出接口却不知道其具体的实现,此时应该怎么办?】。
2.1、典型的工厂方法示例
1、定义一个导出内容的接口
cs
/***
* Title:"设计模式" 项目
* 主题:工厂方法
* Description:
* 业务需求:实现一个导出数据的应用框架,来让客户选择数据的导出方式,并真正的执行数据导出
* (通常这种框架系统在导出数据上会有一些约定的方式,如:导出为文本格式、数据库格式、xml格式等)
*
* 需求知识点分析:
* 1、框架是什么?(框架是能完成一定功能的半成品软件)
* 2、框架能做什么?(能完成一定的功能,加快应用开发进度;给我们一个精良的程序架构)
* 3、对框架的理解(基于框架的开发,事情还是那些事情,这是看谁做的问题;虽然基于框架开发,
* 可以不去做框架所做的事情,但是应该明白框架在做什么,以及框架是如何实现相应功能的)
*
* 4、框架和设计模式的关系:
* 《1》设计模式比框架更抽象(框架是已经实现出来的软件了,虽是半成品,但是已经实现了;但设计模式的重心是在解决问题的方案上,即思想层面)
* 《2》设计模式是比框架更小的体系结构元素(框架的目标很明确,就是要解决某个领域的某些问题,是很具体的功能;不同领域实现出来的框架不一样)
* 《3》框架比设计模式更加特例化(框架总是针对特定领域; 设计模式更加注重从思想是哪个、方法上解决问题,更加通用化)
*
* 问题分析:
* 1、需要实现导出数据的应用框架(即不管用户选择什么样的导出格式,最后打出的都是对应的文件;
* 且系统并不知道究竟要导出成为什么样的文件;因此应该有一个统一的接口来描述系统最后生成的对象,
* 并操作输出的文件) 【所以第一步就是设计一个导出文件的接口并定义导出方法】
* 2、对于实现导出数据的业务功能对象,它应该根据需要来创建相应的导出接口实现对象,因此特定的导出实现
* 与具体的业务相关的;但是对于实现导出数据的业务功能对象来说,它并不知道应该创建哪一个导出接口对象,
* 也不知道如何创建【即:对于实现导出数据业务的对象,它需要创建导出接口的具体对象,但它只知道导出接口
* 却不知道其具体的实现,此时应该怎么办?】
*
* Date:2025
* Version:0.1版本
* Author:Coffee
* Modify Recoder:
***/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 导出文件对象的接口
/// </summary>
internal interface IExportFile
{
//导出内容为文件
bool Export(string data);
}//Interface_end
}
2、继承导出内容接口实现具体的导出文件类
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 导出为文本格式的对象
/// </summary>
internal class ExportTxtFile : IExportFile
{
public bool Export(string data)
{
//简单示意
Console.WriteLine($"导出【{data}】数据到文本文件");
return true;
}
}//Class_end
}
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 导出为数据库格式的对象
/// </summary>
internal class ExportDB : IExportFile
{
public bool Export(string data)
{
//简单示意
Console.WriteLine($"导出【{data}】数据到数据库中");
return true;
}
}//Class_end
}
3、实现导出操作类
cs
/***
* Title:"设计模式" 项目
* 主题:工厂方法模式
* Description:
* 基础概念:工厂方法模式的本质是【延迟到子类来选择实现】
* 工厂方法模式定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,FactoryMethod使一个类的实例化延迟到其子类
*
* 工厂方法模式的功能:是让父类在不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现;
* 1、通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法
* (注意:子类在实现这些抽象方法的时候,通常并不是真正地由子类来实现具体的功能,
* 而是在子类的方法里面做选择,选择具体的产品实现对象)
* 2、实现成具体的类(通常情况下需要具体的子类来决定要如何创建父类所需要的对象;
* 通过工厂方法,可以让子类对象来覆盖父类的实现,从而提供更好的灵活性)
* 3、工厂方法的参数和返回(工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现;
* 一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体类的实例)
* 4、谁来使用工厂方法创建对象(应该是Creator中的其他方法在使用工厂方法创建对象;客户端在使用Creator对象)
*
* 如何理解IOC/DI?【IOC---InversionofControl 控制反转】【DI---Dependency Injection 依赖注入】
* 1、参与者都有谁?(一般有三方参与者:一个是某个对象(任意的普通对象);另一个是IOC/DI的容器
* (指用来实现IOC/DI功能的一个框架程序);最后一个是某个对象的外部资源)
* 2、谁依赖于谁:当然是某个对象依赖于IOC/DI的容器
* 3、为什么需要依赖:对象需要IOC/DI的容器来提供对象需要的外部资源
* 4、谁注入于谁:IOC/DI的容器注入控制对象;
* 5、到底注入什么:注入某个对象所需要的外部资源
* 6、谁控制谁:当然是IOC/DI的容器来控制对象了
* 7、控制什么:主要是控制对象实例的创建
* 8、为何叫反转:反转是相对于正向而言的,那什么算是正向的呢?(常规情况下的应用程序:
* 如果A里面要使用C,你会怎么做?当然是直接创建C的对象【这种情况就被称为正向的】
* 【那什么是反向呢?就是A类中不再主动获取C,而是被动的等待,等待IOC/DI的容器获取一个C的实例,
* 然后反向地注入到A类中】)
* 9、依赖注入和控制反转是同一概念吗?(依赖注入和控制反转是对同一件事情的不同描述;
* 【依赖注入】是从应用程序的角度描述,即【应用程序依赖容器创建并注入它所需要的外部资源】
* 【控制反转】是从容器的角度描述,即【容器控制应用程序,由容器反向地向应用程序注入其所需的外部资源】)
* 10、工厂方法与IOC/DI的关系【工厂方法与IOC/DI的思想是相似的,都是主动变被动,进行了主从换位,从而获得了更灵活的程序结构】
*
* 参数化工厂方法:指通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象
*
* 工厂方法模式的优点:
* 1、可以在不知具体实现的情况下编程(若需要某个产品对象,只需要使用接口即可,无需关心具体实现,具体实现延迟到子类完成)
* 2、更容易扩展对象的新版本(工厂方法给子类提供了一个挂钩(钩子方法hook),使得扩展新对象版本变得非常容易)
* 3、连接平行的类层次(工厂方法除了可以创造产品对象外,在连接平行的类层次上也容易)
*
* 工厂方法的缺点:
* 1、具体产品对象与工厂方法的耦合性(在工厂方法模式中,工厂方法是需要创建具体产品对象的实例,因此具体产品对象与工厂方法耦合)
*
* 何时选用工厂方法模式:
* 1、如果一个类需要创建某个接口对象,但是又不知道具体的实现,可选择工厂方法模式,把创建对象工作延迟到子类实现;
* 2、如果一个类本身就希望由它的子类来创建所需对象的时候,就使用工厂方法
*
* Date:2025
* Version:0.1版本
* Author:Coffee
* Modify Recoder:
***/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 实现导出数据的业务功能对象
/// </summary>
abstract class ExportOperate
{
//工厂方法,创建导出的接口对象
protected abstract IExportFile FactoryMethod();
//导出文件
public bool Export(string data)
{
//使用工厂方法
IExportFile exportFile = FactoryMethod();
return exportFile.Export(data);
}
}//Class_end
}
4、创建导出操作类的具体实现类
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 具体的创建器实现对象,实现导出成本文本对象
/// </summary>
internal class ExportTxtFileOperate : ExportOperate
{
protected override IExportFile FactoryMethod()
{
//创建导出为文本的对象
return new ExportTxtFile();
}
}//Class_end
}
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 具体的创建器实现对象,实现创建导出数据库对象
/// </summary>
internal class ExportDBOperate : ExportOperate
{
protected override IExportFile FactoryMethod()
{
//创建导出数据库对象
return new ExportDB();
}
}//Class_end
}
5、客户端调用工厂方法测试
cs
namespace FactoryMethodPattern
{
internal class Program
{
static void Main(string[] args)
{
FactoryMethodTest();
Console.ReadLine();
}
private static void FactoryMethodTest()
{
Console.WriteLine("------工厂方法测试------");
//创建需要使用的对象
ExportOperate exportOperate = new ExportTxtFileOperate(); ;
//调用导出方法
exportOperate.Export("测试导出数据到文本文件");
ExportOperate exportOperate2 =new ExportDBOperate();
exportOperate2.Export("测试导出数据到数据库");
}
}//Class_end
}
运行结果如下:

2.2、IOC/DI与工厂方法示例
定义消息通知的接口
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern.IOC_DI_Demo
{
internal interface IC
{
void Notify(string info);
}//Interface_end
}
2.2.1、IOC/DI示例
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern.IOC_DI_Demo
{
/// <summary>
/// 我们需要再类A里面使用C,正常情况下我们是直接创建C的实例使用;
/// 但我们让类A不在主动获取C,而是被动等待IOC/DI的容器获取一个C的实例,在注入到A类中
/// </summary>
internal class ClassA
{
//定义IC,但不实例化创建,等待被注入
private IC c = null;
public ClassA(IC c)
{
//在这里等待IC对象实例的注入
this.c = c;
}
public void Test(string info)
{
//这里需要使用IC,但是并不主动实例化创建IC,直接从外部获取
c.Notify(info);
}
}//Class_end
}
2.2.2、工厂方法实现IOC/DI示例
1、创建具体类实现IC接口
cs
using FactoryMethodPattern.IOC_DI_Demo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern.FactoryMethodDemo
{
/// <summary>
/// 具体实现IC接口的报警类
/// </summary>
internal class Alarm : IC
{
public void Notify(string info)
{
Console.WriteLine($"具体实现接口方法的C1类对象,内容是【{info}】");
}
}//Class_end
}
2、定义抽象方法作为接口操作(也就是工厂方法)
cs
using FactoryMethodPattern.IOC_DI_Demo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern.FactoryMethodDemo
{
abstract class ICOperate
{
//定义一个抽象方法作为工厂
protected abstract IC CreateC();
//这里需要使用C,但是不知道用哪一个,就不主动创建C了,直接使用,然后让子类实现,客户端选择
public void MSGNotify(string info)
{
CreateC().Notify(info);
}
}//Class_end
}
3、继承工厂方法的具体实现操作类
cs
using FactoryMethodPattern.IOC_DI_Demo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern.FactoryMethodDemo
{
internal class AlarmMSGOpearte : ICOperate
{
protected override IC CreateC()
{
//直接在具体的报警消息操作里面使用对应的对象
return new Alarm();
}
}//Class_end
}
4、客户端调用实现IOC/DI示例的测试工厂方法Demo
cs
using FactoryMethodPattern.FactoryMethodDemo;
namespace FactoryMethodPattern
{
internal class Program
{
static void Main(string[] args)
{
FactoryMethodDemoTest();
Console.ReadLine();
}
private static void FactoryMethodDemoTest()
{
//直接使用C操作类实例化了具体的报警消息操作对象类
ICOperate cOperate = new AlarmMSGOpearte();
//调用消息通知方法
cOperate.MSGNotify("测试消息");
}
}//Class_end
}
运行结果如下:

2.3、参数化工厂方法
1、关于接口【IExportFile】与导出文本文件(ExportTxtFile)、数据库文件(ExportDB)内容都一样不变。
2、定义参数化工厂的操作方法
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
internal class ParameterExportOperate
{
/// <summary>
/// 导出文件
/// </summary>
/// <param name="type">对象导出的类型</param>
/// <param name="data">需要保存的数据</param>
/// <returns>true:表示导出成功</returns>
public bool Export(int type,string data)
{
IExportFile exportFile = FactoryMethod(type);
return exportFile.Export(data);
}
/// <summary>
/// 工厂方法,创建导出内容的接口对象
/// </summary>
/// <param name="type">对象导出类型</param>
/// <returns></returns>
protected virtual IExportFile FactoryMethod(int type)
{
IExportFile exportFile = null;
switch (type)
{
case 1:
exportFile = new ExportTxtFile();
break;
case 2:
exportFile = new ExportDB();
break;
default:
break;
}
return exportFile;
}
}//Class_end
}
客户端调用该参数化工厂方法
cs
using FactoryMethodPattern.FactoryMethodDemo;
namespace FactoryMethodPattern
{
internal class Program
{
static void Main(string[] args)
{
ParameterFactoryMethodTest();
Console.ReadLine();
}
private static void ParameterFactoryMethodTest()
{
Console.WriteLine("------参数化工厂方法测试------");
//创建需使用的对象
ParameterExportOperate parameterExportOperate = new ParameterExportOperate();
//调用出书数据的功能方法
parameterExportOperate.Export(1,"测试数据内容");
parameterExportOperate.Export(2, "测试数据内容");
}
}//Class_end
}
运行结果如下:
3、扩展参数化工厂方法十分容易
《1》新增一个导出XML文件的方法
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
/// <summary>
/// 导出为Xml格式的对象
/// </summary>
internal class ExportXml : IExportFile
{
public bool Export(string data)
{
//简单示意
Console.WriteLine($"导出【{data}】数据到XML文件中");
return true;
}
}//Class_end
}
《2》定义一个类来拓展参数化导出操作类
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FactoryMethodPattern
{
internal class ExtandParameterExportOperate:ParameterExportOperate
{
protected override IExportFile FactoryMethod(int type)
{
IExportFile exportFile = null;
//可以全部覆盖重写,也可以直接添加类型实现拓展
if (type == 3)
{
exportFile = new ExportXml();
}
else
{
exportFile=base.FactoryMethod(type);
}
return exportFile;
}
}//Class_end
}
客户端调用该扩展的参数化工厂方法测试
cs
using FactoryMethodPattern.FactoryMethodDemo;
namespace FactoryMethodPattern
{
internal class Program
{
static void Main(string[] args)
{
ParameterFactoryMethodTest();
Console.ReadLine();
}
private static void ParameterFactoryMethodTest()
{
Console.WriteLine("------参数化工厂方法测试------");
//创建需使用的对象
ParameterExportOperate parameterExportOperate = new ParameterExportOperate();
//调用出书数据的功能方法
parameterExportOperate.Export(1,"测试数据内容");
parameterExportOperate.Export(2, "测试数据内容");
Console.WriteLine("\n--使用拓展的方法--");
ParameterExportOperate extandExportOperate = new ExtandParameterExportOperate();
extandExportOperate.Export(1, "测试内容1");
extandExportOperate.Export(2, "测试内容2");
extandExportOperate.Export(3,"测试内容3");
}
}//Class_end
}
运行结果如下:
