四.创建型设计模式——Factory Method Pattern(工厂方法模式)
-
定义
工厂方法模式又称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,属于类的创建型模式。在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实例化(创建)哪一个类。
在简单工厂模式中,一个工厂类处于对产品类进行实例化的中心位置,它知道每一个产品类的细节,并决定何时哪一个产品类应当被实例化。简单工厂模式的优点是能够使客户端独立于产品的创建过程,并且在系统中引入新产品时无需对客户端进行修改,缺点是当有新产品要加入系统中时,必须修改工厂类,以加入必要的处理逻辑。简单工厂模式的致命弱点就是处于核心地址的工厂类,因为一旦它无法确定要对哪个类进行实例化,就无法使用该模式,而工厂方法模式则可以很好地解决这一问题。
工厂方法模式的UML图如下:
其中的类或对象之间的关系为:
1. Product(产品角色):定义产品的接口。
2. ConcreteProduct(真实的产品):实现接口的Product的类。
3. Creator(工厂角色):声明工厂方法(FactoryMethod),返回一个产品。
4. ConcreteCreator(真实的工厂):实现FactoryMethod工厂方法,由客户调用,返回一个产品的实例。
典型应用的顺序图如下,不同的工厂负责创建不同的产品。
-
实例1——多文档系统
应用工厂方法模式提供的最大灵活性生成不同的对象。抽象类可以返回一个默认的对象,而每一个派生的子类都可以返回扩展了的其他对象。
//产品基类
abstract class Page
{
}
//工厂抽象类
abstract class Document
{
protected ArrayList pages = new ArrayList();
public Document()
{
this.CreatePages();
}
public ArrayList Pages
{
get { return pages; }
}
abstract public void CreatePages();
}
//派生类,具体产品类——技能页
class SkillsPage : Page
{
}
//派生类,具体产品类——教育页
class EducationPage : Page
{
}
//派生类,具体产品类——经验页
class ExperiencePage : Page
{
}
//派生类,具体产品类——介绍页
class IntroductionPage : Page
{
}
//派生类,具体产品类——结果页
class ResultPage : Page
{
}
//派生类,具体产品类——结论页
class ConclusionPage : Page
{
}
//派生类,具体产品类——总结页
class SummaryPage : Page
{
}
//派生类,具体产品类——文献页
class BibliographyPage : Page
{
}
//具体工厂类——个人简历,包括技能、教育、经验
class Resume : Document
{
public override void CreatePages()
{
pages.Add(new SkillsPage());
pages.Add(new EducationPage());
pages.Add(new ExperiencePage());
}
}
//具体工厂类——报告,包括介绍、结果、结论、总结、文献
class Report : Document
{
public override void CreatePages()
{
pages.Add(new IntroductionPage());
pages.Add(new ResultPage());
pages.Add(new ConclusionPage());
pages.Add(new SummaryPage());
pages.Add(new BibliographyPage());
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
Document[] docs = new Document[2];
docs[0] = new Resume();
docs[1] = new Report();
foreach (Document document in docs)
{
Console.WriteLine("\n" + document + "--------------");
foreach (Page page in document.Pages)
{
Console.WriteLine(" " + page);
}
}
Console.Read();
}
}
-
实例2——扩展了的手机工厂
现实中不同品牌的手机应由不同的工厂制造,下面我们扩展上一节介绍的手机简单工厂的示例,所应用的模式就是工厂方法(FactoryMethod)模式。
//手机接口
public interface iMobile
{
void Call();
}
//手机工厂接口
public interface iMobileFactory
{
iMobile ProduceMobile();
}
//摩托罗拉手机现实手机接口
public class Motorola : iMobile
{
public void Call()
{
Console.WriteLine("摩托罗拉手机");
}
}
//诺基亚手机实现手机接口
public class Nokia : iMobile
{
public void Call()
{
Console.WriteLine("诺基亚手机");
}
}
//摩托罗拉工厂实现了生产手机的方法,返回摩托罗拉手机
public class MotorolaFactory : iMobileFactory
{
public iMobile ProduceMobile()
{
Console.WriteLine("摩托罗拉工厂制造了");
return new Motorola();
}
}
//诺基亚工厂实现了生产手机的方法,返回诺基亚手机
public class NokiaFactory : iMobileFactory
{
public iMobile ProduceMobile()
{
Console.WriteLine("诺基亚工厂制造了");
return new Nokia();
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
iMobileFactory mf;
iMobile m;
mf = new MotorolaFactory();
m = mf.ProduceMobile();
m.Call();
mf = new NokiaFactory();
m = mf.ProduceMobile();
m.Call();
Console.Read();
}
}
-
优势和缺陷
在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节。工厂方法模式的核心是一个抽象工厂类,各种具体工厂类通过抽象工厂类将工厂方法继承下来。如此使得客户可以只关心抽象产品和抽象工厂,完全不用理会返回的是哪一种具体产品,也不用关系它是如何被具体工厂创建的。
1. 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
2. 使用工厂方法模式的另一个优点是在系统中加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性非常好。优秀的面向对象设计鼓励使用封装(Encapsulation)和委托(Delegation)来构造软件系统,工厂方法模式正是使用了封装和委托的典型例子,其中封装是通过抽象工厂来体现的,而委托则是通过抽象工厂将创建对象的责任完成交给具体工厂来体现。
3. 使用工厂方法模式的缺点是在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,当两者都比较简单时,系统会有相对额外的开销。
-
应用情景
下面的情景很适合应用工厂方法模式:
1. 类不知道自己要创建哪一个对象。
2. 类用它的子类来指定创建哪个对象。
3. 客户需要清楚创建了哪一个对象。