【源码分析设计模式 7】Integer 中的享元模式
//获得网站分类
public WebSite getWebSiteCategory(String key) {
if(!pool.containsKey(key)) {
pool.put(key, new ConcreteWebSite(key));
}
return (WebSite)pool.get(key);
}
//获得网站分类总数
public int getWebSiteCount() {
return pool.size();
}
}
4、client 客户端
这里测试用例给了两种网站,原先我们需要做三个产品展示和三个博客的网站,也即需要六个网站类的实例,但其实它们本质上都是一样的代码,可以利用用户 ID 号的不同,来区分不同的用户,具体数据和模板可以不同,但代码核心和数据库却是共享的。
package designMode.Flyweight.web2;
public class Client {
public static void main(String[] args) {
WebSiteFactory factory = new WebSiteFactory();
WebSite fx = factory.getWebSiteCategory("产品展示");
fx.use();
WebSite fy = factory.getWebSiteCategory("产品展示");
fy.use();
WebSite fz = factory.getWebSiteCategory("产品展示");
fz.use();
WebSite fa = factory.getWebSiteCategory("博客");
fa.use();
WebSite fb = factory.getWebSiteCategory("博客");
fb.use();
WebSite fc = factory.getWebSiteCategory("博客");
fc.use();
System.out.println("网站分类总数为:" + factory.getWebSiteCount());
}
}
5、运行结果
可以看出,虽然我们做了 6 个网站,但网站分类只有 2 个。这样基本算是实现了享元模式的共享对象的目的,但想想上面提到的内部状态和外部状态,这里实际上没有体现对象间的不同,只体现了它们的共享部分。
6、用户类
所以我们再加一个用户类,作为网站类的外部状态,并在 use()方法中传递用户对象,UML 如下:
下面添加一个 User 类。
package designMode.Flyweight.web2;
public class User {
private String name;
public User(String name){
this.name = name;
}
public String getName(){
return name;
}
}
然后再对 use()方法进行修改,添加参数,以抽象类为例:
package designMode.Flyweight.web2;
public abstract class WebSite {
public abstract void use();
public abst
ract void use(User user);
}
而客户端中只需对每一个网站添加一个用户即可,如:
fx.use(new User("素小暖"));
运行结果如下:
这样就可以协调内部与外部状态,哪怕接手了上千个网站的需求,只要要求相同或类似,实际开发代码也就是分类的哪几种。
七、Integer 的享元模式解析
1、测试类
package designMode.advance.flyWeight;
public class FlyWeight {
public static void main(String[] args) {
//如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在
//范围类,则仍然 new
//小结:
//1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new), 否则,就直接从 缓存池返回
//2. valueOf 方法,就使用到享元模式
//3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
Integer x = 127; // 得到 x 实例,类型 Integer
Integer y = new Integer(127); // 得到 y 实例,类型 Integer
Integer z = Integer.valueOf(127);//..
Integer w = new Integer(127);
System.out.println(x.equals(y)); // 大小,true
System.out.println(x == y ); // false
System.out.println(x == z ); // true
System.out.println(w == x ); // false
System.out.println(w == y ); // false
Integer x1 = Integer.valueOf(200);
Integer x2 = Integer.valueOf(200);
System.out.println("x1==x2" + (x1 == x2)); // false
}
}
2、如果想了解怎么实现共享的,就得分析 Integer 的源代码
Integer x = 127;和 Integer z = Integer.valueOf(127);是一个意思;
下面我们来看下 valueOf 方法:
//意思就是 在一个区间之内,直接用 IntegerCache.cache[]数组里面的数返回,否则 new 一个新对象。
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
再看一下 IntegerCache 类:
//是 Integer 内部的私有静态类,里面的 cache[]就是 jdk 事先缓存的 Integer。
private static class IntegerCache {
static final int low = -128;//区间的最低值
static final int high;//区间的最高值,后面默认赋值为 127,也可以用户手动设置虚拟机参数
static final Integer cache[]; //缓存数组
static {
// high value may be configured by property
int h = 127;
//这里可以在运行时设置虚拟机参数来确定 h :-Djava.lang.Integer.IntegerCache.high=250
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {//用户设置了
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);//虽然设置了但是还是不能小于 127
// 也不能超过最大值
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
//循环将区间的数赋值给 cache[]数组
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
其实很简单,就是用一个 Integer 数组先缓存了,后面如果是是在区间内的数直接从缓存数组中取,否则才构造新的 Integer。缓存思想还是很重要的!谢谢大家观看。
八、享元模式和单例模式的区别
评论