在软件系统中,经常面临着“某个对象”的创建工作,由于需要的变化,这个对象经常面临着剧烈的变化,但是它却拥有比较稳定的接口。例如本例:我们要进行汽车性能测试(汽车的起动,行驶以及停止功能测试),测试内容是固定的,但参与测试的对象却是随时变化的(丰田车系列,宝马车系列或者其它任何相应的车款)
如何应对这种变化?如何提供一种“封装机制”来隔离出“这个易变对象”(不同款的汽车)的变化,从而保持系统中“其他依赖该对象的对象”(汽车功能测试工作)不随着需要改变而改变。
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method模式是类的创建模式,其用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。 在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不接触哪一个产品类被实例化这种细节。这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
在Factory Method模式中,工厂类与产品类往往具有平行的等级结构,它们之间一一对应。
下面我们来实现Factory Method工厂:
一、定义抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Car
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CarFactoryMethod
{
//抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口
定义一个Car抽象类#region 定义一个Car抽象类
abstract class Car
{
public abstract void Start();
public abstract void Run();
public abstract void Stop();
}
#endregion
}
二、定义具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应,在上图中,有两个这种角色:BMWCar与TOYOTACar
BMWCar的定义
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CarFactoryMethod
{
//具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
#region 定义一个宝马车具体类
class BMWCar:Car
{
public override void Start()
{
Console.WriteLine("宝马车起动....");
}
public override void Run()
{
Console.WriteLine("宝马车行驶....");
}
public override void Stop()
{
Console.WriteLine("宝马车制动....");
}
}
#endregion
//具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。
#region 定义一个宝马车具体工厂
class BMWCarFactory : CarFactory
{
public override Car CreateCar()
{
return new BMWCar();
}
}
#endregion
}
TOYOTACar的定义
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CarFactoryMethod
{
//具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
#region 定义一个丰田车具体类
class TOYOTACar : Car
{
public override void Start()
{
Console.WriteLine("丰田车起动....");
}
public override void Run()
{
Console.WriteLine("丰田车行驶....");
}
public override void Stop()
{
Console.WriteLine("丰田车制动....");
}
}
#endregion
//具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。
#region 定义一个丰田车具体工厂
class TOYOTACarFactory : CarFactory
{
public override Car CreateCar()
{
return new TOYOTACar();
}
}
#endregion
}
三、定义抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。在上图中这个角色是:CarFactory
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CarFactoryMethod
{
//抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
#region 定义一个Car抽象工厂类
abstract class CarFactory
{
public abstract Car CreateCar();
}
#endregion
}
四、定义具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。在上图中有两个这样的角色:BMWCarFactory与TOYOTACarFactory
BMWCarFactory
#region 定义一个宝马车具体工厂
class BMWCarFactory : CarFactory
{
public override Car CreateCar()
{
return new BMWCar();
}
}
#endregion
TOYOTACarFactory
#region 定义一个丰田车具体工厂
class TOYOTACarFactory : CarFactory
{
public override Car CreateCar()
{
return new TOYOTACar();
}
}
#endregion
五、客户应用,在客户应用时才真正创建具体的子类工厂并生产子类
定义一个Cartest类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CarFactoryMethod
{
//定义一个应用模块,在此模块中将用到我们的抽象工厂(Creator)角色:CarFactory
class CarTest
{
#region 车辆"起行止"测试工作
//注意:因为我们要把此工作与具体的产品类角色分隔开,让它们之间松耦合,所以引入了抽象类角色来担任被此方法操作的对象,起到隔山打牛的作用
//所以此处,我们把CarFactory抽象类工厂做为参数传入,由它来生产抽象类对象,并由DrivenTest方法对抽象类对象进行操作
//而把产生具体类工厂和具体类对象的工作放到了客户端才进行
public void DrivenTest(CarFactory cft)
{
Car tcar= cft.CreateCar();
try
{
Console.WriteLine("开始进入测试....");
tcar.Start();
tcar.Run();
tcar.Stop();
Console.WriteLine("起动正常,行驶正常,制动正常......");
}
catch(Exception ex)
{
string ke = ex.ToString();
}
}
#endregion
}
}
运行代码:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CarFactoryMethod
{
class Program
{
static void Main(string[] args)
{
CarTest ct = new CarTest();
//客户端创建BMWCarFactory对象,DrivenTest方法要求传入的类型是CarFactory,而实际传入的是BMWCarFactory。
//我们在客户端才实现了产生具体类工厂和具体类对象的工作
ct.DrivenTest(new BMWCarFactory());
ct.DrivenTest(new TOYOTACarFactory());
Console.Read();
}
}
}
运行后效果如下: