一起学习设计模式--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,一经查实,立即删除!

相关文章

雨后漫步长安街

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

聊一聊Load Average

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

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

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

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

开始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;这个概念相对于微服…

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

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

一文读懂欧拉函数

欧拉函数φ(N)表示小于或等于N的正整数中与N互质的数的个数。又称φ函数、欧拉商数。下面介绍欧拉函数的几个性质&#xff1a;我们根据这几个性质就可以求出欧拉函数。基本思路是首先置φ(N)N&#xff0c;然后再枚举素数p&#xff0c;将p的整数倍的欧拉函数φ(kp)进行如下操作。…

windows 7在工作组模式下管理 Hyper-V

之前在工作组模式下win7来管理server core 下的hyper-v一直不成功&#xff0c;server core下的相应的防火墙端口也已开放但hyper管理器就是无法和hyper-v主机通讯在2008R2下没有问题&#xff0c;现把解决的过程记录下来&#xff0c;以备后查。1、下载Windows 7 远程服务器管理工…

打工就是 “演戏”,你可以认真,但别太当真

这是头哥侃码的第235篇原创时间飞逝&#xff0c;2021年的春节假期已经结束了。在我的记忆中&#xff0c;以前每逢春节的时候&#xff0c;身边的亲戚和朋友们都会非常忙碌&#xff0c;什么聚个餐&#xff0c;串个门呀&#xff0c;什么拜个年&#xff0c;打个牌啦……但从去年开始…

2018年的第一个工作日,推荐几个优质公众号

2017刚过去不久&#xff0c;我们迎来了2018。工作、生活节奏超快的今天&#xff0c;想要不断提升自我&#xff0c;碎片化阅读学习是你最佳的选择&#xff0c;如果你有一颗学习的心&#xff0c;那这些学习型的公众号&#xff0c;绝对会让你受益匪浅。小编为你精选了技术领域几个…

redis session java获取attribute_redis里的数据结构

Redis作为当前使用非常广泛的内存数据库&#xff0c;在代码层面做了很多极致的优化&#xff0c;已获取更好的性能。其中重要的一部分&#xff0c;就是对于底层数据结构的使用。Redis会根据数据量、数据大小等来优化对于不同结构的使用&#xff0c;从而获得更佳的运行效率和内存…

.NET 云原生架构师训练营(模块二 基础巩固 安全)--学习笔记

2.8 安全认证 VS 授权ASP .NET Core 认证授权中间件认证JWT 认证授权认证 VS 授权认证是一个识别用户是谁的过程授权是一个决定用户可以干什么的过程401 Unauthorized 未授权403 Forbidden 禁止访问ASP .NET Core 认证授权中间件在接收到请求之后&#xff0c;认证&#xff08;A…

java超长字符序列化_String 字符串最长可以有多长?

Java技术栈www.javastack.cn打开网站看更多文章String 类可以说是在 Java 中使用最频繁的类了&#xff0c;就算是刚刚接触 Java 的初学者也不会陌生&#xff0c;因为对于 Java 程序来说&#xff0c;main 方法就是使用一个 String 类型数组来作为参数的(String[] args)。对于这样…

一文读懂 AVL 树

背景AVL 树是一棵平衡的二叉查找树&#xff0c;于 1962 年&#xff0c;G. M. Adelson-Velsky 和 E. M. Landis 在他们的论文《An algorithm for the organization of information》中发表。所谓的平衡之意&#xff0c;就是树中任意一个结点下左右两个子树的高度差不超过 1。&am…