一 、 一个咖啡摊的例子
在这个咖啡摊(Coffee Stall)所使用的系统里,有一系列的咖啡"风味(Flavor)"。客人到摊位上购买咖啡,所有
的咖啡均放在台子上,客人自己拿到咖啡后就离开摊位。咖啡有内蕴状态,也就是咖啡的风味;咖啡没有环境因
素,也就是说没有外蕴状态。如果系统为每一杯咖啡都创建一个独立的对象的话,那么就需要创建出很多的细小
对象来。这样就不如把咖啡按照种类(即"风味")划分,每一种风味的咖啡只创建一个对象,并实行共享。
使用咖啡摊主的语言来讲,所有的咖啡都可按"风味"划分成如 Capucino、Espresso 等,每一种风味的咖啡不
论卖出多少杯,都是全同、不可分辨的。所谓共享,就是咖啡风味的共享,制造方法的共享等。因此,享元模式
对咖啡摊来说,就意味着不需要为每一份单独调制。摊主可以在需要时,一次性地调制出足够一天出售的某一种
风味的咖啡。
很显然,这里适合使用单纯享元模式。系统的设计如下:
[C#] 纯文本查看 复制代码 using System;
using System.Collections;
namespace JobApi
{
public abstract class Order
{
// 将咖啡卖给客人
public abstract void Serve();
// 返回咖啡的名字
public abstract string GetFlavor();
}
public class Flavor : Order
{
private readonly string flavor;
// 构造函数,内蕴状态以参数方式传入
public Flavor(string flavor)
{
this.flavor = flavor;
}
// 返回咖啡的名字
public override string GetFlavor()
{
return flavor;
}
// 将咖啡卖给客人
public override void Serve()
{
Console.WriteLine("Serving flavor " + flavor);
}
}
public class FlavorFactory
{
private readonly Hashtable flavors = new Hashtable();
public Order GetOrder(string key)
{
if (!flavors.ContainsKey(key))
flavors.Add(key, new Flavor(key));
return ((Order)flavors[key]);
}
public int GetTotalFlavorsMade()
{
return flavors.Count;
}
}
public class Client
{
private static FlavorFactory flavorFactory;
private static int ordersMade;
public static void Main(string[] args)
{
flavorFactory = new FlavorFactory();
TakeOrder("Black Coffee");
TakeOrder("Capucino");
TakeOrder("Espresso");
TakeOrder("Capucino");
TakeOrder("Espresso");
TakeOrder("Black Coffee");
TakeOrder("Espresso");
TakeOrder("Espresso");
TakeOrder("Black Coffee");
TakeOrder("Capucino");
TakeOrder("Capucino");
TakeOrder("Black Coffee");
Console.WriteLine("\nTotal Orders made: " + ordersMade);
Console.WriteLine("\nTotal Flavor objects made: " +
flavorFactory.GetTotalFlavorsMade());
}
private static void TakeOrder(string aFlavor)
{
Order o = flavorFactory.GetOrder(aFlavor);
// 将咖啡卖给客人
o.Serve();
ordersMade++;
}
}
}
二 、 咖啡屋的例子
在前面的咖啡摊项目里,由于没有供客人坐的桌子,所有的咖啡均没有环境的影响。换言之,咖啡仅有内蕴状态,
也就是咖啡的种类,而没有外蕴状态。
下面考虑一个规模稍稍大一点的咖啡屋(Coffee Shop)项目。屋子里有很多的桌子供客人坐,系统除了需要提供
咖啡的"风味"之外,还需要跟踪咖啡被送到哪一个桌位上,因此,咖啡就有了桌子作为外蕴状态。
由于外蕴状态的存在,没有外蕴状态的单纯享元模式不再符合要求。系统的设计可以利用有外蕴状态的单纯享元
模式。系统的代码如下:
[C#] 纯文本查看 复制代码 using System;
using System.Collections;
namespace JobApi
{
public abstract class Order
{
// 将咖啡卖给客人
public abstract void Serve(Table table);
// 返回咖啡的名字
public abstract string GetFlavor();
}
public class Flavor : Order
{
private readonly string flavor;
// 构造函数,内蕴状态以参数方式传入
public Flavor(string flavor)
{
this.flavor = flavor;
}
// 返回咖啡的名字
public override string GetFlavor()
{
return flavor;
}
// 将咖啡卖给客人
public override void Serve(Table table)
{
Console.WriteLine("Serving table {0} with flavor {1}", table.Number, flavor);
}
}
public class FlavorFactory
{
private readonly Hashtable flavors = new Hashtable();
public Order GetOrder(string key)
{
if (!flavors.ContainsKey(key))
flavors.Add(key, new Flavor(key));
return ((Order)flavors[key]);
}
public int GetTotalFlavorsMade()
{
return flavors.Count;
}
}
public class Table
{
private readonly int number;
public Table(int number)
{
this.number = number;
}
public int Number
{
get { return number; }
}
}
public class Client
{
private static FlavorFactory flavorFactory;
private static int ordersMade;
public static void Main(string[] args)
{
flavorFactory = new FlavorFactory();
TakeOrder("Black Coffee");
TakeOrder("Capucino");
TakeOrder("Espresso");
TakeOrder("Capucino");
TakeOrder("Espresso");
TakeOrder("Black Coffee");
TakeOrder("Espresso");
TakeOrder("Espresso");
TakeOrder("Black Coffee");
TakeOrder("Capucino");
TakeOrder("Capucino");
TakeOrder("Black Coffee");
Console.WriteLine("\nTotal Orders made: " + ordersMade);
Console.WriteLine("\nTotal Flavor objects made: " +
flavorFactory.GetTotalFlavorsMade());
}
private static void TakeOrder(string aFlavor)
{
Order o = flavorFactory.GetOrder(aFlavor);
// 将咖啡卖给客人
o.Serve(new Table(++ordersMade));
}
}
}
三 、 享元模式应当在什么情况下使用
当以下所有的条件都满足时,可以考虑使用享元模式:
 一个系统有大量的对象。
 这些对象耗费大量的内存。
 这些对象的状态中的大部分都可以外部化。
 这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除
时,每一个组都可以仅用一个对象代替。
 软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
满足以上的这些条件的系统可以使用享元对象。
最后,使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多
的享元实例可供共享时才值得使用享元模式。
四 、 享元模式的优点和缺点
享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:
 享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部
化,这使得程序的逻辑复杂化。
 享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变
长。
|