设计模式的艺术之道–工厂方法模式
声明:本系列为刘伟老师博客内容总结(http://blog.youkuaiyun.com/lovelion),博客中有完整的设计模式的相关博文,以及作者的出版书籍推荐
本系列内容思路分析借鉴了刘伟老师的博文内容,同时改用C#代码进行代码的演示和分析(Java资料过多 C#表示默哀).
本系列全部源码均在文末地址给出。
本系列开始讲解创建型模式,顾名思义,这类模式主要是为了解决类的创建问题。
- 创建型模式(Creational Pattern)关注对象的创建过程。分析你怎么来的
- 创建型模式对类的实例化过程进行了抽象,对用户隐藏了类的实例的创建细节。客户不知道你怎么来的
- 创建型模式描述如何将对象的创建和使用分离,让用户在使用对象时无须关心对象的创建细节。你用就行,别管我怎么来的。
- 创建型模式关注点 创建什么(What) 由谁创建(Who) 何时创建(When)
6种常见的创建型模式
工厂方法模式
简单工厂模式虽然简单,但存在一个很严重的问题。当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,将违背“开闭原则”,如何实现增加新产品而不影响已有代码?
1.1定义
- 工厂方法模式 (Simple Factory Pattern):定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
- 老板开始雇人(工厂)干活了,炒菜的炒菜(产品),做面的做面(产品),做点心的做点心(产品)。老板把任务分配下去自己不管了。
- 工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象。
1.2情景实例
问题描述
- 日志记录器(Logger)设计
菜鸟软件公司欲开发一个系统运行日志记录器,该记录器可以通过多种途径保存系统的运行日志,如通过文件记录或数据库记录,用户可以通过修改配置文件灵活地更换日志记录方式。在设计各类日志记录器时,菜鸟公司的开发人员发现需要对日志记录器进行一些初始化工作,初始化参数的设置过程较为复杂,而且某些参数的设置有严格的先后次序,否则可能会发生记录失败。如何封装记录器的初始化过程并保证多种记录器切换的灵活性是Sunny公司开发人员面临的一个难题。
初步思路
LoggerFactory充当创建日志记录器的工厂,提供了工厂方法createLogger()用于创建日志记录器,Logger是抽象日志记录器接口,其子类为具体日志记录器。其中,工厂类LoggerFactory代码片段如下所示。
//日志记录器工厂
class LoggerFactory {
//静态工厂方法
public static Logger createLogger(String args) {
if(args.equalsIgnoreCase("db")) {
//连接数据库,代码省略
//创建数据库日志记录器对象
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器,代码省略
return logger;
}
else if(args.equalsIgnoreCase("file")) {
//创建日志文件
//创建文件日志记录器对象
Logger logger = new FileLogger();
//初始化文件日志记录器,代码省略
return logger;
}
else {
return null;
}
}
}
现有缺点(未来变化)
- (1) 用户可能需要更换日志记录方式,日志工厂类需要单独修改,
- (2如果增加新类型的日志记录器,必须修改静态工厂方法的业务逻辑,违反了“开闭原则”。
- (3) 工厂类过于庞大,包含了大量的if…else…代码,导致维护和测试难度增大。
如何改进
引入工厂方法模式
- 对具体的产品类,单独提升出一个产品的工厂类
- 针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构
UML类图
实例关键代码
namespace FactoryMethod
{
interface Logger
{
//抽象产品
void WriteLog();
}
class FileLogger : Logger
{
//具体产品
public void WriteLog()
{
Console.WriteLine("文件日志记录。");
}
}
interface LoggerFactory
{
Logger CreateLogger();
}
class FileLoggerFactory : LoggerFactory
{
// 具体产品工厂
public Logger CreateLogger()
{
//创建文件日志记录器对象
Logger logger = new FileLogger();
//创建文件,代码省略
return logger;
}
}
class Program
{
//客户端调用
static void Main(string[] args)
{
LoggerFactory factory;
Logger logger;
//读取配置文件
string factoryString = ConfigurationManager.AppSettings["factory"];
//反射生成对象
factory = (LoggerFactory)Assembly.Load("FactoryMethod").CreateInstance(factoryString);
logger = factory.CreateLogger();
logger.WriteLog();
Console.ReadLine();
}
}
}
1.3模式分析
动机和意图
- 在简单工厂模式中,所有的产品都由同一个工厂创建,工厂类职责较重
- 具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性
一般结构
- 工厂方法模式包含如下角色:
- Factory:工厂角色 负责创建产品
- Product:抽象产品角色 产品的抽象归类提升
- ConcreteProduct:具体产品角色 工厂输出的产品
- ConcreteFactory : 具体工厂 生产具体产品的具体工厂
改进的优点
- 向客户隐藏了哪种具体产品类将被实例化这一细节,运行时自行选择确定
- 如何创建这个对象的细节则完全封装在具体工厂内部
- 在系统中加入新产品时,完全符合开闭原则
现存的缺点
- 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度
- 增加了系统的抽象性和理解难度
优化空间
- 工厂的造产品方法可以进行多种参数的重载
- 工厂方法的隐藏,在工厂类中将直接调用产品类的业务方法
- 客户端无须调用工厂方法创建产品,
适用场景
(1) 客户端不知道它所需要的对象的类,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)。
(2) 抽象工厂类通过其子类来指定创建哪个对象。
举例:小王去王记饭馆吃饭(小王要吃面,告诉了老板,老板吩咐王麻子厨师去做) 图片读取器(png,jpg,gif) 每种格式对应一个工厂和产品
实例源代码
GitHub地址
百度云地址:链接: https://pan.baidu.com/s/1hsOXlTa 密码: vgtv
本文深入探讨工厂方法模式,解析其核心思想与应用场景。通过对比简单工厂模式,展示工厂方法模式如何更好地解决对象创建问题,特别是在系统扩展性方面。文章通过一个具体的日志记录器实例,演示如何利用工厂方法模式来提高代码的灵活性和可维护性。
1041

被折叠的 条评论
为什么被折叠?



