C#学习之设计模式:工厂模式
最近研究一下设计模式中工厂模式的应用,在此记录如下:
什么是工厂模式?
工厂模式属于设计模式中的创造型设计模式的一种。它的主要作用是协助我们创建对象,为创建对象提供最佳的方式。减少代码中的耦合程度,方便后期代码的维护。
工厂模式又可以详细细分为简单工厂模式,工厂方法模式和抽象工厂模式。下面我们一一道来。
简单工厂模式
严格意义上来说,简单工厂模式并不能计入设计模式大家族。
因为它并不严格符合设计模式要求的对扩展开放,对修改关闭的设计原则。但是简单工厂模式简单好用,行之有效,在小型项目或简单的代码逻辑中使用它的人并不少。
定义:
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
类图:
其中:
工厂(Creator)角色是 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
抽象产品(Product)角色 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色 是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
通俗理解就是:Creator是工厂类,通过它进行条件选择决定实例化哪一个具体的product,并返回接口Iproduct,客户端直接使用Iproduct即可,无需了解Iproduct到底是什么类。
适用范围和适用场景
- 工厂类负责创建的对象比较少;
- 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
- 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。
优点:
简单直接,易于理解,减少了客户端的条件判断。
缺点:
1.由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的类。
2.如果需要添加新的类,则就需要改变工厂类了。 当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
代码示例
//客户端,客户端调用时只与简单工厂类进行交互,客户端不知道也不关心工厂类返回给它的是哪一个具体的类,只要知道这个类能够完成的功能有哪些即可
class Program
{
static void Main(string[] args)
{
string DataBaseType = "SqlServer";
Factory factory = new Factory(DataBaseType);
factory.IDataBase.Select();
factory.IDataBase.Insert();
Console.ReadKey();
}
}
//数据库接口,相当于图中的Iproduct。通过定义接口,方便客户端知道如何调用工厂方法返回的类
public interface IDataBase
{
void Select();
void Insert();
}
//具体的产品类,满足接口约束之后,完成各自不同的动作,相当于图中的product_A,product_B等
public class Oracle : IDataBase
{
public void Insert()
{
Console.WriteLine("执行Oracle类的Insert");
}
public void Select()
{
Console.WriteLine("执行Oracle类的Select");
}
}
public class Access : IDataBase
{
public void Insert()
{
Console.WriteLine("执行Access类的Insert");
}
public void Select()
{
Console.WriteLine("执行Access类的Select");
}
}
public class SqlServer : IDataBase
{
public void Insert()
{
Console.WriteLine("执行sqlServer类的Insert");
}
public void Select()
{
Console.WriteLine("执行sqlServer类的Select");
}
}
//简单工厂类,通过条件分支判断具体应该实例化哪一个类。相当于图中的Creator
public class Factory
{
public IDataBase IDataBase { get; set; }
public Factory(string DbType)
{
switch (DbType)
{
case "SqlServer":
IDataBase = new SqlServer();
break;
case "Oracle":
IDataBase = new Oracle();
break;
case "Access":
IDataBase = new Access();
break;
}
}
}
简单工厂模式,我非常喜欢用,它足够简单,代码量小,但是有效。通常,我们国土行业内部系统中会出现类似于这样的代码。
class BadCode
{
string GetBadCode(string DataBaseType)
{
string Result = "";
if (DataBaseType == "Oracle")
{
/*此处大量代码(数十行)运算获得从oracle数据库中获取的结果Result*/
}
else if (DataBaseType == "Access")
{
/*此处大量代码(数十行)运算获得从Access数据库中获取的结果Result*/
}
else if (DataBaseType == "SqlServer")
{
/*此处大量代码(数十行)运算获得从SqlServer数据库中获取的结果Result*/
}
return Result;
}
}
这样的代码阅读起来非常费劲,如果想要修改这样的代码,也是非常彷徨的。如果把每个条件分支中的代码稍作整理封装到类中,并提取公共接口,采用简单工厂模式组织的话,代码的清晰度必然有大的提升。
工厂方法模式
简单工厂高效,好用,但是它只适合简单的有固定的待创建对象列表的情况,不然反复修改工厂类中的case分支很麻烦,也不符合软件设计的开闭原则。
通过分析人们发现简单工厂模式主要是工厂类与判断分支耦合了,那么就从这个地方下手,根据依赖倒转原则把工厂类抽象出一个接口,将具体的实例化方法延迟到接口的子类来实现。工厂方法模式就出现了。
定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到其子类
类图:
它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品
抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。
抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
抽象工厂不再负责直接创建具体的对象,它只定义了一个生成抽象产品的接口,由其子类去生产具体的产品。这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
简单的说就是:用户调用Creator接口,并实例化为一个具体的Concrete Creator,Concrete Creator返回一个Product给用户使用。用户无需知道Product是哪一个具体的Concrete Product。Product也不关心是谁在调用它。在需要更换Concrete Creator时,因为采用了依赖注入的原则设计,也只需要将Creator实例化为另一个Concrete Creator,其他地方无需修改。
适用范围和适用场景
工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。 适用于单个产品分类多个产品的情况,或者用户知道自己需要实例化哪一个具体的类,但是这个具体的类可能需要经常变化的情况。
代码示例
class Program
{
static void Main(string[] args)
{
IFactory factory = new SqlServerFactory();
IDataBase dataBase = factory.CreateDataBase();
dataBase.Insert();
dataBase.Select();
Console.ReadKey();
}
}
public interface IFactory
{
IDataBase CreateDataBase();
}
public interface IDataBase
{
void Select();
void Insert();
}
public class OracleFactory : IFactory
{
public IDataBase CreateDataBase()
{
return new Oracle();
}
}
public class AccessFactory : IFactory
{
public IDataBase CreateDataBase()
{
return new Access();
}
}
public class SqlServerFactory : IFactory
{
public IDataBase CreateDataBase()
{
return new SqlServer();
}
}
public class SqlServer : IDataBase
{
public void Insert()
{
Console.WriteLine("执行sqlServer类的Insert");
}
public void Select()
{
Console.WriteLine("执行sqlServer类的Select");
}
}
public class Oracle : IDataBase
{
public void Insert()
{
Console.WriteLine("执行Oracle类的Insert");
}
public void Select()
{
Console.WriteLine("执行Oracle类的Select");
}
}
public class Access : IDataBase
{
public void Insert()
{
Console.WriteLine("执行Access类的Insert");
}
public void Select()
{
Console.WriteLine("执行Access类的Select");
}
}
优点:
1.需要增加待生成的子类对象时,只需要添加子类和相应的工厂即可,无需修改已有代码,符合开闭设计原则。
2.在于通过依赖注入可以很轻松的实现具体产品类的更换,而无需修改大量的代码,如果配合反射甚至可以做到无需修改代码。
缺点:
将必要的判断移到了客户端调用方,导致如果需要根据条件判断初始化哪个子类的条件判断暴露给了调用方。因此一般工厂方法模式适用于已知需要初始化的类,但此类可能经常需要变化的情况。当然这个缺陷可以通过反射来进行解决。
抽象工厂模式
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
定义
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
类图:
核心角色为:
抽象工厂(AbstractFactory),是抽象工厂模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。与工厂方法模式不同的是,抽象工厂模式的抽象工厂定义了至少两组不同的抽象产品的创建方法。
具体工厂(Concrete Creator):这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。与工厂方法模式不同的是,抽象工厂模式的具体工厂实现了至少两组不同的抽象产品的创建方法。
抽象产品(AbstractProduct)角色:抽象工厂模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品(ConcreteProduct)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
适用范围和适用场景
1.当需要创建产品家族,或者需要想让创建的产品集合起来时使用。
2.当系列产品不固定,在以后可能会增加整个系列产品时使用。
优点:
抽象工厂模式的优点和工厂方法模式的优点基本类似,但是抽象工厂模式更擅长于处理多个产品线的情况,通俗的说就是当需要同时初始化两种及以上不同类别的对象的时候,使用抽象工厂模式相对于工厂方法模式可以少写很多具体工厂类,能够更好的管理代码。
缺点:
抽象工厂模式不是万能的。抽象工厂模式的缺点和工厂方法模式的却点也基本类似。但是它还有一个独有的缺点:它特别不适用于产品系列不固定的情况,它可以很容易的添加ConcreteProduct以及Concrete Creator,但是它在添加AbstractProduct的时候需要修改大量的现有类,不符合开闭原则。
代码示例
/// <summary>
/// 抽象工厂,两条“产品线”,采矿权和探矿权
/// </summary>
public interface IFactory
{
ICKQ CreateCKQ();
ITKQ CreateTKQ();
}
public class XLFactory : IFactory
{
public ICKQ CreateCKQ()
{
return new CKQXL();
}
public ITKQ CreateTKQ()
{
return new TKQXL();
}
}
public class BGFactory : IFactory
{
public ICKQ CreateCKQ()
{
return new CKQBG();
}
public ITKQ CreateTKQ()
{
return new TKQBG();
}
}
/// <summary>
/// 采矿权“产品线”下的“产品”
/// </summary>
public interface ICKQ
{
//签收
void Sign();
//办理完成
void Finish();
//保存
void Save();
}
/// <summary>
/// 探矿权“产品线”下的“产品”
/// </summary>
public interface ITKQ
{
void Sign();
void Finish();
void Save();
}
public class CKQXL : ICKQ
{
public void Finish()
{
Console.WriteLine("实现采矿权新立的办理完成");
}
public void Save()
{
Console.WriteLine("实现采矿权新立的保存");
}
public void Sign()
{
Console.WriteLine("实现采矿权新立的签收");
}
}
public class CKQBG : ICKQ
{
public void Finish()
{
Console.WriteLine("实现采矿权变更的办理完成");
}
public void Save()
{
Console.WriteLine("实现采矿权变更的办理完成");
}
public void Sign()
{
Console.WriteLine("实现采矿权变更的办理完成");
}
}
public class TKQBG: ITKQ
{
public void Finish()
{
Console.WriteLine("实现探矿权变更的办理完成");
}
public void Save()
{
Console.WriteLine("实现探矿权变更的保存");
}
public void Sign()
{
Console.WriteLine("实现探矿权变更的签收");
}
}
public class TKQXL:ITKQ
{
public void Finish()
{
Console.WriteLine("实现探矿权新立的办理完成");
}
public void Save()
{
Console.WriteLine("实现探矿权新立的保存");
}
public void Sign()
{
Console.WriteLine("实现探矿权新立的签收");
}
}
static void Main(string[] args)
{
IFactory factory = new XLFactory();
ICKQ ckq = factory.CreateCKQ();
ckq.Save();
ckq.Sign();
ckq.Finish();
ITKQ tkq = factory.CreateTKQ();
tkq.Save();
tkq.Sign();
tkq.Finish();
Console.ReadKey();
}
三种工厂优劣势比较及总结
一句话来说就是:工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结构。简单工厂模式简单好用,包含判断逻辑,但不适合大型复杂情况。
个人理解:最好用的是简单工厂,最难懂的是抽象工厂。很多情况下,我们甚至可以将简单工厂模式与工厂方法模式和抽象工厂模式结合起来使用,可能是一个最好的折中的方式。
代码地址:
https://coding.net/u/wenpeng/p/DesignPattern/git
引用说明
- https://baike.baidu.com/item/简单工厂模式/8801727?fr=aladdin
- https://baike.baidu.com/item/设计模式/1212549?fr=aladdin
- http://blog.csdn.net/carson_ho/article/details/52343584
- 《大话设计模式》 https://book.douban.com/subject/2334288/
- https://baike.baidu.com/item/工厂方法模式
- https://baike.baidu.com/item/抽象工厂模式
- https://www.cnblogs.com/jenkinschan/p/5712874.html
C#学习之设计模式:工厂模式的更多相关文章
- .NET设计模式: 工厂模式
.NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html .NET设计模式(1): ...
- 【设计模式】Java设计模式 -工厂模式
[设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...
- Java设计模式学习记录-简单工厂模式、工厂方法模式
前言 之前介绍了设计模式的原则和分类等概述.今天开启设计模式的学习,首先要介绍的就是工厂模式,在介绍工厂模式前会先介绍一下简单工厂模式,这样由浅入深来介绍. 简单工厂模式 做法:创建一个工厂(方法或类 ...
- 学习:java设计模式—工厂模式
一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类: 1)简单工厂模式(Simple Facto ...
- 设计模式学习心得<抽象工厂模式 Abstract Factory>
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...
- [GeekBand] 设计模式——工厂模式学习笔记
本文参考文献:GeekBand课堂内容,授课老师:李建忠 :大话设计模式 其余的模式方法请自行查看Geekband相关课程,在此不累述. 这周的课题是: 针对DrawingSystem中的基类Sha ...
- Java设计模式(学习整理)---工厂模式
1.工厂模式 1.1 为什么使用工厂模式? 因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时 ...
- Java设计模式学习记录-抽象工厂模式
前言 上篇博客介绍了简单工厂模式和工厂方法模式,这次介绍抽象工厂模式,抽象工厂模式和工厂方法模式的区别在于需要创建对象的复杂程度上. 抽象工厂模式 抽象工厂模式是围绕着一个超级工厂创建其他工厂.这个超 ...
- 设计模式——工厂模式(Factory)
要想正确理解设计模式,首先必须明白它是为了解决什么问题而提出来的. 设计模式学习笔记 --Shulin 转载请注明出处:http://blog.csdn.net/zhshulin 1.概念 工厂模式定 ...
- 一张图搞定Java设计模式——工厂模式! 就问你要不要学!
小编今天分享的内容是Java设计模式之工厂模式. 收藏之前,务必点个赞,这对小编能否在头条继续给大家分享Java的知识很重要,谢谢!文末有投票,你想了解Java的哪一部分内容,请反馈给我. 获取学习资 ...
随机推荐
- 前端测试框架Jest系列教程 -- 简介
写在前面: 随着互联网日新月异的发展,用户对于页面的美观度,流畅度以及各方面的体验有了更高的要求,我们的网页不再是简单的承载文字,图片等简单的信息传递给用户,我们需要的是更加美观的页面展示,更快的浏览 ...
- javaWeb开发中关于eclipse等ide重新部署或重启项目等原因造成上传文件丢失问题解决方案
在开发项目时,有时候需要用到上传功能,比如头像上传等,其文件会保存到服务器中.但是我发现在用eclipse做项目的过程中,每次重新部署项目,原来上传的文件就会丢失. 其原因是因为每次项目修改后,ecl ...
- mysql 系统性浅聊 myisam 存储引擎【原创】
>>思维导图 >>介绍 mysql中的存储引擎都是以插件的形式存在,目前用的最多存储引擎就是innodb和myisam.MySQL5.5.5以后(包括5.5.5)默认使用Inn ...
- iOS 视频播放方式整理
初衷 多媒体这整个系列的文章自己也准备好开始整理了,先从视频音频最简单也是最常用的播放出发慢慢的往下深究,探索到底层的编码解码等等,这篇文章就从视频的播放这个最简单的说起. iOS的视频播放方式有几种 ...
- YiShop_最全微信营销涨粉技巧
在我们开始推广企业订阅号之前,我们必须思考微信营销策略,客户通过微信可以获得什么?企业通过微信可以得到什么?微分销专家建议企业微信定位为互动.服务工具,因为获取一个粉丝很难,可是失去一个粉丝,却是一件 ...
- NGUI_Atlas
二.NGUI的图集制作: 1.概述: 将导入的图片资源全部制成一张图集,可以节约资源,当制成图集后,就可以将导入的图片资源进行删除, 再后续的操作直接使用图集中的图片即可,NGUI自带的Atlas M ...
- AspNet Core :创建自定义 EF Core 链接数据库
这两天比较忙,写的会慢一点. 我们以控制台演示 EF Core的链接数据库 首先创建控制台程序 创建数据上下文类 EntityTable /// <summary> /// 继承 DbCo ...
- ubuntu更换开机动画
ubuntu更换启动动画 作为一个个用linux作为桌面环境,并且完全替代了windows的来说,怎么折腾好看,是一个重要的问题,而Ubuntu的开机动画,那紫色的画面,ubuntu那几个大字,实在丑 ...
- Linux下实现CAD数据的导出
近期公司项目涉及到CAD的导出,而且部署服务器申请不到Windows下的,所以技术上的解决方案就是寻求如何在Linux下实现CAD数据的导出. 于是百度了一下,找了几个相关库和软件. 1.dxflib ...
- 逐步搭建Lamp环境之vim的三种模式以及基本命令
在Linux中vim的三种模式分别为:命令模式.末行模式.编辑模式.以下是三者的关系图: 三种模式的彼此切换: 命令模式是vim中的默认模式. 命令模式切换至末行模式: 使用英文冒号(:). 末行模式 ...