11-Flyweight: too many objects

The odd thing about flyweight, in the company of the other design patterns, is that it’s a performance hack. It’s generally ideal to simply make an object for every item in your system, but some problems generate a prohibitive number of objects, which may result in excessive slowness or running out of memory.

Flyweight solves this problem by reducing the number of objects. To do this, you externalize some of the data in an object, so that you can pretend that you have more objects than you really do. However, this adds complexity to the interface for using such objects, because you must pass in additional information to method calls in order to tell the method how to find the externalized information.

As a very simple example, consider a DataPoint object that holds an int , a float , and an id that carries the object number. Suppose you need to create a million of these objects, and then manipulate them, like so:

//: flyweight:ManyObjects.java

 

class DataPoint {

  private static int count = 0;

  private int id = count++;

  private int i;

  private float f;

  public int getI() { return i; }

  public void setI(int i) { this.i = i; }

  public float getF() { return f; }

  public void setF(float f) { this.f = f; }

  public String toString() {

    return "id: " + id + ", i = " + i + ", f = " + f;

  }

}

 

public class ManyObjects {

  static final int size = 1000000;

  public static void main(String[] args) {

    DataPoint[] array = new DataPoint[size];

    for(int i = 0; i < array.length; i++)

      array[i] = new DataPoint();

    for(int i = 0; i < array.length; i++) {

      DataPoint dp = array[i];

      dp.setI(dp.getI() + 1);

      dp.setF(47.0f);

    }

    System.out.println(array[size -1]);

  }

} ///:~

 

Depending on your computer, this program may take several seconds to run. More complex objects and more involved operations may cause the overhead to become untenable. To solve the problem the DataPoint can be reduced from a million objects to one object by externalizing the data held in the DataPoint :

//: flyweight:FlyWeightObjects.java

 

class ExternalizedData {

  static final int size = 5000000;

  static int[] id = new int[size];

  static int[] i = new int[size];

  static float[] f = new float[size];

  static {

    for(int i = 0; i < size; i++)

      id[i] = i;

  }

}

 

class FlyPoint {

  private FlyPoint() {}

  public static int getI(int obnum) {

    return ExternalizedData.i[obnum];

  }

  public static void setI(int obnum, int i) {

    ExternalizedData.i[obnum] = i;

  }

  public static float getF(int obnum) {

    return ExternalizedData.f[obnum];

  }

  public static void setF(int obnum, float f) {

    ExternalizedData.f[obnum] = f;

  }

  public static String str(int obnum) {

    return "id: " +

      ExternalizedData.id[obnum] +

      ", i = " +

      ExternalizedData.i[obnum] +

      ", f = " +

      ExternalizedData.f[obnum];

  }

}

 

public class FlyWeightObjects {

  public static void main(String[] args) {

    for(int i = 0; i < ExternalizedData.size; i++) {

      FlyPoint.setI(i, FlyPoint.getI(i) + 1);

      FlyPoint.setF(i, 47.0f);

    }

    System.out.println(

      FlyPoint.str(ExternalizedData.size -1));

  }

} ///:~

 

Since all the data is now in ExternalizedData , each call to a FlyPoint method must include the index into ExternalizedData . For consistency, and to remind the reader of the similarity with the implicit this pointer in method calls, the “this index” is passed in as the first argument.

Naturally, it’s worth repeating admonishments against premature optimization. “First make it work, then make it fast – if you have to.” Also, a profiler is the tool to use for discovering performance bottlenecks, not guesswork.

//这个例子比较直接化。比较好理解,但是,具体的应用还是需要实践的。实际上,在应用中就是对一个“对象池”,可以对这些对象进行重复利用。以下这个例子更加直观。

 

Flyweight模式在XML等数据源中应用
我们上面已经提到,当大量从数据源中读取字符串,其中肯定有重复的,那么我们使用Flyweight模式可以提高效率,以唱片CD为例,在一个XML文件中,存放了多个CD的资料.

每个CD有三个字段:
1.出片日期(year)
2.歌唱者姓名等信息(artist)
3.唱片曲目 (title)

其中,歌唱者姓名有可能重复,也就是说,可能有同一个演唱者的多个不同时期 不同曲目的CD.我们将"歌唱者姓名"作为可共享的ConcreteFlyweight.其他两个字段作为UnsharedConcreteFlyweight.

首先看看数据源XML文件的内容:


<?xml version="1.0"?>
<collection>

<cd>
<title>Another Green World</title>
<year>1978</year>
<artist>Eno, Brian</artist>
</cd>

<cd>
<title>Greatest Hits</title>
<year>1950</year>
<artist>Holiday, Billie</artist>
</cd>

<cd>
<title>Taking Tiger Mountain (by strategy)</title>
<year>1977</year>
<artist>Eno, Brian</artist>
</cd>

.......

</collection>


虽然上面举例CD只有3张,CD可看成是大量重复的小类,因为其中成分只有三个字段,而且有重复的(歌唱者姓名).

CD就是类似上面接口 Flyweight:


public class CD {

  private String title;
  private int year;
  private Artist artist;

  public String getTitle() {  return title; }
  public int getYear() {    return year;  }
  public Artist getArtist() {    return artist;  }

  public void setTitle(String t){    title = t;}
  public void setYear(int y){year = y;}
  public void setArtist(Artist a){artist = a;}

}

将"歌唱者姓名"作为可共享的ConcreteFlyweight:

public class Artist {

  //内部状态
  private String name;

  // note that Artist is immutable.
  String getName(){return name;}

  Artist(String n){
    name = n;
  }

}

再看看Flyweight factory,专门用来制造上面的可共享的ConcreteFlyweight:Artist

public class ArtistFactory {

  Hashtable pool = new Hashtable();

  Artist getArtist(String key){

    Artist result;
    result = (Artist)pool.get(key);
    ////产生新的Artist
    if(result == null) {
      result = new Artist(key);
      pool.put(key,result);
      
    }
    return result;
  }

}

当你有几千张甚至更多CD时,Flyweight模式将节省更多空间,共享的flyweight越多,空间节省也就越大.

该例子内容来自:设计模式之Flyweight(享元) FlyWeight模式

你可能感兴趣的:(设计模式,flyweight)