Flyweight模式实例

原创 2004年05月19日 22:29:00

                                                           Flyweight模式<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

Flyweight模式用来避免相似类的大量重复。在编程时存在这样一种情形:你似乎需要产生大量的,很小的类的实例来展现数据。但在某些情况下,如果你能意识到其实许多的实例除了少量参数外,本质上是相同的,你将能极大的减少需要实例化的不同类——如果你能将这些变量移到类的实例之外,作为方法的一部分将他们传过来,实例的个数就可以通过共享而极大的减少。

Flyweight设计模式就是提供了处理这种类的一种方法。Flyweight就是指体现实例唯一性的内部数据以及作为参数传进来的外部数据。Flyweight模式适用于小而精的类,像屏幕上单个的字符或是图标。例如,你在windows的屏幕上绘制了一系列的图标,每一个代表了一个人或是数据文件的文件夹,见下图:

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

在这种情况下,每个文件夹拥有一个类的实例以记住人名等信息以及图标的屏幕位置并不是一个好的方法。在通常情况下,这些图标都是一个很小的图片,而绘出它们的位置是基于window的大小动态计算出来的。

在设计模式的另外一个例子中,文档中的每个字符都是一个字符类的实例,但是他们在屏幕上的位置被视为外部数据。在这种情况下每个字符只需拥有一个实例,而不是每次字符的展现都需要一个。

Flyweight是类的实例共享。

首先看起来似乎每个类都像是个singleton,但实际上可能有少量而非一个的实例,比如一个类可能既有所有字符的一个实例,也有所有图标的一个实例(也比如下面将要谈到的文件夹类,将拥有选中和未选中文件夹这样两个实例)。需要分配的实例的数量必须是确实需要的类实例的数量,而这些工作通常由FlyweightFactory类来完成。这个工厂类通常是个singleton,因为它需要跟踪某个特定的实例是否被产生。它返回一个新的实例,如果实例已经存在则返回一个引用。

为了决定你的部分程序是否适用于Flyweight,你应考虑是否可能从类中移出一些数据作为外部数据。如果这样可以极大的减少需要维护的不同类实例的数量,那么这就是一个Flyweight适用的地方。

实例

假设我们想为组织的每一个人绘制小的文件夹图标,下面标有人名。如果这是个大的组织,那么就可能要大量的这种图标,然而实际上所有的图标都是相同的图片。即使我们需要两个图标——一个表明选中,一个表明未选中——不同图标的数量也是很小的。在这样的系统中,每个人对应一个图标对象,带有姓名,选中状态等信息,是对资源的极大浪费。

因此,现在我们建立一个FolderFactory来返回选中或未选中的文件夹类,而不是建立多余的实例。由于这是一个简单的例子,我们仅在开始就将他们建立并返回他们中的一个。

public class FolderFactory {

      private Folder selFolder, unselFolder;

      //-----

      public FolderFactory()            {

             //create the two folders

             selFolder = new Folder(Color.Brown);

             unselFolder = new Folder(Color.Bisque);

      }

      //-----

      public Folder getFolder(bool selected) {

             if(selected)

                    return selFolder;

             else

                    return unselFolder;

      }

}

如果有多个实例存在的情况下,工厂应该有一张表来记录哪些已经被创建,只有表中不存在的才能被创建。

我们使用Flyweight来做的唯一事情就是在需要绘制文件夹的时候将姓名等信息传递给它。姓名等是外部数据,因此我们能够共享文件夹对象,在这种情况下,只用建立两个实例。以下完成的文件夹类仅简单建立了一个文件夹实例,拥有两个背景颜色,以及一个public的绘制方法用来在你所指定的地方绘制文件夹。

public class Folder {

      //Draws a folder at the specified coordinates

      private const int w  = 50;

      private const int h = 30;

      private Pen blackPen, whitePen;

      private Pen grayPen;

 

      private SolidBrush backBrush, blackBrush;

      private Font fnt;

      //------

      public Folder(Color col) {

             backBrush = new SolidBrush(col);

             blackBrush = new SolidBrush(Color.Black);

             blackPen = new Pen(Color.Black);

             whitePen = new Pen(Color.White);

             grayPen = new Pen(Color.Gray);

             fnt = new Font("Arial", 12);

      }

      //-----

      public void draw(Graphics g, int x, int y, string title) {

             //color folder

             g.FillRectangle(backBrush, x, y, w, h);

             //outline in black

             g.DrawRectangle(blackPen, x, y, w, h);

             //left 2 sides have white line

             g.DrawLine(whitePen, x + 1, y + 1, x + w - 1, y + 1);

             g.DrawLine(whitePen, x + 1, y, x + 1, y + h);

             //draw tab

             g.DrawRectangle(blackPen, x + 5, y - 5, 15, 5);

             g.FillRectangle(backBrush, x + 6, y - 4, 13, 6);

             //gray line on right and bottom

             g.DrawLine(grayPen, x, y + h - 1, x + w, y + h - 1);

             g.DrawLine(grayPen, x + w - 1, y, x + w - 1,

              y + h - 1);

             g.DrawString(title, fnt, blackBrush, x, y + h + 5);

      }

}

