设计模式--简单工厂(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;
switch (type.ToLowerInvariant())
{
case "histogram":
{
// 初始化柱状图
break;
}
case "pie":
{
// 初始化饼状图
break;
}
case "line":
{
// 初始化折线图
break;
}
}
}
public void Display()
{
switch (type.ToLowerInvariant())
{
case "histogram":
{
// 显示柱状图
break;
}
case "pie":
{
// 显示饼状图
break;
}
case "line":
{
// 显示折线图
break;
}
}
}
}
修改点:多个if-else 判断不太好,可以用switch更好些
客户端代码通过调用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 适用场景
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只需要知道传入工厂类的参数,对于如何创建对象并不关心。
来源:http://www.cnblogs.com/edisonchou/p/6683262.html
设计模式--简单工厂(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这个关键字的时候, 就应该想到它是具体的实现. 这就是一个具体的类, 为了更灵活, 我们应该使用的是 ...
- 设计模式~简单工厂模式(Factory)
简单工厂模式Simple Factory根据提供给它的数据,返回一个类的实例.通常它返回的类都有一个公共的父类(或者接口对象). 简单工厂的作用是实例化对象,而不需要客户了解这个对象属于哪个具体的子类 ...
- Golang设计模式—简单工厂模式(Simple Factory Pattern)
Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...
- java设计模式--简单工厂
java设计模式--简单工厂 简单工厂不是一个标准的设计模式,但是很常用需要掌握. 在java应用开发中,要"面向接口编程". 1.java中接口的概念: 在java中接口是一种特 ...
- Yii2设计模式——简单工厂模式
除了使用 new 操作符之外,还有更多的制造对象的方法.你将了解到实例化这个活动不应该总是公开进行,也会认识到初始化经常造成"耦合"问题. 应用举例 yii\db\mysql\Sc ...
- Yii2 设计模式——简单工厂模式
除了使用 new 操作符之外,还有更多的制造对象的方法.你将了解到实例化这个活动不应该总是公开进行,也会认识到初始化经常造成“耦合”问题. 应用举例 yii\db\mysql\Schema 中: // ...
- 3. 星际争霸之php设计模式--简单工厂模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- 深入浅出设计模式——简单工厂模式(Simple Factory)
介绍简单工厂模式不能说是一个设计模式,说它是一种编程习惯可能更恰当些.因为它至少不是Gof23种设计模式之一.但它在实际的编程中经常被用到,而且思想也非常简单,可以说是工厂方法模式的一个引导,所以我想 ...
随机推荐
- rabbitmq 结构体
amqp_basic_properties_t props; props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MOD ...
- shell 函数传递参数的几种方式
1.最近总结了 shell 中 function 的传递变量的几种方式 1.传递单个变量 2.传递数组变量 #!/bin/bash #trying to pass an variable. ...
- 线程安全与非线程安全集合说一下,底层怎么实现的(hashmap,concurrenthashmap)
Hashmap本质是数组加链表.根据key取得hash值,然后计算出数组下标,如果多个key对应到同一个下标,就用链表串起来,新插入的在前面. ConcurrentHashMap:在hashMap的基 ...
- activemq---点对点/发布订阅模式简单代码示例
activemq 消息模式流程: ConnnectionFactory --> Connection --> Session --> Message ---ConnectionFac ...
- hibernate配置注意事项
1:多对一配置 private Set<DrawRecordModel> cjrecordsSet = new HashSet<DrawRecordModel>(); 正确 p ...
- 唐太宗灵州勒石 TANGTAIZONGLINZHOULESHI
唐太宗灵州勒石 唐贞观二十年(646年),在唐军和回纥部落联合打败突厥薛延陀部后,原归附薛延陀部的回纥.拔野古.斛薛等部族,越过贺兰山,进入了今宁夏的银川.吴忠一带地区.这些总族向唐朝提出,愿意臣服唐 ...
- SQL执行顺序和coalesce以及case when的用法
1.mysql的执行顺序 from on join where group by having select distinct union //UNION 操作符用于合并两个或多个 SELECT ...
- SQL笔试
一.题目 二.练习数据 SELECT VERSION(); create table CDM_AGENT(ano VARCHAR(6),aname VARCHAR(8)); create table ...
- Tortoise git账号记住密码
方法一: 本地git仓库目录下,找到 .git/config ,然后打开增加: [credential] helper = store 这样只有该目录下的git操作不用每次输入用户名和密码,换一个目录 ...
- CTF | bugku | 秋名山车神
''' @Modify Time @Author ------------ ------- 2019/8/31 19:55 laoalo ''' import requests from lxml i ...