Flyweight模式详解--设计模式(9)
来源:程序员人生 发布时间:2016-08-29 09:03:57 阅读次数:2447次
Flyweight模式产生缘由:
在面向对象系统的设计何实现中,创建对象是最为常见的操作。这里面就有1个问题:如果1个利用程序使用了太多的对象,就会造成很大的存储开消。特别是对大量轻量级(细粒度)的对象,比如在文档编辑器的设计进程中,我们如果为没有字母创建1个对象的话,系统可能会由于大量的对象而造成存储开消的浪费。例如1个字母“a”在文档中出现了100000次,而实际上我们可让这1万个字母“a”同享1个对象,固然由于在不同的位置可能字母“a”有不同的显示效果(例如字体和大小等设置不同),在这类情况我们可以为将对象的状态分为“外部状态”和“内部状态”,将可以被同享(不会变化)的状态作为内部状态存储在对象中,而外部对象(例如上面提到的字体、大小等)我们可以在适当的时候将外部对象最为参数传递给对象(例如在显示的时候,将字体、大小等信息传递给对象)。
Flyweight享元模式作用:
Flyweight模式是1个提高程序效力和性能的模式,会大大加快程序的运行速度。利用场合很多:比如你要从1个数据库中读取1系列字符串,这些字符串中有许多是重复的,那末我们可以将这些字符串贮存在Flyweight池(pool)中。就是先创建1个的原始模型,然后随着不同场合和环境,再产生各具特点的具体模型,很明显,在这里需要产生不同的新对象,所以Flyweight模式中常出现Factory模式。Flyweight的内部状态是用来同享的,Flyweight
factory负责保护1个Flyweightpool(模式池)来寄存内部状态的对象。
Flyweight享元模式的使用处景:
当以下所有的条件都满足时,可以斟酌使用享元模式:
(1).1个系统有大量的对象。
(2).这些对象耗费大量的内存。
(3).这些对象的状态中的大部份都可之外部化。
(4).这些对象可以依照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每个组都可以仅用1个对象代替。
(5).软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
满足以上的这些条件的系统可使用享元对象。
最后,使用享元模式需要保护1个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供同享时才值得使用享元模式。
单纯Flyweight享元模式模式典型的UML结构图如图1所示:
单纯Flyweight享元模式抽象基类及接口:
抽象享元(Flyweight)角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过调用商业方法以参数情势传入。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内同享的。
享元工厂(FlyweightFactory)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地同享。当1个客户端对象调用1个享元对象的时候,享元工厂角色会检查系统中是不是已有1个复合要求的享元对象。如果已有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有1个适当的享元对象的话,享元工厂角色就应 当创建1个适合的享元对象。
客户端(Client)角色:本角色需要保护1个对所有享元对象的援用。本角色需要自行存储所有享元对象的外蕴状态。
单纯Flyweight享元模式典型的示例代码以下:
using System;
using System.Collections;
// 享元工厂类
class FlyweightFactory
{
// 域
private Hashtable flyweights = new Hashtable();
//构造函数
public FlyweightFactory()
{
flyweights.Add("X", new ConcreteFlyweight());
flyweights.Add("Y", new ConcreteFlyweight());
flyweights.Add("Z", new ConcreteFlyweight());
}
// 方法
public Flyweight GetFlyweight(string key)
{
return((Flyweight)flyweights[key]);
}
}
// 轻量级选手
abstract class Flyweight
{
// 方法
abstract public void Operation(int extrinsicstate);
}
// 具体享元类
class ConcreteFlyweight : Flyweight
{
private string intrinsicstate = "A";
// 方法
override public void Operation(int extrinsicstate)
{
Console.WriteLine("ConcreteFlyweight: intrinsicstate {0}, extrinsicstate {1}",
intrinsicstate, extrinsicstate);
}
}
public class Client
{
public static void Main(string[] args)
{
// 任意状态
int extrinsicstate = 22;
FlyweightFactory f = new FlyweightFactory();
//使用不同的轻量级选手实例
Flyweight fx = f.GetFlyweight("X");
fx.Operation(--extrinsicstate);
Flyweight fy = f.GetFlyweight("Y");
fy.Operation(--extrinsicstate);
Flyweight fz = f.GetFlyweight("Z");
fz.Operation(--extrinsicstate);
}
}
复合Flyweight享元模式典型的UML结构图如图2所示:
复合Flyweight享元模式抽象基类及接口:
抽象享元角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是其实不强迫子类实行同享,因此并不是所有的享元对象都是可以同享的。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内同享。有时候具体享元角色又叫做单纯具体享元角色,由于复合享元角色是由单纯具体享元角色通过复合而成的。
复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以同享的,但是1个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可同享的享元对象。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地同享。当1个客户端对象要求1个享元对象的时候,享元工厂角色需要检查系统中是不是已有1个符合要求的享元对象,如果已有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有1个适当的享元对象的话,享元工厂角色就应当创建1个新的适合的享元对象。
客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。
示例1:1个咖啡的例子
在这个咖啡摊(CoffeeStall)所使用的系统里,有1系列的咖啡"风味(Flavor)"。客人到摊位上购买咖啡,所有的咖啡均放在台子上,客人自己拿到咖啡后就离开摊位。咖啡有内蕴状态,也就是咖啡的风味;咖啡没有环境因素,也就是说没有外蕴状态。如果系统为每杯咖啡都创建1个独立的对象的话,那末就需要创建出很多的细小对象来。这样就不如把咖啡依照种类(即"风味")划分,每种风味的咖啡只创建1个对象,并实行同享。
使用咖啡摊主的语言来说,所有的咖啡都可按"风味"划分成如Capucino、Espresso等,每种风味的咖啡不论卖出多少杯,都是全同、不可分辨的。所谓同享,就是咖啡风味的同享,制造方法的同享等。因此,享元模式对咖啡摊来讲,就意味着不需要为每份单独调制。摊主可以在需要时,1次性地调制出足够1天出售的某1种风味的咖啡。
很明显,这里合适使用单纯享元模式。系统的设计以下:
using System;
using System.Collections;
public abstract class Order
{
// 将咖啡卖给客人
public abstract void Serve();
// 返回咖啡的名字
public abstract string GetFlavor();
}
public class Flavor : Order
{
private string flavor;
// 构造函数,内蕴状态以参数方式传入
public Flavor(string flavor)
{
this.flavor = flavor;
}
// 返回咖啡的名字
public override string GetFlavor()
{
return this.flavor;
}
// 将咖啡卖给客人
public override void Serve()
{
Console.WriteLine("Serving flavor " + flavor);
}
}
public class FlavorFactory
{
private 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 = 0;
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++;
}
}
示例2:咖啡店的例子
在前面的咖啡摊项目里,由于没有供客人坐的桌子,所有的咖啡均没有环境的影响。换言之,咖啡唯一内蕴状态,也就是咖啡的种类,而没有外蕴状态。下面斟酌1个范围稍稍大1点的咖啡屋(Coffee Shop)项目。屋子里有很多的桌子供客人坐,系统除需要提供咖啡的"风味"以外,还需要跟踪咖啡被送到哪个桌位上,因此,咖啡就有了桌子作为外蕴状态。
由于外蕴状态的存在,没有外蕴状态的单纯享元模式不再符合要求。系统的设计可以利用有外蕴状态的单纯享元模式。系统的代码以下:
using System;
using System.Collections;
public abstract class Order
{
// 将咖啡卖给客人
public abstract void Serve(Table table);
// 返回咖啡的名字
public abstract string GetFlavor();
}
public class Flavor : Order
{
private string flavor;
// 构造函数,内蕴状态以参数方式传入
public Flavor(string flavor)
{
this.flavor = flavor;
}
// 返回咖啡的名字
public override string GetFlavor()
{
return this.flavor;
}
// 将咖啡卖给客人
public override void Serve(Table table)
{
Console.WriteLine("Serving table {0} with flavor {1}", table.Number, flavor);
}
}
public class FlavorFactory
{
private 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 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 = 0;
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));
}
}
Flyweight享元模式使用总结:
(1).Flyweight享元模式的优点在于它大幅度地下降内存中对象的数量。但是,它做到这1点所付出的代价也是很高的:
(2).Flyweight享元模式使得系统更加复杂。为了使对象可以同享,需要将1些状态外部化,这使得程序的逻辑复杂化。
(3).Flyweight享元模式将享元对象的状态外部化,而读取外部状态使得运行时间略微变长。
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