之因此写这个系列,是了为了本身更好的理解设计模式,也为新手提供一些帮助,我都是用最简单的、最生活化的实例来讲明。在上一篇文章中讲解了单例模式,今天就给你们讲一个比较简单的模式——简单工厂模式。可是这里要说明的是,这个模式并不属于GoF23里面的设计模式,其实他属于一个过渡的模式,这个模式是为了引出下一篇要将的模式:工厂模式。编程
不管是简单工厂仍是复杂工厂,首先它们都是工厂,工厂是干什么的呢? 在现实生活中的工厂是负责生产产品的,产品或者能够食用或者可使用,能够为咱们提供功能或者补充能量,这个产品是有用的,真实存在的。那么在面向对象的软件设计中提到的工厂是什么意思呢?既然也是工厂,确定也是生产东西的,只不过这个东西在这里必定是一个对象的实例,并且这个东西咱们可使用,因此在软件设计中工厂的概念就是指能够生产某个类型对象实例的一个类型。就像咱们要吃饼干不用本身作,去超市买就行了,由于有工厂已经把饼干生产好了,生产是工厂的任务,吃是咱们本身的事情,两个互不干涉,多好啊。。。在软件设计中,若是之后咱们要使用某个类型的实例,就告诉工厂就行,他就会给咱们想要的实例对象,具体工厂是怎么生产咱们就不用管了,咱们也省事了,也就是所谓的解耦了。咱们日常编程中,当使用”new”关键字建立一个对象时,此时该类就依赖与这个对象,也就是他们之间的耦合度比较高,当需求变化时,咱们就不得不去修改此类的源码,此时咱们能够运用面向对象(OO)的很重要的原则去解决这一的问题,该原则就是——封装变化点,既然要封装改变点,天然也就要找到变化的代码,而后把变化的代码用类来封装,这样的一种思路也就是咱们简单工厂模式的实现方式了。下面经过一个现实生活中的例子来引出简单工厂模式。设计模式
生活中,免不了要常常在外面吃饭,固然咱们也能够本身在家作饭吃,可是本身作饭吃麻烦,由于又要本身买菜,然而,出去吃饭就彻底没有这些麻烦的,咱们只须要到餐馆点菜就能够了,买菜的事情就交给餐馆作就能够了,这里餐馆就充当简单工厂的角色,下面让咱们看看现实生活中的例子用代码是怎样来表现的。ide
本身作饭的状况:函数
1 /// <summary> 2 /// 本身作饭的状况 3 /// 没有简单工厂,客户想吃什么菜只能本身炒的 4 /// </summary> 5 public class Customer 6 { 7 /// <summary> 8 /// 烧菜方法 9 /// </summary> 10 /// <param name="type"></param> 11 /// <returns></returns> 12 public static Food Cook(string type) 13 { 14 Food food = null; 15 // 客户A说:我想吃西红柿炒蛋怎么办? 16 // 客户B说:那你就本身烧啊 17 // 客户A说: 好吧,那就本身作吧 18 if (type.Equals("西红柿炒蛋")) 19 { 20 food = new TomatoScrambledEggs(); 21 } 22 // 我又想吃土豆肉丝, 这个仍是得本身作 23 // 我以为本身作好累哦,若是能有人帮我作就行了? 24 else if (type.Equals("土豆肉丝")) 25 { 26 food = new ShreddedPorkWithPotatoes(); 27 } 28 return food; 29 } 30 31 static void Main(string[] args) 32 { 33 // 作西红柿炒蛋 34 Food food1 = Cook("西红柿炒蛋"); 35 food1.Print(); 36 37 Food food2 = Cook("土豆肉丝"); 38 food1.Print(); 39 40 Console.Read(); 41 } 42 } 43 /// <summary> 44 /// 菜抽象类 45 /// </summary> 46 public abstract class Food 47 { 48 // 输出点了什么菜 49 public abstract void Print(); 50 } 51 52 /// <summary> 53 /// 西红柿炒鸡蛋这道菜 54 /// </summary> 55 public class TomatoScrambledEggs : Food 56 { 57 public override void Print() 58 { 59 Console.WriteLine("一份西红柿炒蛋!"); 60 } 61 } 62 63 /// <summary> 64 /// 土豆肉丝这道菜 65 /// </summary> 66 public class ShreddedPorkWithPotatoes : Food 67 { 68 public override void Print() 69 { 70 Console.WriteLine("一份土豆肉丝"); 71 } 72 }
本身作饭,若是咱们想吃别的菜时,此时就须要去买这种菜和洗菜这些繁琐的操做,有了餐馆(也就是简单工厂)以后,咱们就能够把这些操做交给餐馆去作,此时消费者(也就是咱们)对菜(也就是具体对象)的依赖关系从直接变成的间接的,这样就是实现了面向对象的另外一个原则——下降对象之间的耦合度,下面就具体看看有了餐馆以后的实现代码(即简单工厂的实现):工具
1 /// <summary> 2 /// 顾客充当客户端,负责调用简单工厂来生产对象 3 /// 即客户点菜,厨师(至关于简单工厂)负责烧菜(生产的对象) 4 /// </summary> 5 class Customer 6 { 7 static void Main(string[] args) 8 { 9 // 客户想点一个西红柿炒蛋 10 Food food1 = FoodSimpleFactory.CreateFood("西红柿炒蛋"); 11 food1.Print(); 12 13 // 客户想点一个土豆肉丝 14 Food food2 = FoodSimpleFactory.CreateFood("土豆肉丝"); 15 food2.Print(); 16 17 Console.Read(); 18 } 19 } 20 21 /// <summary> 22 /// 菜抽象类 23 /// </summary> 24 public abstract class Food 25 { 26 // 输出点了什么菜 27 public abstract void Print(); 28 } 29 30 /// <summary> 31 /// 西红柿炒鸡蛋这道菜 32 /// </summary> 33 public class TomatoScrambledEggs : Food 34 { 35 public override void Print() 36 { 37 Console.WriteLine("一份西红柿炒蛋!"); 38 } 39 } 40 41 /// <summary> 42 /// 土豆肉丝这道菜 43 /// </summary> 44 public class ShreddedPorkWithPotatoes : Food 45 { 46 public override void Print() 47 { 48 Console.WriteLine("一份土豆肉丝"); 49 } 50 } 51 52 /// <summary> 53 /// 简单工厂类, 负责 炒菜 54 /// </summary> 55 public class FoodSimpleFactory 56 { 57 public static Food CreateFood(string type) 58 { 59 Food food = null; 60 if (type.Equals("土豆肉丝")) 61 { 62 food= new ShreddedPorkWithPotatoes(); 63 } 64 else if (type.Equals("西红柿炒蛋")) 65 { 66 food= new TomatoScrambledEggs(); 67 } 68 69 return food; 70 } 71 }
看完简单工厂模式的实现以后,不少人确定会有这样的疑惑——咱们只是把变化的代码移到了工厂类中罢了,好像没有变化的问题了,若是客户想吃其余菜时,此时咱们仍是须要修改工厂类中的方法(也就是多加case语句,没应用简单工厂模式以前,修改的是客户类)。我首先要说:你们想的很对,每种设计模式只会解决一种问题,他们有本身的使用场景,没有一种模式能够解决全部问题,这个就是简单工厂模式的缺点所在(这个缺点后面介绍的工厂方法模式能够很好地解决),然而,简单工厂模式与以前的实现也有它的优势:编码
虽然上面已经介绍了简单工厂模式的缺点,下面仍是总结下简单工厂模式的缺点:spa
了解了简单工厂模式以后的优缺点以后,咱们以后就能够知道简单工厂的应用场景了:设计
简单工厂模式在不少时候能够叫静态工厂模式(由于工厂类都定义了一个静态方法),由一个工厂类根据传入的参数决定建立出哪种产品类的实例(通俗点表达:经过客户下的订单来负责烧那种菜)。简单工厂模式的UML图以下:
若是你们想看源码,源码以下:code
1 public static Encoding GetEncoding(int codepage) 2 { 3 Encoding encoding = EncodingProvider.GetEncodingFromProvider(codepage); 4 if (encoding != null) 5 { 6 return encoding; 7 } 8 if (codepage < 0 || codepage > 65535) 9 { 10 throw new ArgumentOutOfRangeException("codepage", Environment.GetResourceString("ArgumentOutOfRange_Range", new object[] 11 { 12 0, 13 65535 14 })); 15 } 16 if (Encoding.encodings != null) 17 { 18 encoding = (Encoding)Encoding.encodings[codepage]; 19 } 20 if (encoding == null) 21 { 22 object internalSyncObject = Encoding.InternalSyncObject; 23 lock (internalSyncObject) 24 { 25 if (Encoding.encodings == null) 26 { 27 Encoding.encodings = new Hashtable(); 28 } 29 if ((encoding = (Encoding)Encoding.encodings[codepage]) != null) 30 { 31 return encoding; 32 } 33 if (codepage <= 1201) 34 { 35 if (codepage <= 42) 36 { 37 switch (codepage) 38 { 39 case 0: 40 encoding = Encoding.Default; 41 goto IL_193; 42 case 1: 43 case 2: 44 case 3: 45 break; 46 default: 47 if (codepage != 42) 48 { 49 goto IL_182; 50 } 51 break; 52 } 53 throw new ArgumentException(Environment.GetResourceString("Argument_CodepageNotSupported", new object[] 54 { 55 codepage 56 }), "codepage"); 57 } 58 if (codepage == 1200) 59 { 60 encoding = Encoding.Unicode; 61 goto IL_193; 62 } 63 if (codepage == 1201) 64 { 65 encoding = Encoding.BigEndianUnicode; 66 goto IL_193; 67 } 68 } 69 else if (codepage <= 20127) 70 { 71 if (codepage == 1252) 72 { 73 encoding = new SBCSCodePageEncoding(codepage); 74 goto IL_193; 75 } 76 if (codepage == 20127) 77 { 78 encoding = Encoding.ASCII; 79 goto IL_193; 80 } 81 } 82 else 83 { 84 if (codepage == 28591) 85 { 86 encoding = Encoding.Latin1; 87 goto IL_193; 88 } 89 if (codepage == 65001) 90 { 91 encoding = Encoding.UTF8; 92 goto IL_193; 93 } 94 } 95 IL_182: 96 encoding = Encoding.GetEncodingCodePage(codepage); 97 if (encoding == null) 98 { 99 encoding = Encoding.GetEncodingRare(codepage); 100 } 101 IL_193: 102 Encoding.encodings.Add(codepage, encoding); 103 } 104 return encoding; 105 } 106 return encoding; 107 }
介绍完了简单工厂模式以后,.NET类库中也有相似的实现,NET中System.Text.Encoding类就实现了简单工厂模式,该类中的GetEncoding(int codepage)就是工厂方法,具体的代码能够经过Reflector反编译工具进行查看对象
.NET 中Encoding的UML图为:
Encoding类中实现的简单工厂模式是简单工厂模式的一种演变,此时简单工厂类由抽象产品角色扮演,然而.NET中Encoding类是如何解决简单工厂模式中存在的问题的呢(即若是新添加一种编码怎么办)?在GetEncoding方法里的switch函数有以下代码:
switch (codepage) { ....... default: unicode = GetEncodingCodePage(codepage); if (unicode == null) { unicode = GetEncodingRare(codepage); //当编码不多见时 } break; ...... }
在GetEncodingRare方法里有一些不经常使用编码的实例化代码,微软正式经过这个方法来解决新增长一种编码的问题。(其实也就是列出全部可能的编码状况),微软之因此以这样的方式来解决这个问题,多是因为如今编码已经稳定了,添加新编码的可能性比较低,因此在.NET 4.5仍然未改动这部分代码。
今天就到这里了,简单工厂模式的介绍都到这里了,提及简单工厂,实际上是一个很容易的工厂,可能不少人有意或者无心的使用过着模式,模式不要太关注实现细节,要关注模式得出的缘由,分析问题的方法。咱们必定要好好的记住面向对象设计的原则,而后好好的体会模式之美,理解会更多。