工厂方法模式(Factory Method Pattern)
- 工厂方法模式概述
工厂方法模式是为了弥补简单工厂模式的不足并且继承它的优点而延生出的一种设计模式,属于GoF中的一种。它能更好的符合开闭原则的要求。
定义:定义了一个用于创建对象的接口,但是让子类决定将哪一个类实例化。即让类的实例化延迟到子类
举个例子:大众汽车公司想必大家都不陌生,它旗下也有不少汽车品牌。大众汽车公司就好比一个汽车工厂,负责生产和销售汽车。它可以为客户提供一个客户需要的汽车。但是,如果客户需要的汽车大众公司目前还没有,但是公司想要盈利,就必须为此而设计汽车,在这种情况下,大众公司就要新添加一种汽车,同时要修改公司内部的生产环境(也就是工厂类的代码)。这就是简单工厂模式的运行情况。简单而言,就是工厂类(汽车公司)什么都要干,要修改必须大动干戈。因而一定程度上违背了开闭原则。而工厂方法模式则不一样,大众汽车公司不在总公司生产汽车,而是成立分公司,收购别的公司,成立具有针对性的汽车工厂专门生产对应的汽车。若客户的大量需求得不到满足,则总公司就另外成立新的二级公司(新品牌汽车的工厂)生产汽车,从而在不修改具体工厂的情况下引进新的产品。正如大众集团的收购一样。以下为简单工厂模式和工厂方法模式的区别:

- 工厂方法模式的结构与实现
结构:
- Product(抽象产品):定义产品的接口,是工厂方法模式所创建对象的公共父类(生产汽车)
- ConcreteProduct(具体产品):实现了抽象产品的接口,某类型的具体产品由专门的工厂创建(如具体类型的汽车)
- Factory(抽象工厂):它声明了工厂方法,用于返回一个产品。工厂方法模式的核心,所有创建对象的工厂必须实现该接口(创建生产汽车的工厂)
- ConcreteFactory(具体工厂):抽象工厂类的子类,实现了抽象工厂中声明的工厂方法,返回一个具体产品类的实例(对应具体的某一个汽车工厂)
实现:(日志记录器)

实现:
//Logger.cs 日志记录器接口 充当抽象产品接口
namespace FactoryMethodSample
{
interface Logger
{
void WriteLog();//抽象产品方法
}
} //DatabaseLogger 数据库日志记录器 具体产品
namespace FactoryMethodSample
{
class DatabaseLogger : Logger
{
public void WriteLog()
{
//数据库日志记录
}
}
} //FileLogger 文件日志记录器 具体产品
namespace FactoryMethodSample
{
class FileLogger : Logger
{
public void WriteLog()
{
//文件日志记录
}
}
} //日志记录器工厂接口,充当抽象工厂
namespace FactoryMethodSample
{
interface LoggerFactory
{
Logger CreateLogger();//抽象工厂方法
}
}
//DatabaseLoggerFactory 数据库日志记录器工厂类,具体工厂
namespace FactoryMethodSample
{
class DatabaseLoggerFactory : LoggerFactory
{
public Logger CreateLogger()
{
//连接数据库
//创建记录器对象
Logger logger = new DatabaseLogger();
...
return logger;
}
}
}
//FileLoggerFactory 文件日志记录器工厂类,具体工厂
namespace FactoryMethodSample
{
class FileLoggerFactory : LoggerFactory
{
public Logger CreateLogger()
{
//创建文件日志记录器
Logger logger = new FileLogger();
...
return logger;
}
}
}
//Program 客户端测试
namespace FactoryMethodSample
{
class Program
{ static void Main(string [] args)
{
LoggerFactory factory;//抽象工厂
Logger logger;//抽象产品
factory = new FileLoggerFactory();//new DatabaseLoggerFactory 可以更换为数据库日志记录器
logger = factory.CreateLogger();//抽象工厂方法
logger.WriteLog();//抽象产品方法
....
}
//如果要添加新的日志记录器,只要增加新的具体工厂类,并在客户端中修改具体工厂的类名便可以,从而避免了对原类的修改
}
}
- 工厂方法模式的重载
在某种情况下,可以用不同的方式来初始化一个产品类,在Logger记录器中,连接数据库的操作可以不用在每次CreateLog后再传入。而是可以将相关参数封装在一个object中,通过object对象将参数传入工厂类中,或者在参数列表中给出连接方式来连接数据库。为此,可以提供一组重载的工厂方法,以不同的方式创建产品。

