Factory Method Pattern 工厂方法模式简介与 C# 示例【创建型】【设计模式来了】
〇、简介
1、什么是工厂方法模式?
一句话解释:
实体类和工厂类均为单独实现,不影响已实现的类,方便扩展。
工厂方法模式(Factory Method Pattern)是一种创建型模式,它允许客户端通过工厂方法来创建对象,而不是直接使用构造函数。这样可以让客户端代码更加灵活,同时保持实现的独立性。工厂方法可以返回同一类的对象,也可以返回不同的类对象。
一个比喻:(班主任与班级)
一个班级对应一个班主任,当插班生超额时,则需要重新组成一个班级,那么此时就需要完成一个班级的各种条件,比如一个新的教室、班主任、班级编号等,当然这个新增的班级对原有的多个班级无影响。
2、优缺点和使用场景
优点:
- 工厂方法模式将对象的创建过程封装到了具体的工厂类中,使得客户端无需知道实际创建的具体对象是哪个类,降低了程序的耦合度。
- 通过定义抽象工厂类和具体工厂类,可以轻松添加新的产品类型,同时不影响现有代码的功能。
- 工厂方法模式可以适应开闭原则,即对扩展开放,对修改关闭。当需要增加新的产品时,只需要添加具体产品类和对应的具体工厂类即可,不必修改现有类的代码。
- 工厂方法模式提高了代码的可测试性,因为可以通过依赖注入的方式来模拟工厂对象并创建所需的产品对象。
缺点:
- 增加复杂性:工厂方法模式引入了额外的抽象层次,使得代码结构变得更加复杂,需要编写更多的代码。
- 在增加新产品类型时,可能需要添加大量的新类和代码,导致系统可维护性降低。
总之,工厂方法模式是一种常用且有效的设计模式,可以提高程序的灵活性和可扩展性,但也需要在实际开发中根据具体情况进行权衡和应用。
使用场景举例:
- 可以用于对象的创建过程比较复杂,需要创建不同类型的对象时,将创建过程封装起来,提高代码的可维护性和灵活性。
- 可以用于不确定对象的类别、个数或者依赖关系的情况。
- 可以用于需要重复使用的对象(例如资源池等)的情况。
- 可以用于测试驱动开发的框架下。
一、工厂方法模式简单实现
如下示例,先定义产品接口 IProduct 和工厂接口 IFactory,再分别进行实现:
#region 定义接口
interface IProduct
{
void Use();
}
// 定义工厂接口
interface IFactory
{
IProduct CreateProduct();
}
#endregion
#region 产品 A
// 实现产品接口的具体产品类
class ConcreteProductA : IProduct
{
public void Use()
{
Console.WriteLine("使用具体产品 A");
}
}
// 实现工厂接口的具体工厂类
class ConcreteFactoryA : IFactory
{
public IProduct CreateProduct()
{
return new ConcreteProductA();
}
}
#endregion
#region 产品 B
class ConcreteProductB : IProduct
{
public void Use()
{
Console.WriteLine("使用具体产品 B");
}
}
class ConcreteFactoryB : IFactory
{
public IProduct CreateProduct()
{
return new ConcreteProductB();
}
}
#endregion
下边进行实际的调用:
// 工厂方法模式
IFactory factoryA = new ConcreteFactoryA();
IProduct productA1 = factoryA.CreateProduct(); // 将对象的创建进行了封装
IProduct productA2 = factoryA.CreateProduct();
productA1.Use();
productA2.Use();
IFactory factoryB = new ConcreteFactoryB();
IProduct productB = factoryB.CreateProduct();
productB.Use();
// 不用工厂方法模式的写法
ConcreteProductA concreteProductA1 = new ConcreteProductA(); // 直接创建对象
concreteProductA1.Use();
ConcreteProductA concreteProductA2 = new ConcreteProductA();
concreteProductA2.Use();
ConcreteProductB concreteProductB = new ConcreteProductB();
concreteProductB.Use();
由以上代码可见:通过工厂方法模式的实现,将对象的创建进行了封装,代码逻辑更清晰易读,当需要新增、删除或修改某个产品时,只需修改具体工厂类即可,既方便又安全。

