一起学习设计模式--02.简单工厂模式

工厂模式是最常用的一类创建型设计模式。我们所说的工厂模式是指工厂方法模式,它也是使用频率最高的工厂模式。

简单工厂模式是工厂方法模式的小弟,它不属于GoF 23种设计模式,但是在软件开发中应用也颇为频繁,通常将它作为学习其它工厂模式的入门。

一、图表库的设计

A科技公司计划使用C#语言开发一套图表库,该图表库可以为应用系统提供各种不同外观的图表,比如柱状图、饼状图、折线图等。A科技公司的图表库设计人员希望为应用系统开发人员提供一套灵活易用的图表库,而且可以比较方便的对图表库进行扩展,以便能够在将来增加一些新类型的图表。A科技公司的研发人员提出了一个初始化设计方案,将所有图表的实现代码封装在一个 Chart 类中,代码如下:

    public class Chart{private string type; //图表类型public Chart(object[][] data, string type){this.type = type;if (type.Equals("histogram", StringComparison.OrdinalIgnoreCase)){//初始化柱状图}else if (type.Equals("pie", StringComparison.OrdinalIgnoreCase)){//初始化饼状图}else if (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 类是一个巨大的类,在该类的设计中存在以下几个问题:

  1. 类中包含大量的 “if - else” 代码块,整个类的代码相当冗长,代码越长,代码的可读性、维护难度、测试难度也越大。而且大量的判断会影响性能,无论是什么类型的图表,类的内部都需要做大量的判断。

  2. Chart 类的职责过重,违反了单一职责原则。它将图表的创建和显示都放在一个类中,不利于类的重用和维护。而且类的构造函数中有大量的判断,并且对象初始化的操作也都写在构造函数中,降低了创建效率。

  3. 如果需要增加新的图表,就需要修改 Chart 类,违反了开闭原则。

  4. 客户端只能通过 new 来实例化 Chart 对象,这样的话 Chart 对象和客户端的耦合度较高,对象的创建和使用无法分离。

  5. 客户端在创建 Chart 对象之前可能还需要进行大量的初始化设计,比如柱状图的颜色、高度等。如果在 Chart 类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设置,那么这些初始设置在每次创建 Chart 对象的时候都会出现,导致了代码的重复。

1.简单工厂模式的流程

首先简单工厂模式不属于GoF 23种经典设计模式,但通常将它作为学习其它工厂模式的基础,它的设计思想很简单,基本流程如下:

  1. 将需要创建各种不同对象的相关代码封装到不同的类中,这些类称为具体的产品类。

  2. 将他们公共的代码进行抽象和提取后封装在一个抽象产品类中,每一个具体的产品类都是这个抽象产品类的子类。

  3. 提供一个工厂类用于创建各种产品,在工厂类中提取一个创建产品的工厂方法,该方法可以根据传入的参数不同创建不同的具体产品对象。

  4. 客户端只需要调用工厂类的工厂方法并传入相应的参数即可得到一个具体的产品对象。

2.简单工厂模式的定义

简单工厂模式定义如下:

简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

3.简单工厂模式的要点

简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节。

4.简单工厂模式的结构

在简单工厂模式结构图中包含3个角色:

  1. Factory(工厂角色):即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。在工厂类中提供了静态的工厂方法FactoryMethod(),返回抽象产品类型 Product。

  2. Product(抽象产品角色):它是工厂类创建所有对象的父类,封装了各种产品对象的公共方法。抽象产品的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。

  3. ConceteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每个具体产品角色都继承了抽象产品角色,需要实现抽象产品中声明的抽象方法。

在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须使用 new 关键字来创建对象,它是工厂模式中最简单的一员。

在使用简单工厂模式时,首先需要对产品类进行重构,将所有产品类中公共的代码转移到抽象产品类中,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现。

三、完整解决方案

为了将 Chart 类的职责分离,同时将 Chart 对象的创建和使用分离,A科技公司开发人员决定使用简单工厂模式对图表库进行重构,重构后的图表库结构如下:

IChart 接口充当抽象产品类,其子类HistogramChart、LineChart、PieChart充当具体产品类,ChartFactory充当工厂类。完整代码如下:

    /// <summary>/// 抽象图表接口:抽象产品类/// </summary>public interface IChart{void Display();}/// <summary>/// 柱状图类:具体产品类/// </summary>public class HistogramChart : IChart{public HistogramChart(){Console.WriteLine("创建柱状图!");}public void Display(){Console.WriteLine("显示柱状图!");}}/// <summary>/// 折线图类:具体产品类/// </summary>public class LineChart : IChart{public LineChart(){Console.WriteLine("创建折线图!");}public void Display(){Console.WriteLine("显示折线图!");}}/// <summary>/// 饼状图类:具体产品类/// </summary>public class PieChart : IChart{public PieChart(){Console.WriteLine("创建饼状图!");}public void Display(){Console.WriteLine("显示饼状图!");}}/// <summary>/// 图表工厂类:工厂类/// </summary>public class ChartFactory{/// <summary>/// 静态工厂方法/// </summary>/// <param name="type">图表类型</param>/// <returns></returns>public static IChart GetChart(string type){IChart chart = null;if (type.Equals("histogram", StringComparison.OrdinalIgnoreCase)){chart = new HistogramChart();Console.WriteLine("初始化设置柱状图");}else if (type.Equals("line", StringComparison.OrdinalIgnoreCase)){chart = new LineChart();Console.WriteLine("初始化设置折线图");}else if (type.Equals("pie", StringComparison.OrdinalIgnoreCase)){chart = new PieChart();Console.WriteLine("初始化设置饼状图");}return chart;}}

编写客户端测试代码:

    class Program{static void Main(string[] args){IChart chart = ChartFactory.GetChart("line");chart.Display();}}

编译运行输出,结果如下:在客户端的测试代码中,我们可以看到使用工厂类的静态工厂方法来创建具体产品对象。如果后期需要更换产品,只需要修改静态工厂方法的入参即可。例如,我们需要将折线图改成饼状图,只需要将代码:IChart chart = ChartFactory.GetChart("line"); 中的 line 改为 pie 即可。

四、方案的改进

一切进行的很顺利,但是A科技公司的开发人员发现,在创建具体的 Chat 对象时,每次更换具体的 Chart 对象类型都需要修改静态工厂方法中的入参,然后还需要重新编译客户端代码,这对客户端来说**违反了开闭原则。**那有没有一种方法可以在不修改代码的前提下就能更换具体的产品对象呢?当然有!在C#中我们可以将静态工厂方法中的入参配置到config文件中,这样每次要替换具体的产品类,我们只需要修改配置文件即可。比如:

<?xml version="1.0" encoding="utf-8" ?>
<configuration><appSettings><add key="chartType" value="line"/></appSettings>
</configuration>

客户端代码如下:

    class Program{static void Main(string[] args){//获取配置文件中的chartType的值string type = System.Configuration.ConfigurationManager.AppSettings["chartType"];IChart chart = ChartFactory.GetChart(type);chart.Display();}}

从上边的代码可以看出,客户端代码中不包含任何与具体图表相关的信息。如果要更换具体图表对象,只需要修改config配置文件中的配置即可,无需修改任何代码,符合开闭原则。

补充:

在ASP.NET Core中配置文件通常使用的是json文件,比如appsettings.json,另外还有很多种配置文件类型,比如:ini、环境变量、用户机密等

五、创建对象与使用对象

如果一个类即要负责创建引用的对象,又要使用使用引用对象的方法,这样就会使创建对象和使用对象的职责耦合在一起,这样会导致一个很严重的问题,那就是违反了开闭原则。那怎么解决这个问题呢?

最常用的一种方法就是将创建对象的职责移除,并交由其它类来负责创建。由谁创建呢?答案是:工厂类。通过引入工厂类,客户类不涉及对象的创建,对象的创建者也不会涉及对象的使用。

所有的工厂模式都强调一点:两个类A和B之间的关系应该仅仅是A创建B或者是A使用B,而不能两种关系都有。将对象的创建和使用分离,也使得系统更加符合单一职责原则,有利于对功能的复用和系统的维护。

将创建对象和使用对象分离还有一个好处:防止用来实例化一个类的数据和代码在多个类中到处都是。

也不是说将每一个类都配置一个工厂类,而是要具体问题具体分析,对于那些产品类很简单,而且也不存在太多变数,构造过程也很简单的类,就无需为其提供工厂类,直接在使用的时候实例化即可。

六、简单工厂模式总结

简单工厂模式提供了专门的工厂类用于创建对象,将对象的创建和对象的使用分离开来。

1.主要优点

  1. 简单工厂模式实现了对象创建和使用的分离。对于客户端来说免除了直接创建产品对象的职责,而仅仅负责“消费”产品即可。

  2. 客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。

  3. 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

2.主要缺点

  1. 工厂类集中了所有具体产品类的创建逻辑,职责过重。一旦工厂类不能正常工作,整个系统都会受到影响。

  2. 简单工厂模式会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。

  3. 系统扩展困难。如果要添加新产品就需要修改工厂逻辑,如果产品类较多时,有可能会造成工厂逻辑过于复杂,不利于系统的扩展和维护。

  4. 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

3.适用场景

  1. 工厂类负责创建的对象比较少。由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

示例代码:

https://github.com/crazyliuxp/DesignPattern.Simples.CSharp

参考资料:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/304587.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python list remove 删除引用_Python——list列表操作一坑(remove)

问题发生背景&#xff1a;在一个对象被调用多次时候对象存入了列表暂时保存(主流程不能断开使用了while True)&#xff0c;然后实例化了大批对象&#xff0c;具体你可参考打飞机游戏或者坦克大战发射子弹&#xff0c;每个子弹一个实例&#xff0c;创建的多到一定时候肯定是要释…

雨后漫步长安街

雨后漫步长安街&#xff0c;四处是祖国60华诞的味道&#xff0c;和城楼合个影&#xff0c;待百年庆典的时候拿出来看一定别有一番滋味。那时我都是老婆婆了……转载于:https://blog.51cto.com/xurui/200059

hive 导入mysql数据库_求助 Hive 导入MYsql 数据库 报错啊

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼sqoop export \--connect "jdbc:mysql://localhost:3306/emp?characterEncodingUTF8" \--username root \--password 123456 \--input-fields-terminated-by , \--table t_dim_sqoop \--export-dir /user/hive/warehous…

聊一聊Load Average

背景 前段时间&#xff0c;由于有几个应用引入了 prometheus-net.DotNetRuntime 这个组件&#xff0c;导致系统 CPU 间歇性飙升。这是这个组件一个已知的雷。。同样也引发了系统的 Load Average (平均负载) 十分不正常。这是一台 4C8G 的服务器&#xff0c;Load Average 却是达…

我的邮局系统,欢迎大家注册!hotxf.com

我的邮局系统&#xff0c;欢迎大家注册&#xff01;hotxf.com mail.hotxf.com 可用于登陆MSN和邮件&#xff0c;25G网络硬盘我在使用 Windows Live 为 hotxf.com 托管电子邮件。我想邀请您在 hotxf.com 中创建电子邮件地址。您可以通过单击 https://domains.live.com/m…

优雅的读懂支持向量机 SVM 算法

转自&#xff1a;JerryLeadhttp://www.cnblogs.com/jerrylead/archive/2011/03/13/1982639.html简介支持向量机基本上是最好的有监督学习算法了。最开始接触SVM是去年暑假的时候&#xff0c;老师要求交《统计学习理论》的报告&#xff0c;那时去网上下了一份入门教程&#xff0…

python socket自动重连_详解python3中socket套接字的编码问题解决

一、TCP1、tcp服务器创建#创建服务器from socket import *from time import ctime #导入ctimeHOST #任意主机PORT 21567 #随机提供个端口号BUFSIZ 1024 # 缓冲区大小设置为1KB&#xff0c;可以根据网络性能和程序需要改变这个容量ADDR (HOST, PORT)tcpSerSock socket(AF_…

java压缩成.tar_java压缩tar.gz | 学步园

zlib、gzip、zip这三个概念的确比较容易混淆&#xff1a;zlib是一种数据压缩程序库&#xff0c;它的设计目标是处理单纯的数据(而不管数据的来源是什么)。gzip是一种文件压缩工具(或该压缩工具产生的压缩文件格式)&#xff0c;它的设计目标是处理单个的文件。gzip在压缩文件中的…

CentOS 介绍

CentOS是Community ENTerprise Operating System的简称&#xff0c;我们有很多人叫它社区企业操作系统&#xff0c;不管你怎么叫它&#xff0c;它都是linux的一个发行版本。CentOS并不是全新的linux发行版&#xff0c;倘若一说到RedHat这个大名&#xff0c;大家似乎都听过&…

Newbe.Claptrap 0.9.4 发布,全新构建

Newbe.Claptrap 0.9.4 发布&#xff0c;全新构建&#xff0c;全新内容&#xff0c;全新体验。简介 此次的版本更新虽然只是一个副版本更新。但实际上我们带来了非常多全新的内容。全方位将本框架带入到一个新的次代。我们希望我们的努力可以为在业务中需要 Actor 模式的开发者带…

【知识贴】战争中的数学应用

一、方程在海湾战争中的应用

Android Donut SDK 1.6r1 今日发布

Google今天公布了新版Android SDK,也就是Donut的最新1.6版,它可以帮助智能手机应用程序开发商迅速将自己的应用转换到Android系统上. SDK的发布有助于Google为自己寻找到更多的合作伙伴,为自己的Android Market添加人气,目前Donut已经可以下载. 下载:Android 1.6 SDK

java让对象分配在栈上_java – Hotspot何时可以在堆栈上分配对象?

我做了一些实验,以便了解Hotspot何时可以进行堆栈分配.事实证明,它的堆栈分配比基于available documentation的预期要有限得多.Choi“Escape Analysis for Java”引用的文章表明,只分配给局部变量的对象总是可以堆栈分配.但事实并非如此.所有这些都是当前Hotspot实现的实现细节…

开始CSRebot

写代码很久了了&#xff0c;时不时想把这些年学到的&#xff0c;用到的&#xff0c;或自己感悟到的&#xff0c;汇总一下&#xff0c;形成一个东西&#xff0c;或资料&#xff0c;或工具。但每当开始时就一片空白&#xff0c;总是觉得这些大家都会&#xff0c;这些只是自己片面…

toast弹窗_Android 开发(一):Toast弹窗与获取控件的值

前言&#xff1a;本人也是刚接触Android开发&#xff0c;自己也在摸索中&#xff0c;在实现功能的过程中走了不少弯路&#xff0c;其中很重要的原因之一就是查询不到想要的内容&#xff0c;所以想把自己的经验分享出来。首先是Toast的应用&#xff0c;直接上关键代码。Toast to…

魔性十足的数学动态图,这种东西都拿出来分享?

小编最近发现了一组超美的与数学有关的动态图&#xff0c;而且看久了简直就有魔性&#xff0c;实在是按耐不住激动的心情&#xff0c;秉持着好图齐分享的原则&#xff0c;在这里和大家一起分享一波~>>>>来源&#xff1a;蒋迅的博客

Command模式

作用: 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作. UML结构图: 解析: Comnand模式的思想是把命令封装在一个类中,就是这里的Command基类,同时把接收对象也封装在一个类中就是这里的Receiver 类中,由调用…

Dapr微服务应用开发系列4:状态管理构件块

Dapr微服务应用开发系列0&#xff1a;概述Dapr微服务应用开发系列1&#xff1a;环境配置Dapr微服务应用开发系列2&#xff1a;Hello World与SDK初接触Dapr微服务应用开发系列3&#xff1a;服务调用构件块题记&#xff1a;这篇介绍状态管理构件块&#xff0c;这个概念相对于微服…

输出螺旋数字正方形java_Java实现顺时针输出螺旋二维数组的方法示例

最近看到一道有点意思的逻辑算法题&#xff0c;便着手实现一下。题目是要求打印 出N*N顺时针螺旋数组&#xff0c;规律如下&#xff1a;// 1 2 3 4 5// 16 17 18 19 6// 15 24 25 20 7// 14 23 22 21 8// 13 12 11 10 9Java 实现示例代码如下&#xff1a;import java.util.Scan…

开线程插数据_python笔记7-多线程之线程同步(锁lock)

前言丨关于吃火锅的场景&#xff0c;小伙伴并不陌生&#xff0c;前面几章笔记里面我都有提到&#xff0c;今天我们吃火锅的场景&#xff1a;吃火锅的时候a同学往锅里下鱼丸&#xff0c;b同学同时去吃掉鱼丸&#xff0c;有可能会导致吃到生的鱼丸。为了避免这种情况&#xff0c;…