B6) Flyweight(享元模式) 定义:使用共享来高效的支持大量的细颗粒对象。 似乎不太常用的一个模式,因为对于要求可能严格了一点:大量的细颗粒对象,真正运用到这个层次上的机会应该不会太多。享元模式就是将大量对象实例中相同的部分提取出来,形成一个原型,这部分称为intrinsic(内部状态),是不变的,而会因外界条件而改变的部分称为extrinsic(外部状态)。那每次需要一个新的对象实例时候,就从享元共享池(pool)中得到内部状态即共享的原型,再根据外部条件,生成外部状态,这样如果是大规模数量的对象,就可以节省很多内存空间。是不是很像文件压缩技术?那就以压缩图像文件的例子来说明一下。首先是BMP文件类: public class BMPFile { private FileInfo info; private Map pixelMap; public Pixel getPixel(Position pos) { return pixelMap.get(pos); } public void setPixel(Position pos, RGB color) { Pixel pixel = new Pixel(pos, color); pixelMap.put(pos, pixel); } } FileInfo包括文件名、大小、日期等等,这暂时不考虑,因为对于享元模式,图片上的成千上万甚至上百万千万的像素(pixel)才是需要考虑的大量的细颗粒对象。像素(pixel)类,包含颜色和坐标,假设一个256色的BMP文件,那么颜色是固定的256种,就是内部状态,而坐标却会因为图片的样子和大小而变化,就是外部状态。如下: public class Pixel { private RGB color; private Position pos; public Pixel(Position pos, RGB color) { this.pos = pos; this.color = color; } } 现在开始压缩,假设分析某个BMP文件后发现,这个BMP共包含两种颜色,黑(RGB:000000)和白(RGB:FFFFFF),图片大小为100x100。那么,这个BMP文件大小为10000个像素x每个像素的大小(假设为12个字节,颜色占用6个,坐标占用6个)+文件信息段(假设为500个字节),共计120500字节。转换成压缩格式,使用享元共享池 public class PixelPool { private Map pool = new Hashmap(); public Pixel getPixel(RGB color) { Pixel pixel = pool.get(color); if (pixel == null) { pixel = new Pixel(null, color); pool.put(color, pixel); } return pixel; } } 那么,得到的新的压缩后的文件为: public class CompressedBMPFile extends BMPFile { private PixelPool pool = new PixelPool(); public void setPixel(Position pos, RGB color) { Pixel pixel = pool.getPixel(color); pixel.setPosition(position); pixelMap.put(pos, pixel); } } 重新来计算一下新的压缩后的文件大小,两个颜色的像素占用2x6个字节+坐标占用字节10000x6+文件信息500字节,大约是60512字节,小了一半。就算是256色的BMP,总共的大小也只有256x6+10000x6+500=62036字节。当然,纪录用的享元池可能还有部分开销,但总体来说,节省了很大的空间,而且通过pool得到已有的pixel对象,不用每次创建新的对象,也是非常节省系统资源的。
|