C#设计模式学习笔记:(2)工厂方法模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7567880.html,记录一下学习过程以备后续查用。
一、引言
接上一篇C#设计模式学习笔记:简单工厂模式(工厂方法模式前奏篇),通过简单工厂模式的了解,它的缺点就是随着需求的变化我们要不停地修改工厂里
面的方法的代码,需求变化越多,里面的if--else也越多,这样就会造成简单工厂的实现逻辑过于复杂。
依设计原则里的开闭原则--对增加代码开放,对修改代码关闭,我们不能总是这样修改简单工厂里面的方法。
下面看看工厂方法模式是如何解决该问题的?
二、工厂方法模式介绍
工厂方法模式:英文名称--Factory Method Pattern;分类--创建型。
2.1、动机(Motivate)
在软件系统的构建过程中,经常面临着“某个对象”的创建工作:由于需求的变化,这个对象(的具体实现)经常面临着剧烈的变化,但是它却拥有比
较稳定的接口。
如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”的变化,从而保持系统中“其他依赖对象的对象”不随着需求改变而改变?
2.2、意图(Intent)
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到子类。--《设计模式》GoF
2.3、结构图(Structure)
2.4、模式的组成
从上图可以看出,在工厂方法模式的结构图有以下角色:
1)抽象工厂角色(Creator):充当抽象工厂角色,定义工厂类所具有的基本的操作,任何具体工厂都必须继承该抽象类。
2)具体工厂角色(ConcreteCreator):充当具体工厂角色,该类必须继承抽象工厂角色,实现抽象工厂定义的方法,用来创建具体产品。
3)抽象产品角色(Product):充当抽象产品角色,定义了产品类型所有具有的基本操作,具体产品必须继承该抽象类。
4)具体产品角色(ConcreteProduct):充当具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类创建,它们之间有一一对应的关系。
2.5、工厂方法模式的具体实现
面向对象设计三大原则:
1)哪里有变化就封装哪里。
2)面向抽象编程,细节和高层实现都要依赖抽象。
3)多组合,少继承。
这三大原则是最根本的原则,学习设计模式必须以这三个原则为基点,否则都是枉然。根据这三大原则又衍生出来6个具体的原则,分别是单一职责
原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则。
既然工厂类有变化,我们就封装它,面向抽象编程。我们先抽象出一个工厂基类,然后每个需求都实现一个具体的工厂类,这样我们就符合了开闭原
则,让一个工厂生产一款产品,并一一对应。具体产品的创建推迟到子类中,此时工厂类(基类)不再负责所有产品的创建,而只是给出具体工厂必须
实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,也就克服了简单工厂模式中缺点。
下面是工厂方法模式的实现代码:
- class Program
- {
- /// <summary>
- /// 汽车抽象类
- /// </summary>
- public abstract class Car
- {
- //开始行驶
- public abstract void Go();
- }
- /// <summary>
- /// 红旗汽车
- /// </summary>
- public class HongQiCar : Car
- {
- public override void Go()
- {
- Console.WriteLine("红旗汽车生产中。");
- }
- }
- /// <summary>
- /// 奥迪汽车
- /// </summary>
- public class AoDiCar : Car
- {
- public override void Go()
- {
- Console.WriteLine("奥迪汽车生产中。");
- }
- }
- /// <summary>
- /// 抽象工厂类
- /// </summary>
- public abstract class Factory
- {
- //工厂方法
- public abstract Car CreateCar();
- }
- /// <summary>
- /// 红旗汽车工厂类
- /// </summary>
- public class HongQiCarFactory : Factory
- {
- /// <summary>
- /// 负责生产红旗汽车
- /// </summary>
- /// <returns></returns>
- public override Car CreateCar()
- {
- return new HongQiCar();
- }
- }
- /// <summary>
- /// 奥迪汽车工厂类
- /// </summary>
- public class AoDiCarFactory : Factory
- {
- /// <summary>
- /// 负责创建奥迪汽车
- /// </summary>
- /// <returns></returns>
- public override Car CreateCar()
- {
- return new AoDiCar();
- }
- }
- static void Main(string[] args)
- {
- #region 工厂方法模式
- //初始化创建汽车的两个工厂
- Factory hongQiCarFactory = new HongQiCarFactory();
- Factory aoDiCarFactory = new AoDiCarFactory();
- //生产一辆红旗汽车
- Car hongQi = hongQiCarFactory.CreateCar();
- hongQi.Go();
- //生产一辆奥迪汽车
- Car aoDi = aoDiCarFactory.CreateCar();
- aoDi.Go();
- Console.Read();
- #endregion
- }
- }
运行结果如下:
使用工厂方法实现的系统,如果系统需要添加新产品时,我们可以利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不需要做任
何改动。假如我们想生产奔驰车,我们只需从Car抽象类下继承一个BenChiCar类、在Factory抽象类下继承一个“奔驰”的工厂类BenChiCarFactory就可
以实现了:
- class Program
- {
- /// <summary>
- /// 汽车抽象类
- /// </summary>
- public abstract class Car
- {
- //开始行驶
- public abstract void Go();
- }
- /// <summary>
- /// 红旗汽车
- /// </summary>
- public class HongQiCar : Car
- {
- public override void Go()
- {
- Console.WriteLine("红旗汽车生产中。");
- }
- }
- /// <summary>
- /// 奥迪汽车
- /// </summary>
- public class AoDiCar : Car
- {
- public override void Go()
- {
- Console.WriteLine("奥迪汽车生产中。");
- }
- }
- /// <summary>
- /// 奔驰汽车
- /// </summary>
- public class BenChiCar : Car
- {
- public override void Go()
- {
- Console.WriteLine("奔驰汽车生产中。");
- }
- }
- /// <summary>
- /// 抽象工厂类
- /// </summary>
- public abstract class Factory
- {
- //工厂方法
- public abstract Car CreateCar();
- }
- /// <summary>
- /// 红旗汽车工厂类
- /// </summary>
- public class HongQiCarFactory : Factory
- {
- /// <summary>
- /// 负责生产红旗汽车
- /// </summary>
- /// <returns></returns>
- public override Car CreateCar()
- {
- return new HongQiCar();
- }
- }
- /// <summary>
- /// 奥迪汽车工厂类
- /// </summary>
- public class AoDiCarFactory : Factory
- {
- /// <summary>
- /// 负责创建奥迪汽车
- /// </summary>
- /// <returns></returns>
- public override Car CreateCar()
- {
- return new AoDiCar();
- }
- }
- /// <summary>
- /// 奔驰汽车工厂类
- /// </summary>
- public class BenChiCarFactory : Factory
- {
- /// <summary>
- /// 负责生产奔驰汽车
- /// </summary>
- /// <returns></returns>
- public override Car CreateCar()
- {
- return new BenChiCar();
- }
- }
- static void Main(string[] args)
- {
- #region 工厂方法模式
- //初始化创建汽车的两个工厂
- Factory hongQiCarFactory = new HongQiCarFactory();
- Factory aoDiCarFactory = new AoDiCarFactory();
- Factory benChiCarFactory = new BenChiCarFactory();
- //生产一辆红旗汽车
- Car hongQi = hongQiCarFactory.CreateCar();
- hongQi.Go();
- //生产一辆奥迪汽车
- Car aoDi = aoDiCarFactory.CreateCar();
- aoDi.Go();
- //生产一辆奔驰汽车
- Car benChi = benChiCarFactory.CreateCar();
- benChi.Go();
- Console.Read();
- #endregion
- }
- }
运行结果如下:
三、Factory Method模式的几个要点
Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱;
Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦
合关系;
Factory Method模式解决“单个对象”的需求变化;
AbstractFactory模式解决“系列对象”的需求变化;
Builder模式解决“对象部分”的需求变化;
3.1、工厂方法模式的优点
1)在工厂方法中,用户只需要知道所要产品的具体工厂,无须关心具体的创建过程,甚至不需要具体产品类的类名。
2)在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。
3.2、工厂方法模式的缺点
1)每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加
了系统具体类的依赖,这并不是什么好事。
3.3、工厂方法模式的使用场合
1)一个类不知道它所需要的对象的类。在工厂方法模式中,我们不需要具体产品的类名,我们只需要知道创建它的具体工厂即可。
2)一个类通过其子类来指定创建那个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,由其子类来确定具体要创建的对
象。在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
3)将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。
四、.NET中实现了工厂方法的类
.NET类库中也有很多实现了工厂方法的类。例如在Asp.net中,处理程序对象是具体用来处理请求,当我们请求一个*.aspx的文件时,此时会映射到
System.Web.UI.PageHandlerFactory类上进行处理,而对*.ashx的请求将映射到System.Web.UI.SimpleHandlerFactory类中(这两个类都是继承于
IHttpHandlerFactory接口的),关于这点说明我们可在“C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\Web.Config”文件中找到相关定义:
<add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True"/>
<add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True"/>
<add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True"/>
上面只摘选了部分配置文件,有时间大家可以自己去研究一下。
下面我们具体看下工厂方法模式在Asp.net中是如何实现的?对一个Index.aspx页面发出请求时,将会调用PageHandlerFactory中GetHandler方法来
创建一个Index.aspx对象,它们之间的类图关系如下:
五、总结
每种模式都有自己的使用场合,切记,如果使用错误,还不如不用。工厂方法模式通过面向对象编程中的多态性来将对象的创建延迟到具体工厂中,
从而解决了简单工厂模式中存在的问题,也很好地符合了开放封闭原则(即对扩展开发,对修改封闭)。
学习设计模式我们一定要谨记设计模式的几大原则,否则是徒劳无功的。就像学务工一样,我们要记心法。几大原则就像独孤九剑的剑诀,学会了,
变化无穷。
C#设计模式学习笔记:(2)工厂方法模式的更多相关文章
- Java设计模式学习笔记(三) 工厂方法模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...
- Java设计模式(二) 工厂方法模式
本文介绍了工厂方法模式的概念,优缺点,实现方式,UML类图,并介绍了工厂方法(未)遵循的OOP原则 原创文章.同步自作者个人博客 http://www.jasongj.com/design_patte ...
- C#设计模式学习笔记:(5)原型模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,记录一下学习过程以备后续查用. 一.引言 很多人说原型设计模式会节省机器内存,他们说 ...
- 设计模式(三)工厂方法模式(Factory Pattern)
一.引言 在简单工厂模式中讲到简单工厂模式的缺点,有一点是——简单工厂模式系统难以扩展,一旦添加新产品就不得不修改简单工厂方法,这样就会造成简单工厂的实现逻辑过于复杂,然而本专题介绍的工厂方法模式可以 ...
- 设计模式(2)工厂方法模式(Factory Method)
设计模式(0)简单工厂模式 设计模式(1)单例模式(Singleton) 源码地址 0 工厂方法模式简介 0.0 工厂方法模式定义 工厂方法模式是在简单工厂模式基础上,为解决更复杂的对象创建问题而衍生 ...
- JS学习十七天----工厂方法模式
工厂方法模式 前言 今天自己看了一下自己写的部分博客,发现写的好丑....開始注意自己的排版!!可是偏亮也不是一朝一夕就完毕的,我尽量让它美丽一点.....每天美丽一点点 正文 工厂方法模式是一种实现 ...
- java23种设计模式(一)工厂方法模式
在说工厂方法模式之前,先了解一下简单工厂模式.工厂方法模式其实是在简单工厂上面做了一些增强. 简单工厂模式:有一个专门的类来生产其他类的实例,生产的这些实例有一个共同父类.这个跟我们的多态有一点像. ...
- Java设计模式(四)工厂方法模式
定义与类型 定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行. 类型:创建型 适用场景 创建对象需要大量重复的代码 客户端(应用层)不依赖于产 ...
- 【python设计模式-创建型】工厂方法模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻 ...
- 设计模式C#合集--工厂方法模式
简单工厂,代码: public interface ISpeak { public void Say(); } public class Hello : ISpeak { public void Sa ...
随机推荐
- Docker深入浅出系列 | Image实战演练
目录 课程目标 Container与Image核心知识回顾 制作Docker Image的两种方式 Dockerfile常用指令 Image实战篇 通过Dockerfile制作Image 通过Dock ...
- axure如何实现提示框3s后自动消失
本示例基于axure8 实现 1.先做两个元件,一个按钮,一个提示框 2.将弹框“发布成功提示”设置为,页面载入时隐藏,这样预览页面时,该弹框是隐藏状态 3.给按钮添加交互样式,如下: 4.预览,点击 ...
- C#系列之基础知识点(一)
知识点一:VS启动方法 第一种:双击图标 第二种:window+R——调出cmd,输入devenu properties 属性的意思 知识点二:后缀名解释 .sln 解决方案文件:包含整个解决方案 ...
- 讲一下java,c语言,c+和c++都是干嘛的,他们运行的软件都是哪些
讲一下java,c语言,c+和c++都是干嘛的,他们运行的软件都是哪些 都是用于开发软件的,用于不同的方面.比如,淘宝的后台,是java做的.而腾讯的qq的后台服务器,是c和c++的.暴雪游戏的后台服 ...
- 《Android Studio实战 快速、高效地构建Android应用》--三、重构代码
要成为高效的Android程序员,需要头脑灵活,能够在开发.调试和测试的过程中重构代码,重构代码最大的风险是可能会引入意外的错误,Android Studio通过分析某些具有危险性的重构操作来降低风险 ...
- Vmware初次安装虚拟机需要做的一些网络配置——nat模式与桥接模式
一.本机设置: 1.首先点击图中红线区域: 2.点击网络适配器 3.会出现如下区域: 4.网卡开启后设置ip地址,此处设置的ip和本机的ip没有关系,设置成你虚拟机里面运行的计算机需要的ip地址网段 ...
- 导弹拦截(dp复习)
题目描述:求最长下降子序列的长度 in: 71 7 3 5 9 4 8 out: 4 解释:1 3 4 8/1 3 5 8 长度为四 这让我想到了hdu上导弹拦截那道题,是求最长上升子序列长度 其转移 ...
- 详解python的装饰器decorator
装饰器本质上是一个python函数,它可以让其它函数在不需要任何代码改动的情况下增加额外的功能. 装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志,性能测试,事务处理,缓存, ...
- CCF_ 201512-3_画图
直接模拟就行了,注意坐标系方向与平常数组不一样,填充操作用深搜和广搜都可以,这里用了广搜. #include<iostream> #include<cstdio> #inclu ...
- windows系统快速安装pytorch的详细教程
pip和conda的区别 之前一直使用conda和pip ,有时候经常会两者混用.但是今天才发现二者装的东西不是在一个地方的,所以发现有的东西自己装了,但是在运行环境的时候发现包老是识别不了,一直都特 ...