》》
------ flyweight 是“轻量级”的意思。对象在计算机中是虚拟存在的东西,它的“重”和“轻”并非指实际重量,
而是它们“所使用的内存大小”。使用内存多的对象就是“重”对象,使用内存少的对象就是“轻”对象。
------ 当程序中需要大量对象时,如果都使用 new 关键字来分配内存,将会消耗大量内存空间。
------ Flyweight 模式:通过尽量共享实例来避免 new 出实例。当需要某个实例时,并不总是通过 new
关键字来生成实例,而是尽量共用已经存在的实例。(Flyweight 模式的核心内容)。
》》示例程序
在示例程序中,有一个将许多普通字符组合成为的“大型字符”的类,它的实例就是重实例。为了进行
测试,我们以文件形式保存了大型字符'0'~'9'和‘-’的字符数据。效果如下:
》》类的一览表
--------- BigChar 是表示“大型字符”的类。它会从文件中读取大型字符的字体数据,并将它们保存在内存中,
然后使用 print 方法输出大型字符。大型字符会消耗很多内存,因此我们需要考虑如何共享 BigChar
类的实例。
--------- BigCharFactory 类会根据需要生成 BigChar 类的实例。不过如果它发现之前已经生成了某个大型
字符的 BigChar 类的实例,则会直接利用该实例,而不是再生成新的实例。此外,为了能够快速查
找出之前是否已经生成了某个大型字符所对应的实例,使用 java.util.Hashmap 类。
--------- BigString 类用于将多个 BigChar 组成“大型字符串”。
--------- Main 类是用于测试程序行为的类。
》》BigChar 类
package flyweight; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; /** * BigChar 类是表示“大型字符”的类 */ public class BigChar { private char charname; // 字符名字 private String fontdata; // 大型字符对应的字符串(由 # .\n 组成) public BigChar(char charname){ // 构造函数 this.charname = charname; try { BufferedReader reader = new BufferedReader( new InputStreamReader(this.getClass().getResourceAsStream("big"+charname+".txt")) ); String line; StringBuffer buf = new StringBuffer(); while((line = reader.readLine())!= null){ buf.append(line); buf.append("\n"); } reader.close(); fontdata = buf.toString(); } catch (IOException e) { e.printStackTrace(); } } public void print(){ System.out.println(fontdata); // 显示大型字符 } }
》》BigCharFactory 类
package flyweight; /** * 1. BigCharFactory 类是生成 BigChar 类的实例的工厂。它实现了共享实例的功能。 * 2 .pool 字段用于管理已生成的 BigChar 类的实例。 * 3. 在 BigCharFactory 类中,使用 java.util.HashMap 类来管理“字符串--》实例”之间的对应关系。使用 * java.util.HashMap 类的 put 方法可以将某个字符串(键)与一个实例(值)关联起来,就可以通过键来 * 获取它相应的值。 * */ import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); // 管理已生成的 BigChar 的实例 private static BigCharFactory singleton = new BigCharFactory(); // Singleton 模式 private BigCharFactory(){}; // 构造函数 public static BigCharFactory getInstance(){ return singleton; // 获取唯一的实例 } public synchronized BigChar getBigChar(char charname){ // 生成(共享)BigChar 类的实例 BigChar bc = (BigChar)pool.get(""+charname); if(bc == null){ bc = new BigChar(charname); pool.put(""+charname,bc); } return bc; } }
》》BigString 类
package flyweight; /** * BigString 类表示由 BigChar 组成的“大型字符串”的类 */ public class BigString { private BigChar[] bigChars; // "大型字符"的数组 public BigString(String string){ // 构造函数 bigChars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for(int i = 0 ; i < bigChars.length ; i++){ bigChars[i] = new BigChar(string.charAt(i)); } } public void print(){ // 显示 for(int i = 0 ; i < bigChars.length ; i++){ bigChars[i].print(); } } }
》》Main 类
package flyweight; /** * 测试程序 */ public class Main { public static void main(String[] args){ String stringValue = "2123"; BigString bigString = new BigString(stringValue); bigString.print(); } }
》》Flyweight 模式中的登场角色
------ Flyweight (轻量级)
按照通常方式编写程序会导致程序变重,所以如果能够共享实例会比较好,而 Flyweight 角色
表示的就是那些实例会被共享的类。在上面的程序中,由 BigChar 类扮演此角色。
------ FlyweightFactory (轻量级工厂)
FlyweightFactory 角色是生成 Flyweight 角色的工厂。在工厂中生成的 Flyweight 角色可以实现
共享实例。在上面的程序中,由 BigCharFactory 类扮演此角色。
------ Client (请求者)
Client 角色使用 FlyweightFactory 角色来生成 Flyweight 角色。在上面的示例程序中,由
BigString 类扮演此角色。
》》扩展思路的要点
-------- 对多个地方产生影响
**** 如果要改变被共享的对象,就会多个地方产生影响。也就是说,一个实例的而改变会同时
反映到使用该实例的地方。
**** 共享的特点:修改一个地方会对多个地方产生影响。
**** 设计者在使用 Flyweight 模式共享信息时必须仔细思考应当共享哪些信息。
-------- Intrinsic 与 Extrinsic
**** 应当共享的信息被称为 Intrinsic 信息。它指的是不论实例在哪里、不论在什么情况下都
不会改变的信息,或是不依赖实例状态的信息。
**** 不应当共享的信息被称为 Extrinsic 信息。它是指当实例的位置、状况发生改变时会变化的
信息,或是依赖于实例状态的信息。
****
-------- 不要让被共享的实例被垃圾回收器回收了
**** 在上面的示例程序中,BigCharFactory 类中,我们使用 java.util.HashMap 来管理已经生成
的 BigChar 的实例。像这样在 java 中自己“管理”实例时,必须注意“不要让实例被垃圾回收器回收了”。
**** 在 Java 程序中可以通过 new 关键字分配内存空间。如果分配了过多内存,就会导致内存不足。
这时,运行 Java 程序的虚拟机就会开始垃圾回收处理。它会查看自己的内存空间(堆空间)中是否
在没有被使用的实例,如果存在就释放该实例,这样就可以回收可用的内存空间。
------- 内存之外的其他资源
***** 在上面的示例程序中,我们了解到共享实例可以减少内存使用量。一般来说,共享实例可以减少
所需资源的使用量。这里的资源指的是计算机中的资源,而内存是资源中的一种。
***** 时间也是一种资源。使用 new 关键字生成实例会花费时间。通过 Flyweight 模式共享实例可以
减少使用 new 关键字生成实例的次数。这样,就可以提高程序运行速度。
***** 文件句柄(文件描述符)和窗口句柄等也都是一种资源。在操作系统中,可以同时使用的文件
句柄和窗口句柄是有限制的。因此,如果不共享实例,应用程序在运行时很容易达到资源极限而导致
崩溃。
》》相关的设计模式
-------- Proxy 模式
如果生成实例的处理需要花费较长时间,那么使用 Flyweight 模式可以提高程序的处理速度。而
Proxy 模式则是通过设置代理提高程序的处理速度。
-------- Composite 模式
有时候可以使用 Flyweight 模式共享 Composite 模式中的 Leaf 角色。
------- Singleton 模式
在 FlyweightFactory 角色中有时候会使用 Singleton 模式。