最近研究一下设计模式中工厂模式的应用,在此记录如下:

  什么是工厂模式?

  工厂模式属于设计模式中的创造型设计模式的一种。它的主要作用是协助我们创建对象,为创建对象提供最佳的方式。减少代码中的耦合程度,方便后期代码的维护。

  工厂模式又可以详细细分为简单工厂模式,工厂方法模式和抽象工厂模式。下面我们一一道来。

  简单工厂模式

  严格意义上来说,简单工厂模式并不能计入设计模式大家族。

  因为它并不严格符合设计模式要求的对扩展开放,对修改关闭的设计原则。但是简单工厂模式简单好用,行之有效,在小型项目或简单的代码逻辑中使用它的人并不少。

  定义:

简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。

  类图:

  其中:

  工厂(Creator)角色是 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  抽象产品(Product)角色 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
  具体产品(Concrete Product)角色 是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

  通俗理解就是:Creator是工厂类,通过它进行条件选择决定实例化哪一个具体的product,并返回接口Iproduct,客户端直接使用Iproduct即可,无需了解Iproduct到底是什么类。

  适用范围和适用场景

  1. 工厂类负责创建的对象比较少;
  2. 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
  3. 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。

  优点:

    简单直接,易于理解,减少了客户端的条件判断。

    缺点:

  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

引用说明

  1. https://baike.baidu.com/item/简单工厂模式/8801727?fr=aladdin
  2. https://baike.baidu.com/item/设计模式/1212549?fr=aladdin
  3. http://blog.csdn.net/carson_ho/article/details/52343584
  4. 《大话设计模式》 https://book.douban.com/subject/2334288/
  5. https://baike.baidu.com/item/工厂方法模式
  6. https://baike.baidu.com/item/抽象工厂模式
  7. https://www.cnblogs.com/jenkinschan/p/5712874.html

C#学习之设计模式:工厂模式的更多相关文章

  1. .NET设计模式: 工厂模式

    .NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html   .NET设计模式(1): ...

  2. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  3. Java设计模式学习记录-简单工厂模式、工厂方法模式

    前言 之前介绍了设计模式的原则和分类等概述.今天开启设计模式的学习,首先要介绍的就是工厂模式,在介绍工厂模式前会先介绍一下简单工厂模式,这样由浅入深来介绍. 简单工厂模式 做法:创建一个工厂(方法或类 ...

  4. 学习:java设计模式—工厂模式

    一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类: 1)简单工厂模式(Simple Facto ...

  5. 设计模式学习心得<抽象工厂模式 Abstract Factory>

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...

  6. [GeekBand] 设计模式——工厂模式学习笔记

     本文参考文献:GeekBand课堂内容,授课老师:李建忠 :大话设计模式 其余的模式方法请自行查看Geekband相关课程,在此不累述. 这周的课题是: 针对DrawingSystem中的基类Sha ...

  7. Java设计模式(学习整理)---工厂模式

    1.工厂模式 1.1 为什么使用工厂模式? 因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时 ...

  8. Java设计模式学习记录-抽象工厂模式

    前言 上篇博客介绍了简单工厂模式和工厂方法模式,这次介绍抽象工厂模式,抽象工厂模式和工厂方法模式的区别在于需要创建对象的复杂程度上. 抽象工厂模式 抽象工厂模式是围绕着一个超级工厂创建其他工厂.这个超 ...

  9. 设计模式——工厂模式(Factory)

    要想正确理解设计模式,首先必须明白它是为了解决什么问题而提出来的. 设计模式学习笔记 --Shulin 转载请注明出处:http://blog.csdn.net/zhshulin 1.概念 工厂模式定 ...

  10. 一张图搞定Java设计模式——工厂模式! 就问你要不要学!

    小编今天分享的内容是Java设计模式之工厂模式. 收藏之前,务必点个赞,这对小编能否在头条继续给大家分享Java的知识很重要,谢谢!文末有投票,你想了解Java的哪一部分内容,请反馈给我. 获取学习资 ...

随机推荐

  1. 垃圾陷阱洛谷dp

    题目描述 卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中.“垃圾井”是农夫们扔垃圾的地方,它的深度为D(2<=D<=100)英尺. 卡门想把垃圾堆起来,等到 ...

  2. 利用模板template动态渲染jsp页面

    一.场景 在js中写html简直是噩梦,刚进新公司,在codereview的时候得知可以通过将html模板写在jsp页面,然后由js调取模板,利用replace()方法替换传值的方式避免在js中拼接h ...

  3. SQL基础教程读书笔记-1

    查询基础 2.2 算数运算符和比较运算符 2.2.1算数运算符 + - * / 需要注意NULL 5 + NULL 10 - NULL 1 * NULL 4 / NULL NULL / 9 NULL ...

  4. coursera普林斯顿算法课part1里Programming Assignment 2最后的extra challenge

    先附上challenge要求: 博主最近在刷coursera普林斯顿大学算法课part1部分的作业,Programming Assignment2最后的这个extra challenge当初想了一段时 ...

  5. 解决failed to push some refs to git

    Administrator@PC-20150110FGWU /K/cocos2d/yc (master) $ git push -u origin master To git@github.com:y ...

  6. windows环境Caffe安装配置步骤(无GPU)及mnist训练

    在硕士第二年,义无反顾地投身到了深度学习的浪潮中.从之前的惯性导航转到这个方向,一切从头开始,在此,仅以此文记录自己的打怪之路. 最初的想法是动手熟悉Caffe,考虑到直接上手Ubuntu会有些难度, ...

  7. Android后台执行的定时器实现

    Android后台运行定时器,方便我们运行定位跟踪等任务需求. 以下简要说明实现Android后台定时器的要点, 文章末尾能够下载到project代码,可直接编译运行. AndroidManifest ...

  8. 设置应用栏(Setting Up the App Bar)

    今天星期五,刚从体育场打完球回来,洗了洗脚.明天还要继续上班,也是非常艰难.近期我的小腰有点不舒服,就早点睡觉歇息. 所以今天就简单的翻译一篇Android官方站点上的文章,我会加一些补充. 原文地址 ...

  9. MySQL 删除数据库中反复数据(以部分数据为准)

    delete from zqzrdp where tel  in (select min(dpxx_id) from  zqzrdp  group by tel  having count(tel)& ...

  10. xcode 6 改动组织及开发人员

    搞个ios 开发死去活来的,各个地方说的都不一样,defaults write 不好用 在xcode6中 改动方法例如以下 改动create by __FULLUSERNAME___ 部分 Syste ...