要使用一个这样的Flyweight类,你的主程序必须计算出每个文件夹的位置来作为Flyweight的绘制程序的一部分,然后将其它信息传递给文件夹实例。由于你可能根据屏幕的维度需要一个不同的布局,因此你并不希望在你每次绘制的时候都告诉实例新的位置,转而,我们在绘制程序中动态的计算它。

按照上面的设计,我们将在开始就建立一个文件夹列表数组,并简单的扫描该数组以绘制每一个文件夹。这样的数组不同于我们开篇谈到的“大量相似类的实例”,它不是一种浪费,因为它实际上只是对两个文件夹实例的一组引用。由于我们想将文件夹显示为“已选择”,并且可根据选择动态改变该状态,我们将使用文件夹工厂在每个时候自动返回适当的实例。

在我们的显示程序中有两处需要计算文件夹的位置:当绘制它的时候和鼠标悬浮在上面的时候。因此,将位置代码抽象为一个Positioner类将会比较方便。

public class Positioner   {

      private const int pLeft = 30;

      private const int pTop  = 30;

      private const int HSpace = 70;

      private const int VSpace = 80;

      private const int rowMax = 2;

      private int x, y, cnt;

      //-----

      public Positioner() {

             reset();

      }

      //-----

      public void reset() {

             x = pLeft;

             y = pTop;

             cnt = 0;

      }

      //-----

      public int nextX() {

             return x;

      }

      //-----

      public void incr() {

             cnt++;

             if (cnt > rowMax) { //reset to start new row

                    cnt = 0;

                    x = pLeft;

                    y += VSpace;

             }

             else {

                    x += HSpace;

             }

      }

      //-----

      public int nextY() {

             return y;

      }

}

因此我们就能写出更加简单的绘图程序:

private void picPaint(object sender,  PaintEventArgs e ) {

      Graphics g = e.Graphics;

      posn.reset ();

      for(int i = 0; i < names.Count; i++) {

             fol = folFact.getFolder(selectedName.Equals(

                    (string)names[i]));

             fol.draw(g, posn.nextX() , posn.nextY (),

                    (string)names[i]);

             posn.incr();

             }

}

 

类图

下图展示了这些类之间的交互

(待续)

C++亨元模式详解--设计模式(10)

Flyweight模式产生原因:        在面向对象系统的设计何实现中,创建对象是最为常见的操作。这里面就有一个问题:如果一个应用程序使用了太多的对象,就会造成很大的存储开销。特别是对于大量轻量...
  • fanyun_01
  • fanyun_01
  • 2016年06月29日 08:47
  • 1718

常见设计模式解析和实现(C++)FlyWeight模式

作用:运用共享技术有效地支持大量细粒度的对象   UML结构图:   解析: Flyweight模式在大量使用一些可以被共享的对象的时候使用。比如,在QQ聊天...
  • zhuhongshu
  • zhuhongshu
  • 2014年09月18日 23:42
  • 1169

浅谈JAVA设计模式之——享元模式(Flyweight)

一、概述 运用共享技术有效地支持大量细粒度的对象。 二、适用性 当都具备下列情况时,使用Flyweight模式:        1.一个应用程序使用了大量的对象。        2.完全由于使用大...
  • l1028386804
  • l1028386804
  • 2015年05月07日 23:46
  • 1690

浅谈Java设计模式(十二)享元模式(Flyweight)

前言: 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。 FlyWeightFactory负责创建和管理享元单元,当一...
  • caihongdao123
  • caihongdao123
  • 2016年07月11日 10:02
  • 2415

JAVA设计模式之享元模式

解释一下概念:也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象...
  • jason0539
  • jason0539
  • 2014年04月04日 07:34
  • 31628

Android设计模式之享元模式(Flyweight Pattern)

一 享元模式介绍:1.1 使用享元模式有什么好处? 享元模式是对象池的一种实现,主要目的是用来尽可能减少内存使用量。适合于存在大量重复对象的场景,来缓存可共享的对象,达到对象共享,避免创建过多对象...
  • happy_horse
  • happy_horse
  • 2016年04月14日 15:56
  • 961

boost::flyweight使用

今天在看书的时候看到了boost.flyweight这个库,设计模式里面讲到了flyweight是一种设计模式,那这个库是干啥的呢?       什么是flyweight?       简单点说,...
  • zirandeai
  • zirandeai
  • 2012年12月12日 22:06
  • 1761

PHP设计模式——享元模式

声明:本系列博客参考资料《大话设计模式》,作者程杰。         享元模式使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内...
  • jhq0113
  • jhq0113
  • 2015年05月16日 13:06
  • 1809

flyweight模式

运用共享技术有效地支持大量细粒度的对象。• 一个应用程序使用了大量的对象。• 完全由于使用大量的对象,造成很大的存储开销。• 对象的大多数状态都可变为外部状态。• 如果删除对象的外部状态,那么可以用相...
  • majcos
  • majcos
  • 2006年10月04日 20:28
  • 415

FlyWeight模式

 Flyweight模式1.1 问题       在面向对象系统的设计何实现中,创建对象是最为常见的操作。这里面就有一个问题:如果一个应用程序使用了太多的对象,就会造成很大的存储开销。特别是对于大量轻...
  • adcxf
  • adcxf
  • 2008年04月19日 08:26
  • 535
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Flyweight模式实例
举报原因:
原因补充:

(最多只允许输入30个字)