工厂方法模式(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 ...
随机推荐
- JS的事件绑定、事件流模型
.t1 { background-color: #ff8080; width: 1100px; height: 40px } 一.JS事件 (一)JS事件分类 1.鼠标事件:click/dbclick ...
- 团队项目第二阶段个人进展——Day5
一.昨天工作总结 冲刺第五天,找到了一个专门的提供后端数据服务的网站:leancloud,并学习了相关操作 二.遇到的问题 对leancloud的数据如何请求和响应不懂 三.今日工作规划 深入学习le ...
- Python_sqlite3
import sqlite3 #导入模块 conn = sqlite3.connect('example.db') #连接数据库 c = conn.cursor() #创建表 c.execute('' ...
- Ubuntu 16.04 安装 Docker
在Ubuntu上安装Docker, 非常简单, 我测试过 16.04, 17.04, 以及最新版 18.04,都是可以成功安装,并使用的. 第一步: 启动root账号 第二步: 配置网络,能上网 ...
- 微信小游戏开发之四:使用three.js引擎
一.前言 微信小游戏中最魔性的'跳一跳'就是基于three.js 引擎开发的 源码放到github上了:GitHub地址 请自行下载. 二.下载 three.min.js 打开页面,复制代码到本地 ...
- 解决redis connection refused: connect无法连接redis
环境 VM VirtualBox安装虚拟机CentOS 7 1.Redis.conf配置文件中 注释掉 bind 127.0.0.1 2.防火墙关闭(或添加可访问的端口,具体不在此描述) 最后一个坑 ...
- mysql索引sql优化方法、步骤和经验
MySQL索引原理及慢查询优化 http://blog.jobbole.com/86594/ 细说mysql索引 https://www.cnblogs.com/chenshishuo/p/50300 ...
- Oracle-06:DML语言数据表的操作
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 开篇放上一个SQL脚本,供测试使用 create table DEPT ( deptno ) not null ...
- Python 的 urllib.parse 库解析 URL
Python 中的 urllib.parse 模块提供了很多解析和组建 URL 的函数. 解析url urlparse() 函数可以将 URL 解析成 ParseResult 对象.对象中包含了六 ...
- n级阶梯,每次走一步或两步,问最多有多少种走法 二叉树实现
NodeTree类 public class NodeTree { private int num; private NodeTree left; private NodeTree right; pu ...