现在我们正式进入GoF23种设计模式中的创建型模式的讲解中来,创建型模式主要解决对象如何创建的问题,提倡创建对象的责任和使用对象的责任分离,以达到更好对创建对象的控制的目的,创建型模式主要包括抽象工厂(Abstract Factory),建造者(Builder),工厂方法(Factory Method),原型(Prototype),单子(Singleton)。这篇文章主要分为两大部分内容,在第一部分中我将介绍抽象工厂模式的原型,包括抽象工厂的意图,可以解决的问题,原型代码和UML等,再结合一个生活中的小例子进行原型的说明。第二部分我会结合实际项目来讲述一下抽象工厂模式是如何应用的。最后我会对抽象工厂模式进行一个小结。
工厂模式的几种形态
工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,不必事先知道每次要实例化哪一个类。工厂模式有以下几种形态:
简单工厂(Simple Factory)模式:又称静态工厂方法模式(Static Factory Method Pattern)。主要是工厂中提供一个静态的方法用来根据不同的参数创建不同的抽象产品的具体实例,一般在IoC中应用比较多,例如通过反射机制和简单工厂模式可以解决依赖注入的问题。简单工厂模式不属于GoF23种设计模式,这里也就不再作过多的分析。感兴趣的园友可以找找相关资料。
工厂方法(Factory Method)模式:又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式。这个模式属于GoF23种设计模式之一,在后面的文章中会做详细的介绍。
抽象工厂(Abstract Factory)模式:又称工具箱(Kit或Toolkit)模式。这是本文的重点。
抽象工厂模式的原型
描述:
假设一个子系统需要一些产品对象,而这些产品对象又属于一个以上的产品等级结构。那么为了将消费这些产品的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。
意图:
抽象工厂模式可以向客户端(Client指代码模式的使用者,后文类同)提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族(Product Family指位于不同产品等级中,功能相关联的产品的集合)中的产品对象。
模式原型UML:
抽象工厂涉及到以下角色:
抽象工厂(AbstractFactory )角色:声明一个操作集合的接口以创建抽象产品族。
具体工厂(ConcreteFactory)角色:实现创建具体产品族的抽象工厂的实现。
抽象产品(AbstractProduct )角色:声明一个产品的接口。
具体产品(Product)角色:定义了一个被具体工厂创建的产品对象,实现了抽象工厂接口。
客户端(Client)角色:使用抽象工厂和抽象产品的类。
模式原型代码:
Abstract Factory pattern code in C#
// 抽象工厂模式原型代码
using System;
namespace DesignPatterns.Creational
{
// 测试程序
class MainApp
{
public static void Main()
{
// 抽象工厂1
AbstractFactory factory1 = new ConcreteFactory1();
Client c1 = new Client(factory1);
c1.Run();
// 抽象工厂2
AbstractFactory factory2 = new ConcreteFactory2();
Client c2 = new Client(factory2);
c2.Run();
// 等候用户输入
Console.Read();
}
}
// 抽象工厂
abstract class AbstractFactory
{
public abstract AbstractProductA CreateProductA();
public abstract AbstractProductB CreateProductB();
}
// 具体工厂1
class ConcreteFactory1 : AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA1();
}
public override AbstractProductB CreateProductB()
{
return new ProductB1();
}
}
// 具体工厂2
class ConcreteFactory2 : AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA2();
}
public override AbstractProductB CreateProductB()
{
return new ProductB2();
}
}
// 抽象产品A,产品族中一个成员
abstract class AbstractProductA
{
}
// 抽象产品B,产品族中一个成员
abstract class AbstractProductB
{
public abstract void Interact(AbstractProductA a);
}
// 具体产品A1
class ProductA1 : AbstractProductA
{
}
// 具体产品B1
class ProductB1 : AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name +
" interacts with " + a.GetType().Name);
}
}
// 具体产品A2
class ProductA2 : AbstractProductA
{
}
// 具体产品B2
class ProductB2 : AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name +
" interacts with " + a.GetType().Name);
}
}
// 客户端,使用环境
class Client
{
private AbstractProductA AbstractProductA;
private AbstractProductB AbstractProductB;
// 构造,注意通过构造传入抽象工厂
public Client(AbstractFactory factory)
{
AbstractProductB = factory.CreateProductB();
AbstractProductA = factory.CreateProductA();
}
public void Run()
{
AbstractProductB.Interact(AbstractProductA);
}
}
}
输出结果为:
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2
生活中的实例:
这个生活中的实例代码演示了一个电脑游戏,在游戏中建立不同的动物世界会使用不同的工厂。虽然创建动物的大陆工厂是不同的,但是动物之间的相互关系保持不变。
Real world code using Abstract Factory in C#
using System;
namespace DesignPatterns.Creational.AbstractFactory.RealWorld
{
class MainApp
{
public static void Main()
{
// 创建非洲大陆
ContinentFactory africa = new AfricaFactory();
AnimalWorld world = new AnimalWorld(africa);
world.RunFoodChain();
// 创建美洲大陆
ContinentFactory america = new AmericaFactory();
world = new AnimalWorld(america);
world.RunFoodChain();
// 等待用户输入
Console.ReadKey();
}
}
// 抽象工厂
abstract class ContinentFactory
{
// 创建食草动物
public abstract Herbivore CreateHerbivore();
// 创建食肉动物
public abstract Carnivore CreateCarnivore();
}
// 具体工厂1
class AfricaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
// 返回牛羚
return new Wildebeest();
}
public override Carnivore CreateCarnivore()
{
// 返回狮子
return new Lion();
}
}
// 具体工厂2
class AmericaFactory : ContinentFactory
{
public override Herbivore CreateHerbivore()
{
// 返回野牛
return new Bison();
}
public override Carnivore CreateCarnivore()
{
// 返回狼
return new Wolf();
}
}
// 抽象产品A
abstract class Herbivore
{
}
// 抽象产品B
abstract class Carnivore
{
// 交互关系,食肉动物可以吃掉食草动物
public abstract void Eat(Herbivore h);
}
// 具体产品A1
class Wildebeest : Herbivore
{
}
// 具体产品B1
class Lion : Carnivore
{
public override void Eat(Herbivore h)
{
// 吃掉牛羚
Console.WriteLine(this.GetType().Name +
" eats " + h.GetType().Name);
}
}
// 具体产品A2
class Bison : Herbivore
{
}
// 具体产品B2
class Wolf : Carnivore
{
public override void Eat(Herbivore h)
{
// 吃掉野牛
Console.WriteLine(this.GetType().Name +
" eats " + h.GetType().Name);
}
}
// 客户端
class AnimalWorld
{
private Herbivore _herbivore;
private Carnivore _carnivore;
// 通过构造器传入具体工厂
public AnimalWorld(ContinentFactory factory)
{
_carnivore = factory.CreateCarnivore();
_herbivore = factory.CreateHerbivore();
}
public void RunFoodChain()
{
_carnivore.Eat(_herbivore);
}
}
}
输出的结果为:
Lion eats Wildebeest
Wolf eats Bison
什么情况下使用抽象工厂:
文献【GOF95】指出,在以下情况下应当考虑使用抽象工厂模式:
1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
2、这个系统的产品有多于一个产品族,而系统只消费其中某一个族的产品(上面这一条叫做抽象工厂模式的原始用意。)
3、同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
4、系统提供一个产品类的库,所有的产品以同样的接口实现,从而使客户端不依赖于实现。
实际项目举例
现在需要创建分属于不同操作系统的视窗构件。比如命令按钮(Button)与文本框(Text)等都是视窗构件,在UNIX系统的视窗环境和Windows操作系统的视窗环境中,这两个构件有不同的本地体现,它们的细节也有所不同。在每一个操作系统中,都有一个视窗构件组成构件家族。在这里就是Button和Text组成的产品族。而每一个视窗构件都构成自己的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不同操作系统的具体实现,如下图所示。
可以发现在上面的产品类图中,有两个产品的等级结构,分别是Button等级结构和Text等级结构、同时有两个产品族,也就是UNIX产品族和Windows产品族。UNIX产品族由UnixButton和UnixText产品组成;而Windows产品族由WinButton和WinText产品构成。
系统对产品对象的创建需求由一个工厂的等级结构满足,其中有两个具体工厂角色,即UnixFactory和WinFactory。UnixFactory对象负责创建Unix产品族中的产品,而WinFactory对象负责创建Windows产品族中的产品。这就是抽象工厂模式的应用,抽象工厂模式的解决方案如下图所示。
显然,一个系统只能够在某一个操作系统的视窗环境下运行,而不能够同时在不同的操作系统上运行。所以,系统实际上只能消费属于同一个产品族的产品。这个案例实际上也正是抽象工厂模式的起源。实现的代码如下。
Project code using Abstract Factory in C#
// 抽象工厂起源案例
using System;
namespace DesignPatterns.Creational.AbstractFactory.Project
{
class MainApp
{
public static void Main()
{
// 创建Unix使用环境
OSFactory unix = new UnixFactory();
Client client = new Client(unix);
client.Run();
// 创建Windows使用环境
OSFactory windows = new WinFactory();
client = new Client(windows);
client.Run();
// 等待用户输入
Console.ReadKey();
}
}
// 抽象工厂
abstract class OSFactory
{
// 创建按钮构件
public abstract Button CreateButton();
// 创建文本框构件
public abstract Text CreateText();
}
// 具体工厂1
class UnixFactory : OSFactory
{
public override Button CreateButton()
{
// 返回Unix下的Button
return new UnixButton();
}
public override Text CreateText()
{
// 返回Unix下的Text
return new UnixText();
}
}
// 具体工厂2
class WinFactory : OSFactory
{
public override Button CreateButton()
{
// 返回Windows下的Button
return new WinButton();
}
public override Text CreateText()
{
// 返回Winodws下的Text
return new WinText();
}
}
// 抽象产品A
abstract class Button
{
}
// 抽象产品B
abstract class Text
{
// 交互关系
public abstract void Interact(Button b);
}
// 具体产品A1
class UnixButton : Button
{
}
// 具体产品B1
class UnixText : Text
{
public override void Interact(Button b)
{
Console.WriteLine(this.GetType().Name +
" interact with " + b.GetType().Name);
}
}
// 具体产品A2
class WinButton : Button
{
}
// 具体产品B2
class WinText : Text
{
public override void Interact(Button b)
{
Console.WriteLine(this.GetType().Name +
" interact with " + b.GetType().Name);
}
}
// 客户端
class Client
{
private Button _button;
private Text _text;
// 通过构造器传入具体工厂
public Client(OSFactory factory)
{
_button = factory.CreateButton();
_text = factory.CreateText();
}
public void Run()
{
_text.Interact(_button);
}
}
}
输出结果为:
UnixText interact with UnixButton
WinText interact with WinButton
小结
抽象工厂模式是一个在实际项目中应用比较多的设计模式之一,抽象工厂模式面对的问题是多个产品等级结构的系统设计,运用抽象工厂模式的关键在于如果把创建产品的职责交给工厂去完成,希望大家在把握住工厂模式原型的基础上尽量的考虑到应用,形成一种思维上的定势。
Welcome to share your idea,thank you!欢迎分享您的想法,谢谢!
大家一起学面向对象设计模式系列 索引贴