设计模式的征途—2.简单工厂(Simple Factory)模式
工厂模式是最常用的一种创建型模式,通常所说的工厂模式一般是指工厂方法模式。本篇是是工厂方法模式的“小弟”,我们可以将其理解为工厂方法模式的预备知识,它不属于GoF 23种设计模式,但在软件开发中却也应用地比较频繁。此外,工厂方法模式还有一位“大哥”—抽象工厂模式,会在后面进行介绍。
简单工厂模式(Simple Factory) | 学习难度:★★☆☆☆ | 使用频率:★★★☆☆ |
一、从一个图表库谈起
M公司想要基于C#语言开发一套图表库,该图表库可以为应用系统提供各种不同外观的图标,例如柱状图、饼状图或折线图等。M公司图表库设计开发人员希望为应用系统开发人员提供一套灵活易用的图表库,而且可以较为方便地对图表库进行扩展,以便于在将来增加一些新类型的图表。
M公司的程序员提出了一个初始设计方案,将所有图表的实现代码封装在一个Chart类中,其框架代码如下所示:
public class Chart
{
private string type; // 图表类型 public Chart(object[][] data, string type)
{
this.type = type; if (this.type.Equals("histogram", StringComparison.OrdinalIgnoreCase))
{
// 初始化柱状图
}
else if (this.type.Equals("pie", StringComparison.OrdinalIgnoreCase))
{
// 初始化饼状图
}
else if (this.type.Equals("line", StringComparison.OrdinalIgnoreCase))
{
// 初始化折线图
}
} public void Display()
{
if (this.type.Equals("histogram", StringComparison.OrdinalIgnoreCase))
{
// 显示柱状图
}
else if (this.type.Equals("pie", StringComparison.OrdinalIgnoreCase))
{
// 显示饼状图
}
else if (this.type.Equals("line", StringComparison.OrdinalIgnoreCase))
{
// 显示折线图
}
}
}
客户端代码通过调用Chart类的构造函数来创建图表对象,根据参数type的不同可以得到不同类型的图标,然后再调用Display()方法来显示相应的图表。
但是,不难看出,Chart类是一个巨大的类,存在很多问题:
- 在Chart类中包含很多if-else代码块,相当冗长,可读性很差;
- Chart类的职责过重,负责初始化和显示各种图表对象,违反了单一职责原则;
- 当需要增加新的图表类型时,必须修改Chart类的源代码,违反了开闭原则;
- 客户端只能通过new关键字来直接创建Chart对象,Chart类与客户端类耦合度较高,对象的创建和使用无法分离;
- 客户端在创建Chart对象之前可能还需要进行大量初始化设置,例如设置柱状图的颜色和高度等,如果在Chart类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设置,这些代码在每次创建Chart对象时都会出现,导致代码的重复;
二、简单工厂模式概述
2.1 要点
简单工厂模式并不属于GoF 23种经典设计模式,但通常将它作为学习其他工厂模式的基础。
简单工厂(Simple Factory)模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法模式,它属于创建型模式。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需的对象,而无须知道其创建细节。
2.2 结构图
简单工厂模式包含3个角色:
- Factory - 工厂角色:该模式的核心,负责实现创建所有产品实例的内部逻辑,提供一个静态的工厂方法GetProduct(),返回抽象产品类型Product的实例。
- Product - 抽象产品角色:所有产品类的父类,封装了各种产品对象的共有方法,它的引入将提高系统的灵活性,使得在工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
- ConcreteProduct - 具体产品角色:简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。
在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象。(可以看出,它是工厂模式家族中最简单的一员)
三、重构图表库的实现
3.1 新的结构图
为了将Chart类的职责分离,同时将Chart对象的创建和使用分离,M公司开发人员决定使用简单工厂模式对图表库进行重构,重构后的结构图如下所示:
3.2 新的代码实现
(1)抽象产品角色:IChartable接口
public interface IChartable
{
void Display();
}
(2)具体产品角色:各种图表类型
public class HistogramChart : IChartable
{
public HistogramChart()
{
Console.WriteLine("创建柱状图...");
} public void Display()
{
Console.WriteLine("显示柱状图...");
}
} public class LineChart : IChartable
{
public LineChart()
{
Console.WriteLine("创建折线图...");
} public void Display()
{
Console.WriteLine("显示折线图...");
}
} public class PieChart : IChartable
{
public PieChart()
{
Console.WriteLine("创建饼状图...");
} public void Display()
{
Console.WriteLine("显示饼状图...");
}
}
(3)工厂角色:ChartFactory
public class ChartFactory
{
public static IChartable GetChart(string type)
{
IChartable chart = null; if (type.Equals("histogram", StringComparison.OrdinalIgnoreCase))
{
chart = new HistogramChart();
Console.WriteLine("初始化设置柱状图...");
}
else if (type.Equals("pie", StringComparison.OrdinalIgnoreCase))
{
chart = new PieChart();
Console.WriteLine("初始化设置饼状图...");
}
else if (type.Equals("line", StringComparison.OrdinalIgnoreCase))
{
chart = new PieChart();
Console.WriteLine("初始化设置折线图...");
} return chart;
}
}
(4)客户端调用:
public static void Main()
{
IChartable chart = ChartFactory.GetChart("histogram");
if (chart != null)
{
chart.Display();
} chart = ChartFactory.GetChart("pie");
if (chart != null)
{
chart.Display();
}
}
运行结果如下:
在客户端代码中,使用工厂类的静态方法来创建具体产品对象,如果需要更换产品,只需要修改静态工厂方法中的参数即可。例如:将柱状图改为饼状图,只需要将代码:
IChartable chart = ChartFactory.GetChart("histogram");
改为:
IChartable chart = ChartFactory.GetChart("pie");
3.3 改进的方案
M公司开发人员发现在创建具体Chart对象时,每次更换一个Chart对象都需要修改客户端中静态工厂方法的参数,客户端代码需要重新编译,这对于客户端而言,是违反了开闭原则的。于是,开发人员希望有一种方法能够在不修改客户端代码地前提下更换具体产品对象。
因此,他们考虑使用配置文件(XML)来实现:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="charttype" value="histogram"/>
</appSettings>
</configuration>
客户端因此改为:
public static void Main()
{
string type = AppConfigHelper.GetChartType(); // 读取配置文件中的charttype
if (string.IsNullOrEmpty(type))
{
return;
} IChartable chart = ChartFactory.GetChart(type);
if (chart != null)
{
chart.Display();
}
}
运行结果如下:
四、简单工厂模式总结
4.1 主要优点
- 实现了对象创建和使用的分离:客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的的参数即可。
- 通过引入配置文件,可以在不修改任何客户端代码地情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
4.2 主要缺点
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受影响。
- 使用简单工厂模式势必会增加系统中类的个数(引入新的工厂类),增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能会造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
4.3 适用场景
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只需要知道传入工厂类的参数,对于如何创建对象并不关心。
参考资料
刘伟,《设计模式的艺术—软件开发人员内功修炼之道》
设计模式的征途—2.简单工厂(Simple Factory)模式的更多相关文章
- 设计模式:简单工厂(Simple Factory)
定义:根据提供的数据或参数返回几种可能类中的一种. 示例:实现计算器功能,要求输入两个数和运算符号,得到结果. 结构图: HTML: <html xmlns="http://www.w ...
- 简单工厂(Simple Factory),最合适的设计模式首秀.
简单工厂又称为静态工厂方法(static factory method)模式,简单工厂是由一个工厂来决定创建出哪一种个体的实现,在很多的讨论中,简单工厂做为工厂方法模式(Factory Method) ...
- 使用C# (.NET Core) 实现简单工厂(Simple Factory) 和工厂方法设计模式 (Factory Method Pattern)
本文源自深入浅出设计模式. 只不过我是使用C#/.NET Core实现的例子. 前言 当你看见new这个关键字的时候, 就应该想到它是具体的实现. 这就是一个具体的类, 为了更灵活, 我们应该使用的是 ...
- 设计模式学习(四): 1.简单工厂 (附C#实现)
New 这是一个典型的情况, 我们需要在运行时来实例化一些具体的类. 在需要修改或者扩展的时候我们就需要改这段代码. 一个程序中可能会多次出现类似的代码, 这使得维护和更新非常困难而且容易出错. 通过 ...
- Unity C# 设计模式(二)简单工厂模式
定义: 简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一. 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例 ...
- Java设计模式(一) 简单工厂模式不简单
摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...
- PHP设计模式(一)简单工厂模式 (Simple Factory For PHP)
最近天气变化无常,身为程序猿的寡人!~终究难耐天气的挑战,病倒了,果然,程序猿还需多保养自己的身体,有句话这么说:一生只有两件事能报复你:不够努力的辜负和过度消耗身体的后患.话不多说,开始吧. 一.什 ...
- 设计模式(二)简单工厂模式(Simple Factory Pattern)
一.引言 这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理解的模式——简单工厂模式. 二.简单工厂 ...
- Headfirst设计模式的C++实现——简单工厂模式(Simple Factory)之二
为了引出后续的工厂方法,把在简单工厂模式的基础上增加了新功能——加盟店 简而言之就是把原来的单一简单工厂(能生产cheese和greek两种pizza)细分成了纽约地区的和芝加哥地区的(每种地区都能生 ...
随机推荐
- 【转】Tomcat源代码阅读系列
在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码(Tomcat源代码阅读系列之一) Tomcat总体结构(Tomcat源代码阅读系列之二) Tomcat启动过程(Tomcat ...
- MySQL主从复制与主主复制
1.简介 MySQL作为世界上使用最为广泛的数据库之一,免费是其原因之一.但不可忽略的是它本身的功能的确很强大.随着技术的发展,在实际的生产环境中,由单台MySQL数据库服务器不能满足实际的需求.此时 ...
- 探究CSS中的包裹性
之前一直都知道css中的部分元素具有包裹性,今天写博客的时候正好也遇到了一个,所以想总结一下,有错误的地方欢迎指出来. 什么是包裹性? 包裹性就是父元素的宽度会收缩到和内部元素宽度一样. 哪些元素具有 ...
- JavaScript知识点总结
JavaScript学习总结1.JavaScript是作用于网络和HTML的一个编程语言.2.JavaScript代码必须放在<script></script>标签之间,Jav ...
- java基础之路(一)
Java数据类型分为内置类型和扩展类型两大类,其中的内置类型就是基本数据类型,而扩展类型则是Java语言根据基本类型扩展出的其他类型(也叫引用类型)(如:class,String等).本文主要讨论的是 ...
- MES工具机器列表功能操作
概述 在机器权限组.限制工序变更等其他情况下,有时候需要重启中间件才能起作用,这样会直接影响其他人员的加工情况.为了改善这种情况,现在MES工具中新加了一个功能,可以单独重启某一个机器,其它机器不受影 ...
- 数字化工厂解决方案——OA办公自动化与ERP
移动办公APP/即时通讯 通过集成手机应用,将移动办公引入到企业信息化管理中,能随时随地的完成审批.查询.警报.知会.公告发布.KPI统计和信息推送.系统已经支持苹果系统.安卓系统和微软WP8系统.企 ...
- Python 3中字符串可以被改变吗?
Python 3中字符串可以被改变吗? 字符串有改变这个方法的:replace,比如: a = 'lkjhgfdsa' a.replace(') '123kjhgfdsa' #返回结果 从上面这个例子 ...
- Jquery的学习:基础核心!
一.什么是 jQuery jQuery是一个JavaScript库,它通过封装原生的JavaScript函数得到一整套定义好的方法.它的作者是John Resig,于2006年创建的一个开源项目,随着 ...
- ajax三级联动下拉菜单
ajax写三级联动,先写一个文件类吧,以后用的时候直接调用即可: 来找一张表: 实现: 中国地域的三级联动:省.市.区: 图: 说一下思路: (1)当用户选择省份的时候触发事件,把当前的省份的id通过 ...