二、在 .NET 框架中的一个实际应用
依赖注入(Dependency Injection,简称DI)实际上是工厂方法模式的一种变体,通常与工厂方法模式一起使用。依赖注入可以帮助我们实现对象的创建和管理,使得对象的创建过程更加灵活和可控。
下面一个实例,在创建了语言接口 ILanguage、语言工厂抽象类 LanguageFactory 并实现后,又创建了一个 ClientCode 类来封装客户端代码的实现,并使用构造函数将具体工厂类作为参数传递给该类。客户端代码可以通过调用 Speak() 方法来创建相应的产品对象,并执行相关方法。
// 抽象语言类
public interface ILanguage {
void Speak();
}
// 英语类
public class English : ILanguage {
public void Speak() {
Console.WriteLine("Speak English.");
}
}
// 中文类
public class Chinese : ILanguage {
public void Speak() {
Console.WriteLine("说中文。");
}
}
// 工厂方法抽象类
public abstract class LanguageFactory {
public abstract ILanguage CreateLanguage();
}
// 英语工厂
public class EnglishFactory : LanguageFactory {
public override ILanguage CreateLanguage() {
return new English();
}
}
// 中文工厂
public class ChineseFactory : LanguageFactory {
public override ILanguage CreateLanguage() {
return new Chinese();
}
}
// 客户端代码
public class ClientCode {
private readonly LanguageFactory _factory;
public ClientCode(LanguageFactory factory) { // 依赖注入 LanguageFactory
_factory = factory;
}
public void Speak() {
ILanguage language = _factory.CreateLanguage();
language.Speak();
}
}
// 使用示例
static void Main(string[] args)
{
ClientCode code1 = new ClientCode(new EnglishFactory()); // 依赖注入:英语工厂
code1.Speak(); // Speak English.
ClientCode code2 = new ClientCode(new ChineseFactory()); // 依赖注入:中文工厂
code2.Speak(); // 说中文。
}
当然,如果后续有扩展需求,比如再新建一个俄语实现:
(只需增加两个独立的实现:语言接口 ILanguage、语言工厂抽象类 LanguageFactory)
// 俄语类
public class Russian : ILanguage
{
public void Speak()
{
Console.WriteLine("По-русски.");
}
}
// 俄语工厂
public class RussianFactory : LanguageFactory
{
public override ILanguage CreateLanguage()
{
return new Russian();
}
}
然后直接使用,注入俄语工厂new RussianFactory()即可输出俄语,而对原有的实现没有任何影响。
ClientCode code3 = new ClientCode(new RussianFactory()); // 依赖注入:俄语工厂
code3.Speak();
实际输出,见第三行:

