园子里有很多关于模式的文章,工厂模式作为使用频率最高的模式亦被谈及的最多. 大多数文章只是解释工厂模式是如何搭建的, 却没有提及其区别和功能, 使得越看越混淆. 本文从使用的角度看工厂模式, 毕竟学习模式目的就是为了使用模式, 何时,何地,使用如何的模式则要从理解这些模式的特点出发了.
工厂模式可能是我们最常遇见和使用的设计模式, 如果把工厂模式以及其实现部分看成是一个黑盒子,那么我们只需要告诉它我所需要的对象(注意:我不知道这个对象是如何实现的), 我们即可得到该对象.
工厂模式可以细分为三种, 即: 简单工厂模式, 工厂方法模式, 虚拟工厂模式.
简单工厂模式(Simple Factory Pattern):
![](https://www.cnblogs.com/images/cnblogs_com/sleeper520/SimpleFactory.gif)
如图所示,简单工厂达到了将类的实例化封装的效果. 因为对同类class的解藕, 黑盒子外面我们能看到的ClassFactory和BaseClass借口. 把代码:
SubClassA aEntity=new SubClassA();
SubClassB bEntity=new SubClassB();
转换成了:
BaseClass xEntity=ClassFactory.Create(A);
BaseClass xEntity=ClassFactory.Create(B);
这样,我们可以通过统一的方法调用来实现不同的功能.
xEntity.DoSomeThing();
在我们只知道前提为A的情况下它调用了
SubClassA.DoSomeThing();
而在只知道前提为B的情况下调用:
SubClassB.DoSomeThing();
于是在黑盒子外的我们,只需要考虑A和B的不同情况,而不需要想A情况会怎么样,B情况如何操作了.
工厂方法模式(Factory Method Pattern)
![](https://www.cnblogs.com/images/cnblogs_com/sleeper520/FactoryMethod.gif)
留意工厂方法模式的类结构图,它和上面的简单工厂模式有什么样的区别?
单看ClassFactoryA和ClassFactoryB,它们实际上就是两个简单工厂模式.因为ClassFactoryA和ClassFactoryB都是实现了BaseClass的创建过程, 所以它们是对同类对象的创建工厂. 于是提取出ClassFactoryA和ClassFactoryB的共性, 将它们归类到BaseClassFactory接口, 就实现了工厂方法模式.
看一个工厂方法模式的例子:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
Code
// Factory Method pattern -- Real World example
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Factory.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
// Note: constructors call Factory Method
Document[] documents = new Document[2];
documents[0] = new Resume();
documents[1] = new Report();
// Display document pages
foreach (Document document in documents)
{
Console.WriteLine("\n" + document.GetType().Name+ "--");
foreach (Page page in document.Pages)
{
Console.WriteLine(" " + page.GetType().Name);
}
}
// Wait for user
Console.Read();
}
}
// "Product"
abstract class Page
{
}
// "ConcreteProduct"
class SkillsPage : Page
{
}
// "ConcreteProduct"
class EducationPage : Page
{
}
// "ConcreteProduct"
class ExperiencePage : Page
{
}
// "ConcreteProduct"
class IntroductionPage : Page
{
}
// "ConcreteProduct"
class ResultsPage : Page
{
}
// "ConcreteProduct"
class ConclusionPage : Page
{
}
// "ConcreteProduct"
class SummaryPage : Page
{
}
// "ConcreteProduct"
class BibliographyPage : Page
{
}
// "Creator"
abstract class Document
{
private ArrayList pages = new ArrayList();
// Constructor calls abstract Factory method
public Document()
{
this.CreatePages();
}
public ArrayList Pages
{
get{ return pages; }
}
// Factory Method
public abstract void CreatePages();
}
// "ConcreteCreator"
class Resume : Document
{
// Factory Method implementation
public override void CreatePages()
{
Pages.Add(new SkillsPage());
Pages.Add(new EducationPage());
Pages.Add(new ExperiencePage());
}
}
// "ConcreteCreator"
class Report : Document
{
// Factory Method implementation
public override void CreatePages()
{
Pages.Add(new IntroductionPage());
Pages.Add(new ResultsPage());
Pages.Add(new ConclusionPage());
Pages.Add(new SummaryPage());
Pages.Add(new BibliographyPage());
}
}
}
在工厂方法模式中, 具体功能的实现依然是在其所包含的两个简单工厂的结构中, 不同的是对于前文所示的A,B两种条件,工厂方法做了更细的细分.c#中datacommand就是使用的工厂方法模式. datacommand中细分为SqlDataCommand, OledbDataCommand等, 它们都是实现了DataCommand接口的具体的工厂, 同时在这两个工厂中, 我们可以选择CommandType为Queue或者SavedProcedure. 这便是在工厂中选择创建那种对象的条件A和B. 这样,我们就可以通过
工厂方法模式创建适用于何种数据库,何种查询方式的command对象.
虚构工厂模式(Abstract Factory Pattern)
虚构工厂模式较以上两种模式更佳复杂, 它将不同系列, 却以不同方式完成类似功能的类别进行系列的划分.如下图所示.
Abstruct Factory
BaseClass1, BaseClass2和BaseClass3是互相联系的类别,它们的共同作用能完成一定功能的任务, 因为环境条件的不同,我们需要在这3个类中继承出不同的类别系列来完成任务,而这些系列被归纳到ClassFactoryA和ClassFactoryB.因为ClassFactoryA和ClassFactoryB完成的功能类似,所以我们定义了BaseClassFactory父类, 同时将这些共同的功能封装在其中.这样就形成了虚拟工厂模式. 程序示例如下:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
// Abstract Factory pattern -- Real World example
using System;
namespace DoFactory.GangOfFour.Abstract.RealWorld
{
// MainApp test application
class MainApp
{
public static void Main()
{
// Create and run the Africa animal world
ContinentFactory africa = new AfricaFactory();
AnimalWorld world = new AnimalWorld(africa);
world.RunFoodChain();
// Create and run the America animal world
ContinentFactory america = new AmericaFactory();
world = new AnimalWorld(america);
world.RunFoodChain();
// Wait for user input
Console.Read();
}
}
// "AbstractFactory"
abstract class ContinentFactory
{
public abstract Herbivore CreateHerbivore();
public abstract Carnivore CreateCarnivore();
}
// "ConcreteFactory1"
class AfricaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
return new Wildebeest();
}
public override Carnivore CreateCarnivore()
{
return new Lion();
}
}
// "ConcreteFactory2"
class AmericaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
return new Bison();
}
public override Carnivore CreateCarnivore()
{
return new Wolf();
}
}
// "AbstractProductA"
abstract class Herbivore
{
}
// "AbstractProductB"
abstract class Carnivore
{
public abstract void Eat(Herbivore h);
}
// "ProductA1"
class Wildebeest : Herbivore
{
}
// "ProductB1"
class Lion : Carnivore
{
public override void Eat(Herbivore h)
{
// Eat Wildebeest
Console.WriteLine(this.GetType().Name +
" eats " + h.GetType().Name);
}
}
// "ProductA2"
class Bison : Herbivore
{
}
// "ProductB2"
class Wolf : Carnivore
{
public override void Eat(Herbivore h)
{
// Eat Bison
Console.WriteLine(this.GetType().Name +
" eats " + h.GetType().Name);
}
}
// "Client"
class AnimalWorld
{
private Herbivore herbivore;
private Carnivore carnivore;
// Constructor
public AnimalWorld(ContinentFactory factory)
{
carnivore = factory.CreateCarnivore();
herbivore = factory.CreateHerbivore();
}
public void RunFoodChain()
{
carnivore.Eat(herbivore);
}
}
}
从结构图你也许会觉得虚拟工厂模式和工厂方法模式区别不大,只是虚拟工厂模式中同时实现了多个BaseClass的方法而已. 其实不然, 结构图的区别说明的是虚拟工厂模式强调的是系列与系列间的解藕, 而工厂方法的维度更细微一些. 同时它们在实现的方法上也不同.
工厂方法模式将实例化推迟到子类, 在黑盒子外面我们仍将具体的class视为操作的主体, 这样我们只要通过派生出不同的工厂类继承工厂的接口,而派生于BaseClass的子类我们有足够的灵活度来重写它的实现.
foreach (Page page in document.Pages)
{
Console.WriteLine(" " + page.GetType().Name);
}
虚拟工厂模式中, 工厂接口使用虚拟工厂将其子工厂的功能进行了归纳总结,它使得我们在实现虚拟工厂的时候子结构更严禁稳定.
ContinentFactory africa = new AfricaFactory();
AnimalWorld world = new AnimalWorld(africa);
world.RunFoodChain();
注意比较在工厂方法模式中我们使用的Page和在虚拟工厂中使用的World之间的区别.
只有清楚了解才能妥善使用,只有通过使用才能更加了解. 这是我对工厂模式的理解, 欢迎大家共同交流, 不怕挨砖头.