工厂方法模式定义了一个创建对象的接口,但有子类决定需要实例化的类是哪一个。工厂方法模式把类的实例化推迟到子类。
4.2工厂方法模式UML类图:
工厂方法:abstract Product FactoryMethod(string type)
4.3应用场景
当我们需要创建的单个对象可能发生的改变时,例如:假设一个玩具生产商今天生产玩具汽车,明天改变产品转而生产玩具飞机了(单个对象发生了变化),这个时候,我们就可以使用工厂方法模式。
个人感觉简单工厂也完全可以实现工厂方法甚至后面的抽象工厂所能实现的所有功能,简单工厂是将创建对象可能的变化封装在一个普通的方法中,而工厂方法及抽象工厂则是将创建对象可能的变化封装在其抽象(接口、抽象类)的子类中。所以,如果没有什么特殊的需要,个人觉得利用简单工厂就足够了。
4.4工厂方法模式分析与实现(c#描述)
namespace FactoryMethod
{
//所有产品共有的抽象类
public abstract class Product
{
public string ProductName;
public abstract void ShowMyName();
public virtual void ProductBehavior1()
{
Console.WriteLine("这是抽象类中产品的缺省行为1。");
}
public virtual void ProductBehavior2()
{
Console.WriteLine("这是抽象类中产品的缺省行为2。");
}
}
//具体产品类A1-被A工厂制造
public class ProductA1 : Product
{
public ProductA1()
{
this.ProductName = "我是A工厂的A1产品。";
}
public override void ShowMyName()
{
Console.WriteLine(this.ProductName);
}
}
//具体产品类A2-被A工厂制造
public class ProductA2 : Product
{
public ProductA2()
{
this.ProductName = "我是A工厂的A2产品。";
}
public override void ShowMyName()
{
Console.WriteLine(this.ProductName);
}
}
//具体产品类B1-被B工厂制造
public class ProductB1 : Product
{
public ProductB1()
{
this.ProductName = "我是B工厂的B1产品。";
}
public override void ShowMyName()
{
Console.WriteLine(this.ProductName);
}
//具体产品类B覆盖父类中缺省的方法
public override void ProductBehavior2()
{
Console.WriteLine("我覆盖了抽象类产品的缺省行为2。");
}
}
//具体产品类B2-被B工厂制造
public class ProductB2 : Product
{
public ProductB2()
{
this.ProductName = "我是B工厂的B2产品。";
}
public override void ShowMyName()
{
Console.WriteLine(this.ProductName);
}
//具体产品类B覆盖父类中缺省的方法
public override void ProductBehavior1()
{
Console.WriteLine("我覆盖了抽象类产品的缺省行为1。");
}
}
//工厂抽象类
public abstract class Factory
{
public abstract Product CreateAProduct(string astg_Type);//工厂方法,由子类来进行具体的产品实例化
}
//具体工厂类A,生产一系列A风格的产品
public class FactoryA:Factory
{
public override Product CreateAProduct(string astg_Type)
{
//这里虽然一个工厂可以生产多个产品,但是,同一时间只能生产一个。
if (astg_Type == "汽车")
{
return new ProductA1();
}
else if (astg_Type == "飞机")
{
return new ProductA2();
}
else return null;
}
}
//具体工厂类B,生产一系列B风格的产品
public class FactoryB : Factory
{
public override Product CreateAProduct(string astg_Type)
{
if (astg_Type == "汽车")
{
return new ProductB1();
}
else if (astg_Type == "飞机")
{
return new ProductB2();
}
else return null;
}
}
//调用类
public class FactoryMethod_Test
{
public static void Do()
{
Factory lobj_Factory = new FactoryA();//A工厂用来制造A风格的产品。
lobj_Factory.CreateAProduct("汽车");
lobj_Factory = new FactoryB();//B工厂用来制造B风格的产品。这样:通过对Factory赋以不同的子类,我们可以实现动态的变更正在创建的产品,例如我们原来创建的是A风格的汽车,现在创建的则是B风格的汽车,这种弹性是简单工厂所做不到的。
lobj_Factory.CreateAProduct("汽车");
//总结:子类的工厂方法封装了产品类的创建过程,即封装了产品的实现。
//客户只依赖Factory这个抽象,如果增加产品或改变产品时,此处的客户端代码不受影响。
}
}
}
工厂方法模式遵守了一个重要的设计原则,即:依赖倒置原则。客户不依赖于具体的产品,而只依赖于工厂(Factory)这个抽象,由这个抽象来生产具体的产品,这样一来,即使产品将来可能会发生变化,而抽象却是永远不会变的,抽象不变,那就意味着客户代码不用修改,呵呵!那么,如何在实际编程中尽量做到不违背依赖倒置原则,这里有一些指导方针:
1、变量不可以持有具体类的引用。因为如果使用new,就会持有具体类的使用,可以利用工厂避免这种行为。例如:Factory lobj_Factory = new FactoryA(),这里lobj_Factory就持有了FactoryA()的具体引用。如果哪天我们需要的是FactoryB(),则此处代码就意味着必须改变,这就是持有具体类引用的后果。那我们不是经常string lstg_Test=new string()吗?这是因为string的实现类不可能改变,所以我们可以这么做。
2、不要让类派生自具体类。如果派生自具体类,就会依赖于具体类,请派生自一个接口(抽象类或接口,这里的接口是广义的概念)。
3、尽量不要覆盖基类中已实现的方法。如果覆盖基类中已有的方法,那么此基类就不是一下真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。
依赖倒置原则好像是在要求我们在设计是一开始想到的应该就是如何把具体类抽象出来,即:当我们看到鸭子的时候应该首先想到的是:先设计一个动物类,呵呵。
至此,工厂方法模式完毕。