代码修改如下:
namespace FactoryMethodSample
{
interface LoggerFactory
{
Logger CreateLogger();//抽象工厂方法
Logger CreateLogger(string args);
Logger CreateLogger(object obj);
}
} //DatabaseLoggerFactory 数据库日志记录器工厂类,具体工厂
namespace FactoryMethodSample
{
class DatabaseLoggerFactory : LoggerFactory
{
public Logger CreateLogger()
{
//连接数据库
//创建记录器对象
Logger logger = new DatabaseLogger();
...
return logger;
}
} class DatabaseLoggerFactory : LoggerFactory
{
public Logger CreateLogger(string args)
{
//用args连接数据库
//
Logger logger = new DatabaseLogger();
...
return logger;
}
} class DatabaseLoggerFactory : LoggerFactory
{
public Logger CreateLogger(object obj)
{
//将参数封装在obj中来连接数据库
Logger logger = new DatabaseLogger();
...
return logger;
}
}
}
- 工厂方法模式的隐藏
在客户端中,为简化使用,可以隐藏工厂方法。在工厂类调直接调用产品类的方法,在客户端中无需用工厂方法创建产品对象,直接使用工厂对象即可调用所创建的产品对象中的业务方法

代码修改如下:
//LoggerFactory 修改
abstract class LoggerFactory//改接口为抽象类
{
public void WriteLog()//工厂类直接调用日志记录器的WriteLog();
{
Logger logger = this.CreateLogger();
logger.WriteLog();
}
public abstract Logger CreateLogger();
} //客户端修改如下
namespace FactoryMethodSample
{
class Program
{
static void Main(string [] args)
{
LoggerFactory factory;
//Load configuration file;
string factoryString = ConfigurationManager.AppSettings["factory"];
//反射生成对象
factory = (LoggerFactory)Assembly.Load("FactoryMethodSample").CreateInstance(factoryString);
factory.WriteLog();//直接使用工厂对象调用产品对象的业务方法
}
}
}
- 工厂方法模式的优缺点和适用环境
(1)工厂方法模式的优点
(1)用户只需要关心产品对应的工厂,甚至无需关心创建细节或具体产品类的类名
(2)基于工厂角色和产品的多态性设计是工厂模式的关键。它能自主决定如何创建哪种产品对象,而创建细节都封装在具体工厂内部
(3)在系统要添加新的产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,只要添加一个具体工厂和具体产品即可,从而提高系统的可扩展性(符合开闭原则)
(2)工厂方法模式的缺点
(1)在添加新产品时,要编写新的具体产品类,并要提供与之对应的具体工厂类。系统软件个数也成对增加,从而增加了系统的复杂度,带来更多开销
(2)由于系统的可扩展性,在客户端中要引入抽象层进行定义,从而增加了系统的抽象性和理解难度
(3)工厂方法模式的适用环境
(1)客户端不需要知道它需要的对象的类。只需知道对应的工厂即可
(2)抽象工厂类通过子类来指定创建那哪个对象。即抽象工厂类只需要提供一个创建产品的接口,而由其子类确定具体要创建的对象,利用多态性和里氏代换原则,子类对象将覆盖父类对象,从而使系统更容易扩展
- 在日志记录器实例中,在更换日志记录器时需要修改客户端代码。为了按照开闭原则的要求执行,可以在不修改任何客户端代码的基础上更换或增加新的日志记录方式。即通过配置文件和程序集的反射机制,读取配置文件中存储的类名字符串生成对象。这里没有进行讲解,可以自行搜索..~_~!!
工厂方法模式(Factory Method Pattern)的更多相关文章
- 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern)
原文:乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pa ...
- 【设计模式】工厂方法模式 Factory Method Pattern
在简单工厂模式中产品的创建统一在工厂类的静态工厂方法中创建,体现了面形对象的封装性,客户程序不需要知道产品产生的细节,也体现了面向对象的单一职责原则(SRP),这样在产品很少的情况下使用起来还是很方便 ...
- 二十四种设计模式:工厂方法模式(Factory Method Pattern)
工厂方法模式(Factory Method Pattern) 介绍定义一个用于创建对象的接口,让子类决定将哪一个类实例化.Factory Method使一个类的实例化延迟到其子类. 示例有SqlMes ...
- 设计模式-03工厂方法模式(Factory Method Pattern)
插曲.简单工厂模式(Simple Factory Pattern) 介绍工厂方法模式之前,先来做一个铺垫,了解一下简单工厂模式,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背 ...
- 六个创建模式之工厂方法模式(Factory Method Pattern)
问题: 在使用简单工厂模式的时候,如果添加新的产品类,则必需修改工厂类,违反了开闭原则. 定义: 定义一个用于创建对象的接口,让子类决定具体实例化哪个产品类.此时工厂和产品都具有相同的继承结构,抽象产 ...
- 设计模式之工厂方法模式(Factory Method Pattern)
一.工厂方法模式的诞生 在读这篇文章之前,我先推荐大家读<设计模式之简单工厂模式(Simple Factory Pattern)>这篇文档.工厂方法模式是针对简单工厂模式中违反开闭原则的不 ...
- 【UE4 设计模式】工厂方法模式 Factory Method Pattern 及自定义创建资源
概述 描述 又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式,或者多态工厂(Polymorphic Factory)模式 工厂父类负责定义创建产品对象的公共接口,而工厂子类 ...
- C#设计模式——工厂方法模式(Factory Method Pattern)
一.概述在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.如何应对这种变化?如何提供一种封装机制来隔离出“这个易变对象 ...
- php工厂方法模式(factory method pattern)
继续练 <?php /* The factory method pattern deals with the problem of creating objects without having ...
随机推荐
- 谈谈书本《c#物联网程序设计基础》中的技术瑕疵,如果你将要读本书,请进来看看!
今天去书店看到一本名为<c#物联网程序设计基础>的书,对物联网感兴趣的我抓起来就看,书中的项目都是上位机开发项目,较简单,如果物联网开发只是这样,看起来我做物联网开发也是绰绰有余.这边书我 ...
- 基于Microsoft Graph打造自己的Timeline应用
原文链接:https://github.com/chenxizhang/office365dev/blob/e9b5a59cb827841d36692cc4ec52c11d43062e04/docs/ ...
- 设计模式之模板方法(Template Method)
在整理模板方法之前,先来说点废话吧.除了记录学习总结,也来记录一下生活吧. 我们公司的老板在北京,老板也会因为项目来公司,不过不是天天来.公司有个同事,只要老板不在就天天迟到,而且一天比一天晚,经常来 ...
- (干货)微信小程序之转发好友
今天简单地说下微信小程序的转发功能,为什么要简单的说下呢,因为主要讲的就是转发给好友或者群组,还有一种是分享到朋友圈,这种就比较复杂一点了,先稍微透漏一点,分享到朋友圈主要是两种方法,一种是后台直接生 ...
- 浮点型 float和double类型的内存结构和精度问题
首先引用一个例子在java中可能你会遇到这样的问题: 例:0.99999999f==1f //true 0.9999999f==1f //false 这是超出精度造成的,为了知道为什么会造成这样的问题 ...
- Spring @Conditional注解的使用
Spring Boot的强大之处在于使用了Spring 4框架的新特性:@Conditional注释,此注释使得只有在特定条件满足时才启用一些配置. 下面来介绍如何使用Condition 首先写一个类 ...
- JQ无缝轮播图-插件封装
类似京东的这种无缝轮播效果: 实例代码下载 HTML代码: <body> <!-- /*觅me 探索生活*/ --> <div class="test" ...
- 解决VMware虚拟机报错“无法连接MKS:套接字连接尝试次数太多,正在放弃”
1.错误描述 在VMware中打开虚拟机时报错: "无法连接MKS:套接字连接尝试次数太多,正在放弃" 物理机操作系统: Windows 7 虚拟机操作系统: Kali Linux ...
- 更改eclipse的编辑器样式风格
这里提供两种方法: 1.从网站http://eclipsecolorthemes.org/ 下载,选好自己喜欢的风格后下载epf文件...再使用eclipse的import->preferenc ...
- 五个最佳RSS新闻阅读器
文章出自http://www.williamlong.info/archives/1591.html 在博客和在线新闻充斥的互联网上,大量信息已经使得用户阅读量过载,幸运的是,通过RSS Feed提供 ...