设计模式学习之简单工厂模式
在Gof四人组编写的《设计模式-可复用面向对象软件基础》一书中,他们将设计模式分为三类:
创建型模式:解决对象的创建问题
结构型模式:结构型模式涉及到如何组合类和对象以获得更大的结构。结构型类模式采用继承机制来组合接口或实现
行为型模式:关心的是类与实体间的通讯问题。
今天讨论一下简单工厂模式,它属于创建型模式,在创建型模式里面,它也算是最简单的了。
1引出问题
我们假设S公司的网络部的经理,小D是该部门的员工,因工作需要,公司需要更换一批电脑,经理让小D去采购,假设眼下电脑的品牌只有知道联想、明基、方正牌子的.在这里经理让小王去市场看看哪个品牌的好,结合性价比,选购电脑。
2.简单程序流程
下面我们用程序虚拟一下购买电脑的流程:{
Console.WriteLine("小D来到电脑城买电脑了");
Console.WriteLine("按‘L’选择联想的电脑;按‘M’选择明基电脑; 按‘F’选择方正电脑");
string pinpai = Console.ReadLine();
switch (pinpai)
{
case "L":
Console.WriteLine("欢迎购买 联想 电脑!");
break;
case "M":
Console.WriteLine("欢迎购买 明基 电脑!");
break;
case "F":
Console.WriteLine("欢迎购买 方正 电脑");
break;
default :
Console.WriteLine( "没有您选择的品牌");
}
}
结果如下:
这个例子已经虚拟了这个简单的买电脑的过程,但是是面向过程的思想。众所周知,当今最流行的编程思想莫过于面向对象编程的思想。为什么面向对象的思想能迅速风靡编程世界呢?答案就在于面向对象编程使程序可复用,易于维护,可扩展,灵活性高。可以通过封装、继承、多态把程序的耦合度降低。就拿上面的程序来说我们当时是假定的只有三个厂家生产电脑,如果现在又出现一个厂家生产神州电脑,那我们就需要更改主程序的代码,增加一个case选择,再增加一个品牌再改一下主程序,这里就是软件中容易变化的部分,想要买电脑是不变的,将变与不变的都放到了一起,这样程序不易维护,而且代码只能用于这个程序,所以更不用提复用了,我们现在要做的就是调整我们的思想,用面向对象的思想来优化上面的程序,为了克服上面所说的缺点我们先把上面易变的部分与不变的地方隔离开来。看下面的改良程序……
3.改良程序__面向对象
{
public static string CreatePC(string pinpai)
{
switch (pinpai)
{
case "L":
return "欢迎购买联想电脑!";
case "M":
return "欢迎购买明基电脑!";
case "F":
return "欢迎购买方正电脑";
case "S":
return "欢迎购买神州电脑";
default :
return "没有您选择的品牌";
}
}
}
public class MyClass
{
public static void Main()
{
Console.WriteLine("小D来到电脑城买电脑了");
Console.WriteLine("按‘L’选择联想的电脑;按‘M’选择明基电脑; 按‘F’选择方正电脑;按'S' 选择神州电脑");
string pinpai = Console.ReadLine();
string diannao = PCFactory.CreatePC(pinpai);
Console.WriteLine(diannao);
Console.ReadLine();
}
现在把业务逻辑封装到PCFactory里了,以后再有电脑品牌的加入的话只需要修改这个类里面的CreatePC方法就可以了,在主程序里面只管调用这个方法,主程序不必关心变化问题了。而且在其它程序也可以复用PCFactory这个类的内容。假如我们将思维再扩展一下,前面我们提到面向对象,那在这个例子里,什么是对象呢,我们要把什么抽象出来呢?对了,答案就是各各品牌的电脑啊,电脑都有共性,比如他们都有颜色啊,重量啊等等一系列属性,我们就把电脑抽象出来,做一父类,让具体的品牌电脑继承它….
{
private string _color;
public string Color
{
get { return _color; }
set { _color = value; }
}
private float _weight;
public float Weigtht
{
get { return _weight; }
set { _weight = value; }
}
public virtual void GetPC()
{
Console.WriteLine("欢迎购买电脑");
}
}
public class lenovoPC : PCProduct
{
public override void GetPC()
{
Console.WriteLine("欢迎购买"+Color+"联想电脑");
}
}
public class BenQPC : PCProduct
{
public override void GetPC()
{
Console.WriteLine("欢迎购买"+Color+"明基电脑");
}
}
public class FangZhengPC : PCProduct
{
public override void GetPC()
{
Console.WriteLine("欢迎购买"+Color+"北大方正电脑");
}
}
public class PCFactory
{
public static PCProduct CreatePC(string pinpai)
{
switch (pinpai)
{
case "L":
return new lenovoPC();
case "M":
return new BenQPC();
case "F":
return new FangZhengPC();
default :
return null;
}
}
}
public class MyClass
{
public static void Main()
{
Console.WriteLine("小D来到电脑城买电脑了");
Console.WriteLine("按‘L’选择联想的电脑;按‘M’选择明基电脑; 按‘F’选择方正电脑;");
string pinpai = Console.ReadLine();
PCProduct diannao = PCFactory.CreatePC(pinpai);
if (diannao != null)
{
diannao.Color = "白色";
diannao.GetPC();
}
Console.ReadLine();
}
}
程序这样改过之后呢,当电脑品牌再增加的时候就不是对原来的程序修改了,而是扩展,现在各添加一个神州电脑的时候只需要新添一个具体电脑的类,它继承自PCProduct类,并修改PCFactory类,增加一个case选择,客户端程序(在这里Main这个主函数)不用做任何的,就可以完成程序的修改了。把电脑这个对象抽象出来,使得程序扩散性增强了,易于更改。
在这里也许有人会问到了那每增加一个品牌的电脑不是都需要更改PCfactory中的CreatePC方法,增加一个Case选择吗?对,这个问题问得好,可以用.net中的反射机制来解决这个问题。可以用配置文件标明具体类的信息,然后反射创建对象实例。
Assembly asm = Assembly.LoadFrom(className+”.dll”)
PCProduct o = asm.CreateInstance (className) as PCProduct;
4.意义
现在回到工厂方法上来,上面的例子实际上就工厂方法了。首先将变化抽象出来(即各种品牌的电脑),使得客户端依赖于稳定的抽象电脑类(PCproduct),不去关心具体的电脑是如何变化的,将对变化的处理即创建具体对象的任务交给工厂类(PCFactory),这样无论电脑品牌是如何变化,它自己都可以高枕无忧。这也是面向对象编程中的一个重要原则-----依赖倒转原则,即面向接口,面向抽象编程,依赖于抽象不依赖于细节。让客户让程序依赖于稳定的接口。