1. 引言
《工厂模式之简单工厂》引言中简单介绍了所有工厂模式中涉及的角色及理解,与简单工厂模式相比,工厂方法模式增加了抽象工厂的角色,具体工厂一般为多个,具体工厂继承抽象工厂。通过一个简单示例说明,有一个抽象工厂能够生产香奈儿的所有产品,此时,会有具体工厂如香奈儿衣服工厂、香奈儿箱包工厂、香奈儿鞋帽工厂,具体工厂会生产对应的产品,如衣服(包括具体产品T恤、羽绒服、裤子等)。
随着当前社会分工越来越细,越来越专业化,各类产品的创建有专门的工厂生产,极大缩短了产品生产周期,提高了生产效率。同理,在软件开发中,软件对象的生产由具体的工厂进行创建,客户可以随意的增加或删除产品对象对其他产品都不会有影响也不需要重新编译。
2. 定义
工厂方法模式,是定义一个用于创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类中。满足了创建型模式中所要求的“创建与使用分离”的特点。
如果要创建的产品不多,只需要一个工厂即可完成,这种模式称为“简单工厂模式”,该模式的缺点是,新增产品时违背了面向对象的“开闭原则”。
3. 工厂方法实现计算器功能
以前面博客工厂模式之简单工厂中提出的实现计算器功能为例,用工厂方法进行实现,代码如下:
简单工厂模式中分支与工厂类耦合,需要增加新的运算类时,需要在工厂方法中的switch..case中添加判断条件,根据依赖倒转原则,将工厂类与运算进行解耦,因此,抽象出一个接口,接口中只有一个方法,创建抽象产品的工厂方法。之后,所有要生产具体类的工厂去实现这个接口。这样简单工厂模式的工厂类,变成了一个抽象工厂接口和多个生产类的具体工厂。此时,简单工厂模式的工厂类变成一个工厂抽象接口和多个具体生成对象的工厂。
class Operate
{
private double _num1;
private double _num2;
public double num1
{
get { return num1; }
set { num1 = _num1; }
}
public double num2
{
get { return num2; }
set { num2 = _num2; }
}
public virtual double getResult(double num1, double num2)
{
double result = 0.0;
return result;
}
}
class Add : Operate
{
public override double getResult(double n1, double n2)
{
return n1 + n2;
}
}
class Sub : Operate
{
public override double getResult(double n1, double n2)
{
return n1 - n2;
}
}
class Multiply : Operate
{
public override double getResult(double n1, double n2)
{
return n1 * n2;
}
}
class Divide : Operate
{
public override double getResult(double n1, double n2)
{
if (n2 == 0)
throw new Exception("除数不能为0");
return n1 / n2;
}
}
interface IFactory
{
Operate createOperate();
}
class AddFactory:IFactory
{
public Operate createOperate()
{
return new Add();
}
}
class SubFactory : IFactory
{
public Operate createOperate()
{
return new Sub();
}
}
class MultiplyFactory : IFactory
{
public Operate createOperate()
{
return new Multiply();
}
}
class DivideFactory : IFactory
{
public Operate createOperate()
{
return new Divide();
}
}
class Program
{
static void Main(string[] args)
{
IFactory operateFactory = new AddFactory();
Operate ope = operateFactory.createOperate();
double result = ope.getResult(1.0,2.0);
Console.WriteLine("{0}",result);
Console.Read();
}
}
在工厂方法模式中,选择用哪个工厂实现运算类,由原来的工厂类内部转移到客户端判别。
4.情景实现
情景介绍:某同学“学雷锋”以学习雷锋的名义在医院做义工(打扫、洗衣、买饭),在他生病的时候,其他人以同样的名义代替他去,首先使用简单工厂模式实现。
分析发现,该场景中可以把“学雷锋”和其他人抽象出一个“雷锋”类,拥有打扫、洗衣、买饭的方法,代码如下:
class LeiFeng
{
public void Sweep()
{
Console.WriteLine("I am sweeping");
}
public void Wash()
{
Console.WriteLine("I am washing");
}
public void Cook()
{
Console.WriteLine("I am cooking");
}
}
class Student:LeiFeng
{
}
static void Main(string[] args)
{
LeiFeng lf=new Student();
lf.Cook();
lf.Sweep();
lf.Wash();
Console.WriteLine("Hello World!");
}
然而,如果有多个学生学雷锋做好事时,需要进行多次实例化,会造成大量重复代码以及创建对象的资源消耗。而对医院病人来说,不需要知道具体的对象是谁,只需要知道是学雷锋的人帮忙就好。此外,有其他社会人士“志愿者”学雷锋做好事,需要再新建一个类继承LieFeng,由雷锋工厂决定实例化谁,这是简单工厂模式的实现,具体如下:
class SimpleFactory
{
public static LeiFeng createObject(string type)
{
LeiFeng result = null;
switch (type)
{
case "学雷锋的大学生":
result = new Student(); break;
case "社区志愿者":
result=new Volunteer();break;
}
return result;
}
}
class Volunteer:LeiFeng
{
}
static void Main(string[] args)
{
LeiFeng stud = SimpleFactory.createObject("学雷锋的大学生");
stud.Sweep();
LeiFeng stud2 = SimpleFactory.createObject("社区志愿者");
stud2.Cook();
Console.WriteLine("Hello World!");
}
上述实现为简单工厂模式,将其更改为工厂方法模式,全部代码如下:
interface SimpleFactory
{
LeiFeng CreateLeiFeng();
}
class StudengFactory:SimpleFactory
{
public LeiFeng CreateLeiFeng()
{
return new Student();
}
}
class VolunteerFactory
{
public LeiFeng CreateLeiFeng()
{
return new Volunteer();
}
}
class LeiFeng
{
public void Sweep()
{
Console.WriteLine("I am sweeping");
}
public void Wash()
{
Console.WriteLine("I am washing");
}
public void Cook()
{
Console.WriteLine("I am cooking");
}
}
class Student:LeiFeng
{
}
class Volunteer:LeiFeng
{
}
static void Main(string[] args)
{
SimpleFactory sf=new StudengFactory();
LeiFeng studeng = sf.CreateLeiFeng();
studeng.Sweep();
studeng.Wash();
Console.WriteLine("Hello World!");
}
5. 总结
工厂方法模式主要由4部分组成:
- 抽象工厂角色。是工厂方法模式的核心,与应用程序无关,是具体工厂角色必须实现的接口或必须继承的父类。(IFactory)
- 具体工厂角色。包含和具体逻辑有关的代码。客户端调用,返回产品实例。(如AddFactory)
- 抽象产品角色。是产品接口(如Operation)。
- 具体产品角色。具体工厂角色所创建的对象就是此角色的实例(如AddOperate类)。
简单工厂模式和工厂方法模式的区别:
简单工厂模式中工厂类中包含必要的逻辑判断,根据客户说的选择条件实例化相关类,客户端与具体产品没有依赖。而工厂方法模式实现时,客户端需要决定实例化哪个工厂来实现运算类,但是逻辑判断在客户端进行。
工厂方法模式是简单工厂模式的衍生,解决了简单工厂模式违反“开闭”原则的问题,并实现了可扩展,能够解决更复杂的层次结构,可用于产品结果复杂的场合。
6. 参考文献
(1)程杰.大话设计模式[M]. 清华大学出版社.