实际上在 .net 框架中,还有许多类似的用法,例如:
- 在 ASP.NET MVC 中,可以使用 HtmlHelper 的扩展方法创建各种类型的 HTML 表单控件,如文本框、下拉框等,这些方法内部调用控件工厂来创建相应的控件对象。
- 在 log4net 框架中,可以使用 LogManager 类的 GetLogger() 方法来获取或创建指定名称的记录器对象。
- 使用 ADO.NET 进行数据库操作时,可以使用 DbProviderFactory 类作为通用的提供者工厂类,用来创建特定数据库提供程序的对象。通过调用 DbProviderFactories.GetFactory() 方法就可以获取该工厂类,并利用其创建具体的数据库连接、命令、数据适配器等对象。
方式都是类似的,详情自行查看源码吧。
Factory Method Pattern 工厂方法模式简介与 C# 示例【创建型】【设计模式来了】的更多相关文章
- Factory Method(工厂方法)
Factory Method(工厂方法) 意图: 定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method 使一个类的实例化延迟到其子类. 适用性: 当一个类不知道它所必须创 ...
- Objective-C设计模式——工厂方法模式virtual constructor(对象创建)
工厂方法模式 工厂方法模式可以控制对象的创建过程,屏蔽对象创建的细节,可以直接创建出我们所需要的已经配置好的对象. 工厂方法模式定义了创建方法的接口,让子类决定实例化哪一个类,工厂方法模式使得一个类的 ...
- OOAD-设计模式(三)之创建型设计模式(5种)
前言 前面介绍了OOAD的基础知识,现在我们来详细的说明一下GOF设计模式中的23种模式,希望大家能够学到东西! 一.工厂方法模式(Factory Method) 1.1.工厂方法模式概述 工厂方法模 ...
- 简单工厂,Factory Method(工厂方法)和Abstract Factory(抽象工厂)模式
对于简单工厂来说,它的工厂只能是这个样子的 public class SimplyFactory { /** * 静态工厂方法 */ public static Prouct factory(Str ...
- 设计模式之美:Factory Method(工厂方法)
索引 别名 意图 结构 参与者 适用性 缺点 效果 相关模式 命名约定 实现 实现方式(一):Creator 类是一个抽象类并且不提供它所声明的工厂方法的实现. 实现方式(二):Creator 类是一 ...
- (Factory method)工厂方法设计模式
定义: 1.) 工厂方法模式是用来封装对象的创建,通过让子类来决定创建的对象是什么,来达到将对象创建的过程封装的目的: 2.) 定义了一个创建对象的接口,但由子类决定要实例的泪是哪一个.工厂方法让类把 ...
- C++工厂方法模式讲解和代码示例
在C++中使用模式 使用示例: 工厂方法模式在 C++ 代码中得到了广泛使用. 当你需要在代码中提供高层次的灵活性时, 该模式会非常实用. 识别方法: 工厂方法可通过构建方法来识别, 它会创建具体类的 ...
- 重学 Java 设计模式:实战工厂方法模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!
- .NET设计模式(5):工厂方法模式(Factory Method)
):工厂方法模式(Factory Method) 工厂方法模式(Factory Method) --.NET设计模式系列之五 Terrylee,2004年1月2日 转载:http://terry ...
- java设计模式学习 ----- 工厂方法模式(Factory Method)
工厂方法模式(Factory Method) 工厂方法模式分为三种:普通工厂模式.多个工厂方法模式.静态工厂方法模式 普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 关系图 ...
随机推荐
- Day05笔记
01.数组类(了解) 1.目的:设计一个类,该类有数组的功能,可以存储数据,可以删除修改数据 2.设计核心数据 1.属性:指针(指向堆区空间),数组实际存储的元素个数,数组容量 2.方法:构造(开辟堆 ...
- 孙勇男:实时视频 SDK 黑盒测试架构丨Dev for Dev 专栏
Dev for Dev 专栏全称为 Developer for Developer,该专栏是声网与 RTC 开发者社区共同发起的开发者互动创新实践活动.透过工程师视角的技术分享.交流碰撞.项目共建等多 ...
- Agora Flat:在线教室的开源初体验
开发者其实很多时候都非常向往开源,开源领域的大佬也特别多,我们谈不上有多资深,也是一边探索一边做.同时,也希望可以借这次机会把我们摸索到的一些经验分享给大家. 01 Flat 是什么 Flat 是一个 ...
- Java并发夺命50问
本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...
- C#笔记之泛型
泛型是C#中应用极为广泛的一种语法,本篇文章将详细介绍泛型的定义.使用.性能等. 一.什么是泛型 首先需要记住的是,泛型是.NET 2.0推出的语法,这样的话,泛型基本可以用于所有程序的开发,而不需要 ...
- 在Kubernetes部署GitLab
在Kubernetes部署GitLab 前置条件 已安装Helm工具已部署NFS自动创建PVC 使用HELM安装 [root@k8s-master01 ~]# helm repo add gitlab ...
- Docker容器中使用GPU
背景 容器封装了应用程序的依赖项,以提供可重复和可靠的应用程序和服务执行,而无需整个虚拟机的开销.如果您曾经花了一天的时间为一个科学或 深度学习 应用程序提供一个包含大量软件包的服务器,或者已经花费数 ...
- python之PySimpleGUI(一)元素
1themesg.theme_previewer()获取所有主题颜色sg.preview_all_look_and_feel_themes()同上theme_name_list = sg.theme_ ...
- [Linux]常用命令之【du/fdisk/df/ls】#磁盘管理/文件管理#
本文的经典应用场景: 1.查找占用磁盘存储空间最大的目录/文件 2.关于[磁盘分区]的相关概念和实操,详见另一博文:[Linux]磁盘分区 - 博客园/千千寰宇 1 fdisk fdisk := &q ...
- AspNetCore 成长杂记(一):JWT授权鉴权之生成JWT(其一)
引子 最近不知怎么的,自从学了WebAPI(为什么是这个,而不是MVC,还不是因为MVC的Razor语法比较难学,生态不如现有的Vue等框架,webapi很好的结合了前端生态)以后,使用别人的组件一帆 ...