94、23种设计模式之工厂方法模式(3/23)
工厂方法模式(Factory Method Pattern)是23种经典设计模式中的创建型模式,其核心思想是将对象的实例化延迟到子类中完成,通过定义一个创建对象的接口(工厂方法),让子类决定具体实例化哪个类。以下是其关键要点与实现分析:
一、模式结构
工厂方法模式包含四个核心角色:
1.抽象产品(Product)
-
定义产品的公共接口,所有具体产品必须实现该接口。
-
示例:ILog接口定义日志记录的通用方法(如Write)。
2.具体产品(Concrete Product)
- 实现抽象产品接口,提供具体功能。
- 示例:FileLog(文件日志)和DatabaseLog(数据库日志)实现ILog接口。
3.抽象工厂(Creator)
- 声明工厂方法(如CreateProduct),返回抽象产品类型。
- 可包含默认实现或通用逻辑。
- 示例:LogFactory抽象类定义CreateLog方法。
4.具体工厂(Concrete Creator)
- 实现抽象工厂的工厂方法,返回具体产品实例。
- 示例:FileLogFactory和DatabaseLogFactory分别创建FileLog和DatabaseLog。
二、C# 实现示例
以下是一个完整的C#代码示例,展示日志系统的工厂方法模式实现:
using System;// 抽象产品:日志接口
public interface ILog
{void Write(string message);
}// 具体产品:文件日志
public class FileLog : ILog
{public void Write(string message){Console.WriteLine($"[文件日志] {message}");}
}// 具体产品:数据库日志
public class DatabaseLog : ILog
{public void Write(string message){Console.WriteLine($"[数据库日志] {message}");}
}// 抽象工厂:日志工厂
public abstract class LogFactory
{public abstract ILog CreateLog();// 通用日志方法(可选)public void LogMessage(string message){ILog log = CreateLog();log.Write(message);}
}// 具体工厂:文件日志工厂
public class FileLogFactory : LogFactory
{public override ILog CreateLog(){return new FileLog();}
}// 具体工厂:数据库日志工厂
public class DatabaseLogFactory : LogFactory
{public override ILog CreateLog(){return new DatabaseLog();}
}// 客户端代码
class Program
{static void Main(string[] args){// 使用文件日志工厂LogFactory fileFactory = new FileLogFactory();fileFactory.LogMessage("这是一条文件日志");// 使用数据库日志工厂LogFactory dbFactory = new DatabaseLogFactory();dbFactory.LogMessage("这是一条数据库日志");}
}
输出结果:
[文件日志] 这是一条文件日志
[数据库日志] 这是一条数据库日志
三、模式优势
1.解耦客户端与具体产品
- 客户端通过抽象工厂和抽象产品接口交互,无需关心具体实现类。
2.符合开闭原则(OCP)
- 新增产品类型时,只需添加具体产品和工厂类,无需修改现有代码。
- 示例:新增ConsoleLog时,仅需创建ConsoleLog类和ConsoleLogFactory类。
3.单一职责原则(SRP)
- 工厂类负责对象创建,客户端代码专注于业务逻辑。
4.灵活性与可扩展性
- 支持动态切换产品类型(如通过配置文件选择工厂)。
四、适用场景
1.需要动态决定创建哪种产品
- 例如:根据用户输入或配置文件选择日志类型(文件/数据库/控制台)。
2.希望解耦对象创建与使用
- 客户端只需知道抽象接口,无需依赖具体实现。
3.系统需要支持多种产品类型
- 例如:不同数据库连接(MySQL、Oracle)、不同UI组件(Windows按钮、macOS按钮)。
五、工厂方法模式和抽象工厂模式对比
1. 核心目标
工厂方法模式
-
单一产品创建:通过子类决定实例化哪一个具体产品(一个产品对应一个工厂)。
-
动态扩展:新增产品时,只需新增具体工厂类(符合开闭原则)。
-
典型场景:日志系统(文件日志/数据库日志)、资源加载(如Unity的Resources.Load)。
抽象工厂模式
- 产品族创建:创建一组相关或依赖的产品(如一套UI组件、一套数据库连接)。
- 统一风格:确保客户端获取的产品属于同一系列(如Windows风格按钮+文本框)。
- 典型场景:跨平台UI库(Windows/macOS控件)、数据库访问层(MySQL/Oracle连接池)。
2. 结构复杂度
工厂方法
- 简单直接,每个工厂负责一个产品。
抽象工厂
- 复杂度高,需维护产品族的一致性(如新增“Linux按钮”需同时修改所有具体工厂)。
3. 如何选择?
用工厂方法模式
- 需要灵活创建单一类型对象,且产品类型可能动态变化。
- 示例:根据配置文件选择日志方式(文件/数据库)。
用抽象工厂模式
- 需要创建一组相关对象,并确保它们属于同一风格或系列。
- 示例:开发跨平台应用时,统一生成Windows/macOS风格的UI组件。
总结
- 工厂方法模式是“一对一”创建,抽象工厂模式是“一对多”创建,前者更灵活,后者更强调产品族的一致性。
六、实际应用案例
1.ASP.NET Core 日志系统
- 通过ILoggerFactory动态注册日志提供程序(如控制台、文件、Azure)。
2.Unity 游戏引擎
- 资源加载(如Resources.Load)可视为工厂方法的变体,根据路径动态创建游戏对象。
3.数据库连接池
- 不同数据库(MySQL、SQL Server)对应不同的连接工厂,通过工厂方法创建连接对象。
七、总结
工厂方法模式通过将对象创建委托给子类,实现了解耦、可扩展性和符合开闭原则,适合需要灵活创建对象的场景。若产品类型较少且固定,可考虑简单工厂模式以降低复杂度;若需严格遵循OCP或产品类型动态变化,工厂方法模式是更优选择。