java中具体的23中设计模式的介绍,可以参考:http://c.biancheng.net/design_pattern/
在享元模式中,有一个很经典的案例就是Integer包装类的实现。看下如下代码:
Integer a = 1;
Integer b = 1;
System.out.println(a == b);
Integer c = 128;
Integer d = 128;
System.out.println(c == d);
这里a == b是true,而c == d是false的结果。因为Integer的源码实现中,使用了共享内存的设计理念(即享元模式),源码如下:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
int high = 127;
cache = new Integer[(high - low) + 1]; //提前缓存127+128+1=256个Integer对象
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
}
// 使用的时候,直接从内存中取出
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
源码中新建的一块内存区域,在需要使用到Integer(-127~128)的程序中,可以直接使用,不用创建Integer的Class对象(已经创建好了)和对应的Integer对象,提升相应速度和性能。
关于Class对象,可以参考下:https://blog.csdn.net/river66/article/details/103606336
所以为了能快速的访问目标数据,提前预置好的区域的方案就称之为享元模式。从CPU的寄存器,再到CPU的缓存,再到内存,这些提前预置好的存放数据的区域,都是为了能快速访问数据。因为相对于磁盘来说,这些都是很小的一个区域,所以英文原文是:Flyweight,轻量级。即小区域的共享,这里的小区域对应我们中文的“元”,共享小区域,共享元数据模式。
所以,我们在日常的开发中会发现,项目中常常会使用HashMap或者其他集合类型,提交将数据库的高频访问数据查询出来,缓存起来,以便查询的时候,可以快速的访问,都是享元模式的体现。
说明:
使用到Integer对象时,用“==”进行比较不规范的,因为Integer是一个有比较性质的对象,Integer已经重写的Object的equals和hashCode方法。所以比较Integer对象的话,需使用equals方法,c.equals(d)为true.
关于为什么要重写equals和hashCode方法,可以参考下:https://blog.csdn.net/river66/article/details/87803663
观察者模式中,谁在乎我状态的变化,当我发生变化的时候,我就会去通知在乎我的人我发生变化了。java自带的Observable、Observer和Subject可以帮助快速实现这样的一个通知Observer的效果,但并不是一定要按照java源码的方式一模一样的去设计实现。只要程序的目的是:当我关注的一个对象,在这个对象变化的时候,给了我通知让我可以执行相应的逻辑,那就是观察者模式的应用。
我需要别人通知我,我必须提前告知别人我的存在,如:提前在web.xml中注册各种监听器(Listener) ,当监听器监听的的对象发生变化时,监听器的方法就会被调用。其实也就是把监听器接口注入到目标对象中,在变化之后的地方调用监听器的方法(模板方法模式)。(1、这里我用到了注入一词,是因为监听器通常需要配置各种属性,是需要提前生成监听器实例对象的,再利用java的反射机制实现注入;2、有些补不能通过属性配置的,则需要继承框架已经给出的Listener,重写相应的方法;之所以继承框架的Listener就会执行我们重写的方法,是因为监听器接口的引用已经被目标对象持有并调用了,模板方法模式的应用)
建造者模式中,此模式的初衷和工厂模式是一样的,都是为了方便获取到特定的目标对象。不一样的地方是工厂模式的控制的粒度没有建造者模式的控制粒度要细。建造者模式适用于由多个模块组成的对象,如果使用构造函数,那构造函数的参数将会非常的多,不方便使用;或者是目标类维护着一个处理逻辑复杂的数据成员等。如:
1、Calendar日历类,组成的模块多,有TimeZone、Locale、time(long)等等,于是有了Calendar.Builder,帮我们默认值设置各种参数信息(没有传入的模块信息)
2、StringBuilder类,底层维护的是一个char数组。由于String是一个final类型,不可以重新赋值,而我们需要在同一个char[]数组中新增或者删除数据,操作逻辑较为复杂,则有了StringBuilder。
说明:String类型本身就是建造者模式的应用,只不过没有Builder的字样而已,底层维护的也是char[]数组。
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;