博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

asp.net泛型技巧-打造可复用的抽象工厂

Posted on 2007-06-20 10:13  徐正柱-  阅读(769)  评论(0编辑  收藏  举报
抽象工厂(Abstract Factory)模式是将工厂和产品全部抽象化,一个抽象工厂生成一组抽象产品,而一个具体工厂则生成具体产品的一个特定组合。它能够维持这种相关对象组合的一致性,并使得用户不需要了解工厂和产品的具体实现。
    泛型不仅能用来做容器,还能够提供代码复用的手段。在泛型的参与下,许多设计就可能更精妙,更具扩展性。今天我就来演示一个利用泛型增强的抽象工厂模式。我们知道,抽象工厂(Abstract Factory)模式是将工厂和产品全部抽象化,一个抽象工厂生成一组抽象产品,而一个具体工厂则生成具体产品的一个特定组合。它能够维持这种相关对象组合的一致性,并使得用户不需要了解工厂和产品的具体实现。传统的Abstract Factory主要弱点是类型依赖性强,可复用性弱。一个抽象工厂通常是为一个特定需要而设计,通常不能被其他需要抽象工厂的场合使用。而且尽管抽象工厂可以将实际生产任务委派给特定类型的工厂,但这种委派是需要通过自己纯代码实现的,没能利用语言所提供的抽象特性。我们今天的任务就是编写一个不针对特定产品类型和数目的泛型抽象工厂,当你需要特定的抽象工厂时,可随时复用无需再定义专门的抽象工厂实现。

  我们首先从只生成一个产品的工厂方法开始,以这作为抽象工厂的原料。很明显,可以设计这样一个接口作为工厂方法的模板:

public interface IFactory<T>
{
 T Create();
}

  这个工厂生产一个T类型的对象。当你实现此工厂时,应该让T为抽象产品的类型——即产品通用的基类。比如我们可以实现一个采用无参数构造函数来创建对象的OpNewFactory实现:

public class OpNewFactory<TAbstractProduct, TProduct> : IFactory<TAbstractProduct>
where TProduct : TAbstractProduct, new()
{
 public TAbstractProduct Create()
 {
  return new TProduct();
 }
}

  从此例子可以看出,你应该仅实现抽象类型的IFactory接口,并生成具体类型。现在我们做完了单一产品的工厂方法模板,就要开始定义生产多个产品的抽象工厂接口了。.NET泛型支持按类型参数个数进行重载,就是说,我们可以定义生产一个、两个、三个……等多种数目的抽象工厂接口,而使用同一个名字。(汗吧,这就是所谓支持“任意数目”的手法)这里免不了要拷贝代码,不过别担心,纯拷贝而已,使用的时候可是非常舒心的哦。我们以生产两种产品类型的抽象工厂为例介绍。能不能定义成这样呢?

public interface IAbstractFactory<T1, T2>
{
 T1 Create();
 T2 Create(); //编译错误!!!
}

  哦不!方法不能以返回类型区分重载,只能靠参数类型重载。然而这里我们显然不能用T1和T2作为参数,因为这些方法就是为了生产T1和T2准备的,怎么能接受它们作为参数呢?难道要命名为Create1和Create2吗?这很难接受,我们希望生产方法能够体现产品的类型,怎么能叫1和2呢。为了解决这个问题,我们引入了TypeToken<T>类型,它的定义如下:

public sealed class TypeToken<T>
{
 static private TypeToken<T> instanceValue = new TypeToken<T>();
 static public TypeToken<T> Instance
 {
  get { return instanceValue; }
 }

 private TypeToken() { }
}

  这个类没有成员,并且每个类型实参只能创建一个实例,因此代价极小。但就是这小小的实例上带有其类型实参的类型信息,因此可以作为判断函数重载的依据。我们用TypeToken<T>作为区分生产函数重载的依据,实现如下:

public interface IAbstractFactory<T1, T2>
{
 T1 Create(TypeToken<T1> token);
 T2 Create(TypeToken<T2> token);
}

  现在我们针对抽象工厂实现具体工厂。具体工厂就是利用生产每种产品的单一工厂来组合实现。因此你只要有每种类型的单一工厂就可以直接组合生成抽象工厂,而无需定义一个类来做这件事。注意,对每种数目的抽象工厂接口都需要对应生成一个具体工厂的实现,这里我仅针对生成两个产品的演示:

public class ConcreteFactory<T1, T2> : IAbstractFactory<T1, T2>
{
 private IFactory<T1> factory1;
 private IFactory<T2> factory2;

 public ConcreteFactory(IFactory<T1> f1, IFactory<T2> f2)
 {
  factory1 = f1;
  factory2 = f2;
 }

 public T1 Create(TypeToken<T1> token)
 {
  return factory1.Create();
 }

 public T2 Create(TypeToken<T2> token)
 {
  return factory2.Create();
 }
}

public static class ConcretFactory
{
 public static ConcreteFactory<T1, T2> NewFactory<T1, T2>(IFactory<T1> f1, IFactory<T2> f2)
 {
  return new ConcreteFactory<T1, T2>(f1, f2);
 }
}

  注意,我又声明了一个没有类型参数的ConcretFactory类,用一个静态方法来生成泛型ConcretFactory的实例,这是因为使用泛型方法可以推测类型参数,使得我们可以不必输入尖括号或Of语句,而泛型类则没有这个功能。现在大功告成!我们用一个例子来演示这个泛型抽象工厂的工作情况。现在假设我们需要一个生产PC的抽象工厂,需要生产两种抽象产品:处理器和内存。处理器和内存的抽象和具体实现如下:

Processor 和 Ram
public abstract class Processor
{
 public abstract string Model { get; }
}

public abstract class Ram
{
 public abstract int Frequency { get;}
}

public class PentiumProcessor : Processor
{
 public override string Model
 {
  get { return "Pentium Extreme Edition 955"; }
 }
}

public class AthlonProcessor : Processor
{
 public override string Model
 {
  get { return "Athlon 64 X2 FX-60"; }
 }
}

public class DDRRam : Ram
{
 public override int Frequency
 {
  get { return 400; }
 }
}

public class DDR2Ram : Ram
{
 public override int Frequency
 {
  get { return 533; }
 }
}

  下面的代码演示了如何随心所欲生成想要的抽象工厂接口以及快速从现有单一产品工厂组合成特定的具体工厂实现。

class Program
{
 static IAbstractFactory<Processor, Ram> ComputerFactory(string type)
 {
  if (type == "Intel")
  {
   return ConcretFactory.NewFactory( new OpNewFactory<Processor, PentiumProcessor>(),
new OpNewFactory<Ram, DDR2Ram>());
  }
  else if (type == "AMD")
  {
   return ConcretFactory.NewFactory( new OpNewFactory<Processor, AthlonProcessor>(),
   new OpNewFactory<Ram, DDRRam>());
  }

  //unknown type
  return null;
 }

 static void Main(string[] args)
 {
  //Yield a computer of Intel
  IAbstractFactory<Processor, Ram> factory1 = ComputerFactory("Intel");

  Ram ram1 = factory1.Create(TypeToken<Ram>.Instance);
  Processor cup1 = factory1.Create(TypeToken<Processor>.Instance);

  Console.WriteLine("An Intel Computer");
  Console.WriteLine("CPU Model: {0}", cup1.Model);
  Console.WriteLine("Memory Frequency: {0} MHz", ram1.Frequency);

  Console.WriteLine();

  //Yield a computer of AMD
  IAbstractFactory<Processor, Ram> factory2 = ComputerFactory("AMD");

  Ram ram2 = factory2.Create(TypeToken<Ram>.Instance);
  Processor cup2 = factory2.Create(TypeToken<Processor>.Instance);

  Console.WriteLine("An AMD Computer");
  Console.WriteLine("CPU Model: {0}", cup2.Model);
  Console.WriteLine("Memory Frequency: {0} MHz", ram2.Frequency);
 }
}