乐趣区

设计模式-享元模式

栗子
使用工厂方法,表现层通过工厂方法创建对象,再传递给业务层,持久层,最后保存到数据库中。报考信息
public class Signinfo{
// 报名 ID
private String id;
// 考试地点
private String location;
// 科目
private String subject;
// 邮寄地址
private String postAddress;
public String getId(){
return id;
}
public void setId(String id){
this.id = id;
}
public String getLocation(){
return location;
}
public String getSubject(){
return subject;
}
public void setSubject(String subject){
this.subject = subject;
}
public String getPostAddress(){
return this.postAddress;
}
public void setPostAddress(String postAddress){
this.postAddress = postAddress;
}
}
报考信息工厂,批量生产报考信息对象
public class SiginfoFactory{
// 报名信息的对象工厂
public static Signinfo getSigninfo(){
return new Signinfo();
}
}
最后书写场景
public class Client{
public static void main(String[] args){
// 从工厂中获得对象
Signinfo signinfo = SigninfoFactory.getSigninfo();
// 进行其他业务处理
}
}
垃圾回收机制
jvm 有垃圾回收机制,即自动内存管理。其中堆是所有线程都共享的,而栈是每个线程都各自拥有的。程序有程序计数器,当线程超过 cpu 数量,或者 cpu 内核数量的时候,线程根据时间片轮询抢夺 cpu 时间资源,即每个线程都需要一个独立的程序计数器记录正在执行的字节码的指令地址。虚拟机栈,虚拟机栈是 java 方法执行的内存模型,为线程私有,每个方法在执行的时候,会创建一个栈帧,这个栈帧保存着局部变量表,操作数栈,动态链接,方法出口,每个方法的调用,都代表着一个栈帧从入栈到出栈的过程。方法调用会入栈,方法返回会出栈关于 java 堆,垃圾收集器的主要管理区域,即 GC 堆,java 垃圾回收算法有引用计数法,通过判断对象的引用数量决定对象是否可以被回收,当引用为 0 的对象,可以被当做垃圾回收,当对象之间相互引用的时候,由于两者引用计数都不为 0,所以不能使用 gc 进行清除。可达性分析算法,程序把引用关系看做一张图,从节点往下搜索,当对象没有任何引用链的时候,会证明不可达,此时进行回收垃圾收集算法,对象先判断是否可以进行回收,先标记再清除,从跟集合扫描,对存活对象标记,标记完成以后,对未标记的进行回收。复制算法。将可用内存分大小相等两块,这一块用完,将存活的复制到另外一块上,再把已经使用的内存空间一次清除掉。即,将堆空间分为两块,一块为新生代,一块为老年代,在进行回收的时候,会把存活的对象,复制到新生代中,将老年代清空。
内存泄露
使用 HashMap,Vector 等集合类的静态使用容易出现内存泄露,因为这些静态变量的生命周期和应用程序一致。即各种资源的连接,网络连接,IO 连接没有被 close 关闭,
更改
当并发数增多的时候,每个线程会创建对象,并发数越多,线程数越多,此时会造成内存的疯狂占用,造成内存泄露。解决:对象池。使用一个共享池来解决问题。代码如下
public class Signinfo4Pool extends Signinfo{
// 定义一个 key
private String key;
// 构造函数获得标志
public Signinfo4Pool(String _key){
this.key = _key;
}
public String getKey(){
return key;
}
public void setkey(String key){
this.key = key;
}
}
下面书写带对象池的工厂类
public class SigninfoFactory{
// 池容器
private static HashMap<String, Signinfo> pool = new hashMap<String, Signinfo>();
// 对象工厂
@Deprecated
// 对原先代码进行修改的时候,需要加上 Deprecated,表明这个方法已经废弃。不在建议使用,不建议删除,因为如果有其他继续使用这个方法的时候,将会导致出现不可预知的问题。用于向下兼容
public static Signinfo(){
return new Signinfo();
}
// 获得对象
public static Signinfo getSigninfo(String key){
Signinfo result = null;
// 当 key 值存在的时候,从对象池中获得该对象。当 key 值不存在的时候,创建对象,并放入对象池中。
if(!pool.containsKey(key)){
result = new Signinfo4Pool(key);
pool.put(key, result);
}else{
result = pool.get(key);
}
return result;
}

}
最后书写场景类
public class Client{
public static void main(String[] args){
// 初始化对象池
for(int i = 0 ; i < 4; i++){
String subject = “ 科目 ” + i;
// 初始化地址
for(int j = 0; j < 30; j++){
String key = subject + “ 地点 ” + j;
SigninfoFactory.getSigninfo(key);
}
}
// 从池中获取对象然后进行处理。
Signinfo singinfo = SignInfoFactory.getSignInfo(“ 科目 1 地点 1 ”);
}
}
总结
即,将共性的内容,提取出来,然后在新建一个子类,然后在子类中预留出外部访问的。上方的栗子为 key。然后在工厂模式中,String 为 key,value 的值为 key 对应的对象池中创建的对象。
ps 在上方的栗子中 id 作为附属的,即,对象的动态信息。池 + 工厂 通过新建一个对象池,该池内的对象有 HashMap 来进行保存,然后通过工厂,输入 key 值,获取到对象。核心在于创建出业务需要的对象,然后在运行的时候,直接使用该对象。因为堆是所有线程所共享的。
线程安全
由于 java 中堆是所有线程所共享的,所以当共享池中的对象数不够的时候,会出现线程安全的问题,即,多个线程,共同访问一个对象,同时修改造成数据的错误。即,在使用享元模式的时候,对象要尽可能的多,直到业务需求全部满足。还要注意线程安全的问题,当一个线程
外部状态 / 内部状态
关于外部状态和内部状态,其中内部状态不可更改,外部状态可更改,但是多线程的时候会出现很严重的问题,即线程不安全,当多个线程共同访问,操作外部状态的时候,会出现线程不安全。至今不知道怎么解决。
继续扩展
此时,新建一个类,用于保存 key 值,替代原先的 String 方式。再写 static 类型的里面保存的 HashMap,用于保存当前池中的对象。需要使用的时候,直接新将外部类,将内容 set 进入。工厂内需要调用状态类的方法,生成 String 和 HashMap 中的 key 进行对比。若已经生成,直接返回该对象,否则不返回该对象
总结
享元模式 = 工厂模式 + 池

退出移动版