.NET设计模式(13):享元模式(Flyweight Pattern)

简介:

享元模式(Flyweight Pattern

——.NET设计模式系列之十三
Terrylee 20063
摘要: 面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题。但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面向对象的方式进行操作?
本文试图通过一个简单的字符处理的例子,运用重构的手段,一步步带你走进Flyweight模式,在这个过程中我们一同思考、探索、权衡,通过比较而得出好的实现方式,而不是给你最终的一个完美解决方案。
主要内容:
1.   Flyweight 模式解说
2 .NET中的Flyweight模式
3 Flyweight模式的实现要点
……
概述
面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题。但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面向对象的方式进行操作?
意图
运用共享技术有效地支持大量细粒度的对象。 [GOF  《设计模式》 ]
结构图
1  Flyweight 模式结构图
生活中的例子
享元模式使用共享技术有效地支持大量细粒度的对象。公共交换电话网( PSTN )是享元的一个例子。有一些资源例如拨号音发生器、振铃发生器和拨号接收器是必须由所有用户共享的。当一个用户拿起听筒打电话时,他不需要知道使用了多少资源。对于用户而言所有的事情就是有拨号音,拨打号码,拨通电话。
2   使用拨号音发生器例子的享元模式对象图
Flyweight 模式解说
Flyweight 在拳击比赛中指最轻量级,即“蝇量级”,这里翻译为“享元”,可以理解为共享元对象(细粒度对象)的意思。提到Flyweight模式都会一般都会用编辑器例子来说明,这里也不例外,但我会尝试着通过重构来看待Flyweight模式。考虑这样一个字处理软件,它需要处理的对象可能有单个的字符,由字符组成的段落以及整篇文档,根据面向对象的设计思想和Composite模式,不管是字符还是段落,文档都应该作为单个的对象去看待, 这里只考虑单个的字符,不考虑段落及文档等对象, 于是可以很容易的得到下面的结构图:
3
示意性实现代码:
None.gif //  "Charactor"
None.gif
public   abstract   class  Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
//Fields
InBlock.gif
    protected char _symbol;
InBlock.gif
InBlock.gif    
protected int _width;
InBlock.gif
InBlock.gif    
protected int _height;
InBlock.gif
InBlock.gif    
protected int _ascent;
InBlock.gif
InBlock.gif    
protected int _descent;
InBlock.gif
InBlock.gif    
protected int _pointSize;
InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public abstract void Display();
ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorA"
None.gif
public   class  CharactorA : Charactor
ExpandedBlockStart.gif

InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorA()
ExpandedSubBlockStart.gif    
{
InBlock.gif      
this._symbol = 'A';
InBlock.gif      
this._height = 100;
InBlock.gif      
this._width = 120;
InBlock.gif      
this._ascent = 70;
InBlock.gif      
this._descent = 0;
InBlock.gif      
this._pointSize = 12;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorB"
None.gif
public   class  CharactorB : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorB()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'B';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 140;
InBlock.gif        
this._ascent = 72;
InBlock.gif        
this._descent = 0;
InBlock.gif        
this._pointSize = 10;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorC"
None.gif
public   class  CharactorC : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorC()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'C';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 160;
InBlock.gif        
this._ascent = 74;
InBlock.gif        
this._descent = 0;
InBlock.gif        
this._pointSize = 14;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

好了,现在看到的这段代码可以说是很好地符合了面向对象的思想,但是同时我们也为此付出了沉重的代价,那就是性能上的开销,可以想象,在一篇文档中,字符的数量远不止几百个这么简单,可能上千上万,内存中就同时存在了上千上万个 Charactor 对象,这样的内存开销是可想而知的。进一步分析可以发现,虽然我们需要的 Charactor 实例非常多,这些实例之间只不过是状态不同而已,也就是说这些实例的状态数量是很少的。所以我们并不需要这么多的独立的 Charactor 实例,而只需要为每一种 Charactor 状态创建一个实例,让整个字符处理软件共享这些实例就可以了。看这样一幅示意图:
4
现在我们看到的 A B C 三个字符是共享的,也就是说如果文档中任何地方需要这三个字符,只需要使用共享的这三个实例就可以了。然而我们发现单纯的这样共享也是有问题的。虽然文档中的用到了很多的 A 字符,虽然字符的 symbol 是相同的,它可以共享;但是它们的 pointSize 却是不相同的,即字符在文档中中的大小是不相同的,这个状态不可以共享。为解决这个问题,首先我们将不可共享的状态从类里面剔除出去,即去掉 pointSize 个状态(只是暂时的 J ),类结构图如下所示:
5
示意性实现代码:
None.gif //  "Charactor"
None.gif
public   abstract   class  Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
//Fields
InBlock.gif
    protected char _symbol;
InBlock.gif
InBlock.gif    
protected int _width;
InBlock.gif
InBlock.gif    
protected int _height;
InBlock.gif
InBlock.gif    
protected int _ascent;
InBlock.gif
InBlock.gif    
protected int _descent;
InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public abstract void Display();
ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorA"
None.gif
public   class  CharactorA : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorA()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'A';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 120;
InBlock.gif        
this._ascent = 70;
InBlock.gif        
this._descent = 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorB"
None.gif
public   class  CharactorB : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorB()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'B';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 140;
InBlock.gif        
this._ascent = 72;
InBlock.gif        
this._descent = 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorC"
None.gif
public   class  CharactorC : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorC()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'C';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 160;
InBlock.gif        
this._ascent = 74;
InBlock.gif        
this._descent = 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

好,现在类里面剩下的状态都可以共享了,下面我们要做的工作就是控制 Charactor 类的创建过程,即如果已经存在了“ A ”字符这样的实例,就不需要再创建,直接返回实例;如果没有,则创建一个新的实例。如果把这项工作交给 Charactor 类,即 Charactor 类在负责它自身职责的同时也要负责管理 Charactor 实例的管理工作,这在一定程度上有可能违背类的单一职责原则,因此,需要一个单独的类来做这项工作,引入 CharactorFactory 类,结构图如下:
6
示意性实现代码:
None.gif //  "CharactorFactory"
None.gif
public   class  CharactorFactory
ExpandedBlockStart.gif
{
InBlock.gif    
// Fields
InBlock.gif
    private Hashtable charactors = new Hashtable();
InBlock.gif
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorFactory()
ExpandedSubBlockStart.gif    
{
InBlock.gif        charactors.Add(
"A"new CharactorA());
InBlock.gif        charactors.Add(
"B"new CharactorB());
InBlock.gif        charactors.Add(
"C"new CharactorC());
ExpandedSubBlockEnd.gif    }

InBlock.gif       
InBlock.gif    
// Method
InBlock.gif
    public Charactor GetCharactor(string key)
ExpandedSubBlockStart.gif    
{
InBlock.gif        Charactor charactor 
= charactors[key] as Charactor;
InBlock.gif
InBlock.gif        
if (charactor == null)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
switch (key)
ExpandedSubBlockStart.gif            
{
InBlock.gif                
case "A": charactor = new CharactorA(); break;
InBlock.gif                
case "B": charactor = new CharactorB(); break
InBlock.gif                
case "C": charactor = new CharactorC(); break;
InBlock.gif                
//dot.gifdot.gif
ExpandedSubBlockEnd.gif
            }

InBlock.gif            charactors.Add(key, charactor);
ExpandedSubBlockEnd.gif        }

InBlock.gif        
return charactor;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

到这里已经完全解决了可以共享的状态(这里很丑陋的一个地方是出现了 switch 语句,但这可以通过别的办法消除,为了简单期间我们先保持这种写法)。下面的工作就是处理刚才被我们剔除出去的那些不可共享的状态,因为虽然将那些状态移除了,但是 Charactor 对象仍然需要这些状态,被我们剥离后这些对象根本就无法工作,所以需要将这些状态外部化。首先会想到一种比较简单的解决方案就是对于不能共享的那些状态,不需要去在 Charactor 类中设置,而直接在客户程序代码中进行设置,类结构图如下:
7
示意性实现代码:
None.gif public   class  Program
ExpandedBlockStart.gif
{
InBlock.gif    
public static void Main()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Charactor ca 
= new CharactorA();
InBlock.gif        Charactor cb 
= new CharactorB();
InBlock.gif        Charactor cc 
= new CharactorC();
InBlock.gif
InBlock.gif        
//显示字符
InBlock.gif
InBlock.gif        
//设置字符的大小ChangeSize();
ExpandedSubBlockEnd.gif
    }

InBlock.gif
InBlock.gif    
public void ChangeSize()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
//在这里设置字符的大小
ExpandedSubBlockEnd.gif
    }

ExpandedBlockEnd.gif}

按照这样的实现思路,可以发现如果有多个客户端程序使用的话,会出现大量的重复性的逻辑,用重构的术语来说是出现了代码的坏味道,不利于代码的复用和维护;另外把这些状态和行为移到客户程序里面破坏了封装性的原则。再次转变我们的实现思路,可以确定的是这些状态仍然属于 Charactor 对象,所以它还是应该出现在 Charactor 类中,对于不同的状态可以采取在客户程序中通过参数化的方式传入。类结构图如下:
8
示意性实现代码:
None.gif //  "Charactor"
None.gif
public   abstract   class  Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
//Fields
InBlock.gif
    protected char _symbol;
InBlock.gif
InBlock.gif    
protected int _width;
InBlock.gif
InBlock.gif    
protected int _height;
InBlock.gif
InBlock.gif    
protected int _ascent;
InBlock.gif
InBlock.gif    
protected int _descent;
InBlock.gif
InBlock.gif    
protected int _pointSize;
InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public abstract void SetPointSize(int size);
InBlock.gif    
public abstract void Display();
ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorA"
None.gif
public   class  CharactorA : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorA()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'A';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 120;
InBlock.gif        
this._ascent = 70;
InBlock.gif        
this._descent = 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void SetPointSize(int size)
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._pointSize = size;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol +
InBlock.gif          
"pointsize:" + this._pointSize);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorB"
None.gif
public   class  CharactorB : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorB()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'B';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 140;
InBlock.gif        
this._ascent = 72;
InBlock.gif        
this._descent = 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void SetPointSize(int size)
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._pointSize = size;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol +
InBlock.gif          
"pointsize:" + this._pointSize);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorC"
None.gif
public   class  CharactorC : Charactor
ExpandedBlockStart.gif
{
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorC()
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._symbol = 'C';
InBlock.gif        
this._height = 100;
InBlock.gif        
this._width = 160;
InBlock.gif        
this._ascent = 74;
InBlock.gif        
this._descent = 0;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
//Method
InBlock.gif
    public override void SetPointSize(int size)
ExpandedSubBlockStart.gif    
{
InBlock.gif        
this._pointSize = size;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public override void Display()
ExpandedSubBlockStart.gif    
{
InBlock.gif        Console.WriteLine(
this._symbol +
InBlock.gif          
"pointsize:" + this._pointSize);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "CharactorFactory"
None.gif
public   class  CharactorFactory
ExpandedBlockStart.gif
{
InBlock.gif    
// Fields
InBlock.gif
    private Hashtable charactors = new Hashtable();
InBlock.gif
InBlock.gif    
// Constructor 
InBlock.gif
    public CharactorFactory()
ExpandedSubBlockStart.gif    
{
InBlock.gif        charactors.Add(
"A"new CharactorA());
InBlock.gif        charactors.Add(
"B"new CharactorB());
InBlock.gif        charactors.Add(
"C"new CharactorC());
ExpandedSubBlockEnd.gif    }

InBlock.gif       
InBlock.gif    
// Method
InBlock.gif
    public Charactor GetCharactor(string key)
ExpandedSubBlockStart.gif    
{
InBlock.gif        Charactor charactor 
= charactors[key] as Charactor;
InBlock.gif
InBlock.gif        
if (charactor == null)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
switch (key)
ExpandedSubBlockStart.gif            
{
InBlock.gif                
case "A": charactor = new CharactorA(); break;
InBlock.gif                
case "B": charactor = new CharactorB(); break
InBlock.gif                
case "C": charactor = new CharactorC(); break;
InBlock.gif                
//dot.gifdot.gif
ExpandedSubBlockEnd.gif
            }

InBlock.gif            charactors.Add(key, charactor);
ExpandedSubBlockEnd.gif        }

InBlock.gif        
return charactor;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
public   class  Program
ExpandedBlockStart.gif
{
InBlock.gif    
public static void Main()
ExpandedSubBlockStart.gif    
{
InBlock.gif        CharactorFactory factory 
= new CharactorFactory();
InBlock.gif
InBlock.gif        
// Charactor "A"
InBlock.gif
        CharactorA ca = (CharactorA)factory.GetCharactor("A");
InBlock.gif        ca.SetPointSize(
12);
InBlock.gif        ca.Display();
InBlock.gif        
InBlock.gif        
// Charactor "B"
InBlock.gif
        CharactorB cb = (CharactorB)factory.GetCharactor("B");
InBlock.gif        ca.SetPointSize(
10);
InBlock.gif        ca.Display();
InBlock.gif
InBlock.gif        
// Charactor "C"
InBlock.gif
        CharactorC cc = (CharactorC)factory.GetCharactor("C");
InBlock.gif        ca.SetPointSize(
14);
InBlock.gif        ca.Display();
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

可以看到这样的实现明显优于第一种实现思路。好了,到这里我们就到到了通过 Flyweight 模式实现了优化资源的这样一个目的。在这个过程中,还有如下几点需要说明:
1 .引入 CharactorFactory 是个关键,在这里创建对象已经不是 new 一个 Charactor 对象那么简单,而必须用工厂方法封装起来。
2 .在这个例子中把 Charactor 对象作为 Flyweight 对象是否准确值的考虑,这里只是为了说明 Flyweight 模式,至于在实际应用中,哪些对象需要作为 Flyweight 对象是要经过很好的计算得知,而绝不是凭空臆想。
3 .区分内外部状态很重要,这是享元对象能做到享元的关键所在。
到这里,其实我们的讨论还没有结束。有人可能会提出如下问题,享元对象( Charactor )在这个系统中相对于每一个内部状态而言它是唯一的,这跟单件模式有什么区别呢?这个问题已经很好回答了,那就是单件类是不能直接被实例化的,而享元类是可以被实例化的。事实上在这里面真正被设计为单件的应该是享元工厂(不是享元)类,因为如果创建很多个享元工厂的实例,那我们所做的一切努力都是白费的,并没有减少对象的个数。修改后的类结构图如下:
9
示意性实现代码:
None.gif //  "CharactorFactory"
None.gif
public   class  CharactorFactory
ExpandedBlockStart.gif
{
InBlock.gif    
// Fields
InBlock.gif
    private Hashtable charactors = new Hashtable();
InBlock.gif
InBlock.gif    
private CharactorFactory instance;
InBlock.gif    
// Constructor 
InBlock.gif
    private CharactorFactory()
ExpandedSubBlockStart.gif    
{
InBlock.gif        charactors.Add(
"A"new CharactorA());
InBlock.gif        charactors.Add(
"B"new CharactorB());
InBlock.gif        charactors.Add(
"C"new CharactorC());
ExpandedSubBlockEnd.gif    }

InBlock.gif    
InBlock.gif    
// Property
InBlock.gif
    public CharactorFactory Instance
ExpandedSubBlockStart.gif    
{
InBlock.gif        
get 
ExpandedSubBlockStart.gif        
{
InBlock.gif            
if (instance != null)
ExpandedSubBlockStart.gif            
{
InBlock.gif                instance 
= new CharactorFactory();
ExpandedSubBlockEnd.gif            }

InBlock.gif            
return instance;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// Method
InBlock.gif
    public Charactor GetCharactor(string key)
ExpandedSubBlockStart.gif    
{
InBlock.gif        Charactor charactor 
= charactors[key] as Charactor;
InBlock.gif
InBlock.gif        
if (charactor == null)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
switch (key)
ExpandedSubBlockStart.gif            
{
InBlock.gif                
case "A": charactor = new CharactorA(); break;
InBlock.gif                
case "B": charactor = new CharactorB(); break
InBlock.gif                
case "C": charactor = new CharactorC(); break;
InBlock.gif                
//dot.gifdot.gif
ExpandedSubBlockEnd.gif
            }

InBlock.gif            charactors.Add(key, charactor);
ExpandedSubBlockEnd.gif        }

InBlock.gif        
return charactor;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

.NET 框架中的Flyweight
Flyweight 更多时候的时候一种底层的设计模式,在我们的实际应用程序中使用的并不是很多。在.NET中的String类型其实就是运用了Flyweight模式。可以想象,如果每次执行string s1 = “abcd”操作,都创建一个新的字符串对象的话,内存的开销会很大。所以.NET中如果第一次创建了这样的一个字符串对象s1,下次再创建相同的字符串s2时只是把它的引用指向“abcd”,这样就实现了“abcd”在内存中的共享。可以通过下面一个简单的程序来演示s1s2的引用是否一致:
None.gif public   class  Program
ExpandedBlockStart.gif
{
InBlock.gif    
public static void Main(string[] args)
ExpandedSubBlockStart.gif    
{
InBlock.gif        
string s1 = "abcd";
InBlock.gif        
string s2 = "abcd";
InBlock.gif
InBlock.gif        Console.WriteLine(Object.ReferenceEquals(s1,s2));
InBlock.gif
InBlock.gif        Console.ReadLine();
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

可以看到,输出的结果为 True 。但是大家要注意的是如果再有一个字符串 s3 ,它的初始值为“ ab ”,再对它进行操作 s3 = s3 + “cd” ,这时虽然 s1 s3 的值相同,但是它们的引用是不同的。关于 String 的详细情况大家可以参考 SDK ,这里不再讨论了。
效果及实现要点
1 .面向对象很好的解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。 Flyweight 设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
2 Flyweight 采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
3 享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。另外它将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
适用性
当以下所有的条件都满足时,可以考虑使用享元模式:
1、    一个系统有大量的对象。
2、    这些对象耗费大量的内存。
3、    这些对象的状态中的大部分都可以外部化。
4、    这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。
5、    软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
满足以上的这些条件的系统可以使用享元对象。最后,使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。
总结
Flyweight 模式解决的是由于大量的细粒度对象所造成的内存开销的问题,它在实际的开发中并不常用,但是作为底层的提升性能的一种手段却很有效。
参考资料
Erich Gamma 等,《设计模式:可复用面向对象软件的基础》,机械工业出版社
Robert C.Martin ,《敏捷软件开发:原则、模式与实践》,清华大学出版社
阎宏,《 Java 与模式》,电子工业出版社
Alan Shalloway James R. Trott ,《 Design Patterns Explained 》,中国电力出版社
MSDN WebCast  C# 面向对象设计模式纵横谈 (12) Flyweight 享元模式 ( 结构型模式 )
[url]http://www.dofactory.com/[/url]











本文转自lihuijun51CTO博客,原文链接:  http://blog.51cto.com/terrylee/67761 ,如需转载请自行联系原作者

相关文章
|
1月前
|
设计模式 存储 Java
23种设计模式,享元模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享技术有效地支持大量细粒度对象的重用。这个模式在处理大量对象时非常有用,特别是当这些对象中的许多实例实际上可以共享相同的状态时,从而可以减少内存占用,提高程序效率
37 4
|
4月前
|
设计模式 算法 Java
行为型设计模式-策略模式(Strategy Pattern)
行为型设计模式-策略模式(Strategy Pattern)
|
4月前
|
设计模式 算法
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
41 1
设计模式 - 行为型模式_ 访问者模式Visitor Pattern
|
4月前
|
设计模式 Java 应用服务中间件
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
37 1
|
4月前
|
设计模式 缓存 安全
设计模式 - 创建型模式_ 单例模式 Singleton Pattern
设计模式 - 创建型模式_ 单例模式 Singleton Pattern
40 0
|
26天前
|
设计模式 存储 Java
小谈设计模式(27)—享元模式
小谈设计模式(27)—享元模式
|
27天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
16 1
|
2月前
|
设计模式 缓存 Java
设计模式之享元模式
设计模式之享元模式
|
2月前
|
设计模式 存储 缓存
【设计模式】享元模式
【设计模式】享元模式
|
4月前
|
设计模式 存储 缓存
聊聊Java设计模式-享元模式
享元(Flyweight)模式:顾名思义就是**被共享的单元**。意图是复用对象,节省内存,提升系统的访问效率。比如在红白机冒险岛游戏中的背景花、草、树木等对象,实际上是可以多次被不同场景所复用共享,也是为什么以前的游戏占用那么小的内存,却让我们感觉地图很大的原因。
17 3
聊聊Java设计模式-享元模式