【设计模式】简单工厂模式 Simple Factory Pattern
简单工厂模式Simple Factory Pattern【Simple Factory Pattern】是设计模式里最简单的一个模式,又叫静态工厂模式【Static Factory Pattern】,这个模式没有收录在GOF 23 个模式中,因为他非常简单,在项目中使用也非常广泛,所以就用它来开篇。
一、简单工厂模式定义:
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。
二、简单工厂模式结构图
简单工厂结构图
从简单工厂结构图中我们可以看出它包含三个角色:
1. IProduct (抽象产品角色):
它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。抽象产品可以使用接口和抽象类来来实现。
2. ConcreteProduct (具体产品角色):
它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象,它是工厂模式家族中最简单的一员。 在使用简单工厂模式时,首先需要对产品类进行重构,不能设计一个包罗万象的产品类,而需根据实际情况设计一个产品层次结构,将所有产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现。
3. Factory (工厂角色):
这个角色就是工厂类,他是工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product。
三、简单工厂的代码实现
public interface IProduct
{
void SomeMethod();
} public class ConcreteProductA : IProduct
{
public void SomeMethod()
{
Console.WriteLine("I am product A");
}
} public class ConcreteProductB : IProduct
{
public void SomeMethod()
{
Console.WriteLine("I am product B");
}
}
public class Factory
{
public static IProduct Create(string productType)
{
IProduct product;
switch (productType.ToUpper())
{
case "A":
product = new ConcreteProductA();
break;
case "B":
product = new ConcreteProductB();
break;
default:
throw new ArgumentException("Invlid Argument.", nameof(productType));
}
return product;
}
}
客户端调用:
static void Main()
{
var product = Factory.Create("A");
product.SomeMethod(); product = Factory.Create("B");
product.SomeMethod(); Console.ReadKey();
}
输出结果:
四、需求改变促使代码重构得到简单工厂模式
甲方提出了这样一个需求:要求做一个音频播放器。只需要支持播放WMA格式的文件就好了。开发人员拿到需求后窃喜,这么简单个需求,简直就是毛毛雨啦。好了编码开始。
很快代码就编写好了:
public class Audio
{
public void Play(string name)
{
Console.WriteLine("Start playing...");
Console.WriteLine($"The song name is: [{name}]");
Console.WriteLine("..........");
}
} [Description("1.2. Simple Factory")]
public class App
{
static void Main()
{
Audio audio=new Audio();
audio.Play("take me to your heart"); Console.ReadKey();
}
}
输出结果如下:
甲方看到软件试用一翻感觉不错很满意,赶紧给开发人员支付了开发费用。将软件投放市场。
很长一段时间,市场放映不错,这个音频播放器市场占有率提高很快。但是有一天一种新的音频格式诞生了,WAV格式的音频文件越来越多了,用户拿到WAV的音乐没办法使用这个播放器播放了,甲方从市场上得到了很多反馈,于是,甲方决定升级这款音频播放器软件使其支持这种新的WAV格式的音频文件。 甲方找到了开发人员要求增加WAV格式文件的播放。开发人员拿到需求后赶紧开始写代码。很快代码写出来了:
public class WmaFile
{
public void Play(string name)
{
Console.WriteLine("Start playing wma file...");
Console.WriteLine($"The song name is: [{name}]");
Console.WriteLine("..........");
}
} public class WavFile
{
public void Play(string name)
{
Console.WriteLine("Start playing wav...");
Console.WriteLine($"The song name is: [{name}]");
Console.WriteLine("..........");
}
} [Description("1.2. Simple Factory")]
public class App
{
static void Main()
{
Console.WriteLine("Please input a or m");
var input = Console.ReadKey();
if (input != null)
{
if (input.Key == ConsoleKey.A)
{
WavFile wav = new WavFile();
wav.Play("take me to your heart.wav");
}
else if (input.Key == ConsoleKey.M)
{
WmaFile audio = new WmaFile();
audio.Play("take me to your heart.wma");
}
} Console.ReadKey();
}
}
运行软件如图:
甲方拿到软件测试一翻,很满意,赶紧投入市场,反响强烈,市场占有率进一步提高。
随着时间的推移一种新的音乐格式出现了,mp3 格式文件的音乐出现了,他的文件占用存储小, 音质好,原来一张光盘只能装5首wma或者wav格式的音乐文件,现在一张光盘可以装30首mp3了。这简直就是一场革命, 但是甲方目前的音频播放播放器不支mp3格式, 甲方的几个竞争对手已经抢先一步支持了mp3的播放,甲方的音频播放器的市场占有率在急剧下降,甲方坐不住了,他来不及从市场上去拿反馈了,赶紧找到开发人员,让开发人员赶紧加班加点开发出支持mp3格式文件,不然他的播放器就要被这个瞬息万变的市场所淘汰。开发人员接到这个命令也是不敢怠慢。赶紧投入工作。
当开发人员打开已经实现的代码时陷入了沉思,再加一个类开发mp3 格式文件的播放功能吗? 如果这样写下去的话代码会变得很难维护和扩展了,现在技术发展这么快,指不定哪一天有冒出一个什么格式,再这样堆积木似的一直写下去自己都不想看自己写的代码了。 于是他决定重构代码,但是怎么重构呢? 突然脑海中闪过了昨天不是在看《设计模式》吗?里面不是有个简单工厂模式吗?这个模式似乎很适合中中场景,好的就用这个模式, 把播放音乐的功能提出一个接口, 然后创建出继承这个接口的各个格式文件的具体播放类,然后在创建一个静态工厂根据客户的音乐文件格式选择创建那个具体的类来播放当前的文件。 有了这个思路那就赶紧动手重构代码吧。 很快代码重构完成了:
public interface IAudio
{
void Play(string name);
} public class Wma : IAudio
{
public void Play(string name)
{
Console.WriteLine("Start playing wma file...");
Console.WriteLine($"The song name is: [{name}.wma]");
Console.WriteLine("..........");
}
}
public class Wav : IAudio
{
public void Play(string name)
{
Console.WriteLine("Start playing wav file...");
Console.WriteLine($"The song name is: [{name}.wav]");
Console.WriteLine("..........");
}
}
public class Mp3 : IAudio
{
public void Play(string name)
{
Console.WriteLine("Start playing mp3...");
Console.WriteLine($"The song name is: [{name}.mp3]");
Console.WriteLine("..........");
}
} public class AudioFactory
{
public static IAudio Create(string songType)
{
IAudio audio;
switch (songType.ToUpper())
{
case "A":
audio = new Wav();
break;
case "M":
audio = new Wma();
break;
case "P":
audio = new Mp3();
break;
default:
throw new ArgumentException("Invalid argument", nameof(songType));
} return audio;
}
} [Description("1.2. Simple Factory")]
public class App
{
static void Main()
{
Console.WriteLine("Please input a or m or p");
var input = Console.ReadKey();
if (input != null)
{
IAudio audio = AudioFactory.Create(input.Key.ToString());
audio.Play("take me to your hert");
} Console.ReadKey();
}
}
输出结果:
好了这个音频播放器支持mp3了, 甲方非常高兴,赶紧把这个新的版本投入市场。甲方通过这次升级挽回了一些他在音频播放市场上的一些颓势。
通过这么一次突然的袭击使开发人员收获不少,最起码学到了一种设计模式(simple facfory)的使用,也是成就满满。 但是开发人员还是心有余悸,如果哪一天突然有出来个什么新的格式是不是又要加班加点的搞上一阵子。 他想如果在不改变这个软件已有代码的情况下,来扩展,比方说,新增了一个类型,但是不需要修改代码通过写一个类或者增加一些配置就可以实现这个完美支持新的格式的音频文件?(这不是OCP原则吗? ) 那么这个问题Simple Factory Pattern 能做到吗? 问题先留着,随着模式学习的深入, 让我们慢慢在后面的学习中去寻找答案。
五、简单工厂模式的优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,客户程序不需要产品的具体生产细节;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,不需要知道产品是怎么创建的。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
- 简单工厂将创建产品的职责放在了单独的静态工厂中去处理和维护一定长度上体现了SRP原则。
六、简单工厂模式的缺点
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中具体产品类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 由于在增加新的产品的时候简单工厂模式必须要修改静态的工厂方法,所以它违背了OCP原则。
七、工厂模式的使用场景
在以下情况下可以使用简单工厂模式:
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
【设计模式】简单工厂模式 Simple Factory Pattern的更多相关文章
- Golang设计模式—简单工厂模式(Simple Factory Pattern)
Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...
- 大白话简单工厂模式 (Simple Factory Pattern)
大白话简单工厂模式 (Simple Factory Pattern) 从买车经历说起 毕业两年,码农张小两口无法忍受挤公交,凌晨起床抢火车票的痛苦,遂计划买车.逛了多家4S店,最终定下日产某车型的轿车 ...
- Net设计模式实例之简单工厂模式(Simple Factory Pattern)
一.简单工厂模式简介(Bref Introduction) 简单工厂模式(Simple Factory Pattern)的优点是,工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类, ...
- 设计模式之简单工厂模式(Simple Factory Pattern)
一.简单工厂模式的由来 所有设计模式都是为解决某类问题而产生的,那么简单工厂模式是为解决什么问题呢?我们假设有以下业务场景: 在一个学生选课系统中,文科生用户选课时,我们要获得文科生的所有课程列表:理 ...
- 设计模式之—简单工厂模式<Simple Factory Pattern >
简单工厂模式结构图: 简单工厂模式以简单的加减乘除运算为例: 运算符类(Operation): namespace CalcTest.Simple_Factory_patterns { class O ...
- 【UE4 设计模式】简单工厂模式 Simple Factory Pattern
概述 描述 又称为静态工厂方法 一般使用静态方法,根据参数的不同创建不同类的实例 套路 创建抽象产品类 : 创建具体产品类,继承抽象产品类: 创建工厂类,通过静态方法根据传入不同参数从而创建不同具体产 ...
- 简单工厂模式(Simple Factory Pattern)
简单工厂模式概述 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类 在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被 ...
- 六个创建模式之简单工厂模式(Simple Factory Pattern)
定义: 定义一个工厂类,它可以根据参数的不同生成对应的类的实例:被创建的类的实例通常有相同的父类.因为该工厂方法尝尝是静态的,所以又被称为静态工厂方法(Static Factory Method) 结 ...
- 【java设计模式】【创建模式Creational Pattern】简单工厂模式Simple Factory Pattern(静态工厂方法模式Static Factory Method Pattern)
public class Test { public static void main(String[] args){ try{ Factory.factory("A").doSt ...
随机推荐
- Jenkins之Log Parse的使用
在初学使用Jenkins的同学们,应该都遇到过通过bash或者cmd命令执行输出的日志,没办法做到对error, warning等状态的分析和统计.在这里就给他介绍一款实现此功能的插件Log Pars ...
- Vuex的初探与实战
1.背景 最近在做一个单页面的管理后台项目,为了提高开发效率,使用了Vue框架来开发.为了使各个部分的功能,独立结构更加清晰,于是就拆分了很多组件,但是组件与组件之间数据共享成了一个问题,父子组件实现 ...
- if条件、while循环、for循环 相关练习
1.实现用户输入用户名和密码,当用户名为 seven 且 密码为 123 时,显示登陆成功,否则登陆失败! while True: name = input('请输入用户名:') psw = inpu ...
- 第一次JVM分析记录:Out of Memory Error (workgroup.cpp:96), pid=6196, tid=139999645685504
tomcat的catalina.out日志报错如下: Exception in thread "http-bio-8081-Acceptor-0" java.lang.OutOfM ...
- 好代码是管出来的——使用Jenkins搭建CI服务器
Jenkins是一个开源的跨平台的CI工具,它可以部署在Windows.Linux等平台上,并且Jenkins提供了非常丰富的插件来帮助完成编译.测试.部署等工作. 本文将介绍在Windows平台上使 ...
- 深入分析Java I/O 工作机制
前言 : I/O 问题是Web 应用中所面临的主要问题之一.而且是任何编程语言都无法回避的问题,是整个人机交互的核心. java 的I/O类操作在java.io 包下,将近80个子类, 大概可以分成 ...
- React 与 React-Native 使用同一个 meteor 后台
meteor 可以快速构建 pc,移动端,桌面端应用. 最大的优点是:数据库的数据发生变化时,可以实时推送到前端,非常适用于实时展示的应用开发. 在 react,react-native 应用中,可以 ...
- 难以理解的AQS(下)
在上一篇博客,简单的说下了AQS的基本概念,核心源码解析,但是还有一部分内容没有涉及到,就是AQS对条件变量的支持,这篇博客将着重介绍这方面的内容. 条件变量 基本应用 我们先通过模拟一个消费者/生产 ...
- Nosql与关系型数据库不同的使用场景
Nosql 1.适合存储非结构化数据存储,数据量且不可预期.如:评论,文章 2.排行榜数据获取,实时更新的数据.如:游戏榜排名,用户投票 3.限时抢购活动.如:淘宝抢购活动 4.反垃圾系统.如:敏感词 ...
- .NET Core微服务之基于Jenkins+Docker实现持续部署(Part 1)
Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.CI, CD 与Jenkins 互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous i ...