【设计模式二十二之享元模式】享元模式详解

细说中介者模式

提示:
博主:章飞 _906285288的博客
博客地址:http://blog.csdn.net/qq_29924041


细说享元模式

享元模式中享是共享的意思,元是单元的意思,其实可以理解为共享的最小单元,最小单元其实可以理解为细粒度最低,也就是无法再往下继续细化状态,所以享元模式其实也就是为了解决细粒度最低对象的共享问题。
这里可以抛出一个问题,就是你在写代码的时候,有没有遇到过oOM,也就是大量细粒度的对象被创建出来。
享元模式其实就是为了解决代码中大量创建对象,以减少内存占用和性能优化使用的

定义

享元模式:使用池技术,利用共享对象的技术,有效的支持大量细粒度的对象。所以享元模式的对象其实是共享对象以及细粒度对象。

细粒度对象分析:细粒度对象不可避免使得对象多并且性质相近,那么我们其实就要将这些对象分为两个部分,一个是内部状态,另外一个叫做外部状态。

内部状态:内部状态其实就是对象可以共享出来的部分,存储在享元对象的内部,并且不会随着环境的改变而去改变的,不必存在某个具体的对象中。

外部状态:外部对象其实就是对象得以依赖的一个标记状态,是随着环境的改变而需要改变的,它是一批对象的唯一的索引值,是一批对象的唯一标识

UML模型

在这里插入图片描述
享元模式主要有以下几个角色:
1:Flyweight——抽象的享元角色,简单的说其实就是一个抽象的产品类,它定义了这个产品的内部状态和外部状态
2:ConcreteFlyweight——具体的享元角色,具体的产品类,实现享元角色定义的相关的业务逻辑,该对象的处理需要注意的是内部状态的处理应该需要与环境无关,不应该出现一个操作即改变了内部状态,又修改了外部状态。
3:unsharedConcreteFlyweight——不存在外部状态,不能够使用共享技术的对象。
4:FlyweightFactory——享元工厂,构造出一个容器池,然后从池子中获得或者复用对象

基于UML的代码
package src.com.zzf.designpattern.flyweightpattern.demo2;


public abstract class FlyWeight {
	private String intrinsic; 
	
	protected final String Extrinsic;
	
	public FlyWeight(String extrinsic) {
		// TODO Auto-generated constructor stub
		this.Extrinsic = extrinsic;
	}

	public String getIntrinsic() {
		return intrinsic;
	}

	public void setIntrinsic(String intrinsic) {
		this.intrinsic = intrinsic;
	}
	
	public abstract void operate() ;
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return super.toString()+"Extrinsic:"+Extrinsic+"\tintrinsic:"+intrinsic;
	}
}

package src.com.zzf.designpattern.flyweightpattern.demo2;


/**
 * 抽象的享元角色
 * @author Administrator
 *
 */
public class ConcreteFlyWeight1 extends FlyWeight{

	public ConcreteFlyWeight1(String extrinsic) {
		super(extrinsic);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void operate() {
		// TODO Auto-generated method stub
		
	}

}

package src.com.zzf.designpattern.flyweightpattern.demo2;


public class ConcreteFlyWeight2 extends FlyWeight{

	public ConcreteFlyWeight2(String extrinsic) {
		super(extrinsic);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void operate() {
		// TODO Auto-generated method stub
		
	}

}

package src.com.zzf.designpattern.flyweightpattern.demo2;


import java.util.HashMap;

/**
 * 享元模式的应用场景:
 * 1:系统中存在大量相似的对象
 * 2:细粒度的对象都具备较接近的外部对象,而且内部状态与环境无关
 * 3:缓存池
 * @author Administrator
 *
 */
public class FlyweightFactory {
	
	private static HashMap<String, FlyWeight> poolHashMap = new HashMap<String, FlyWeight>();
	public static FlyWeight getFlyWeight(String key){
		FlyWeight mFlyWeight = null;
		if (!poolHashMap.containsKey(key)) {
			mFlyWeight = new ConcreteFlyWeight1(key);
			poolHashMap.put(key, mFlyWeight);
			System.out.println("创建新的对象");
		}else {
			mFlyWeight = poolHashMap.get(key);
			System.out.println("重复利用了");
		}
		return mFlyWeight;
	}
	
}

package src.com.zzf.designpattern.flyweightpattern.demo2;


public class UnSharedConcreteFlyWeight {

}

package src.com.zzf.designpattern.flyweightpattern.demo2;

/**
 * 享元模式的注意事项:后一次复用某个对象时,前一次的创建出来的 一定不能再次使用,否则必然会导致数据错乱问题。
 * @author zhouzhangfei
 * 多线程中去使用的话,同时复用一个对象的时候,修改的话很容易产生线程安全的问题,这个就需要对操作都设计成原子性或者类似生命周期的设定,严格禁止同时获取一个对象进行相关操作
 *
 */
public class Clinet {
	public static void main(String[] args) {
		FlyWeight flyWeight = FlyweightFactory.getFlyWeight("123");
		flyWeight.setIntrinsic("flyWeight 内部");
		System.out.println("1-->"+flyWeight.toString());
		
		FlyWeight flyWeight2 = FlyweightFactory.getFlyWeight("123");
		System.out.println("2-->"+flyWeight2.toString());
		flyWeight2.setIntrinsic("flyWeight2 内部");
		System.out.println("1-->"+flyWeight.toString());
		
		FlyWeight flyWeight3 = FlyweightFactory.getFlyWeight("456");
		System.out.println("3-->"+flyWeight3.toString());
		flyWeight3 = null;
	}
}

场景一

场景

引用设计模式之禅中的案例,即考生信息相关的业务,考生通过页面输入相关信息,报考科目,报考地点等,然后提交,我们知道,每次考试,科目数其实是固定的,考试地点其实也是固定的,那么对于很多考生来说,有些可能科目和地点都是一致的,如果有1W个人考试,必定有着大量相关此类信息是重复的,如果都通过new的形式去创造对象,那意味着,要有new出来1W个这样的对象,如果能够使用池子技术的话,共享这些公共对象,那么就可以很大程度上去降低这些对象的创建,从而很大程度上能够优化内存技术。

代码
package src.com.zzf.designpattern.flyweightpattern.demo1;


public class SignInfo {
	private String name;
	private String location;
	private String object;
	private String postAddress;
	public String getPostAddress() {
		return postAddress;
	}
	public void setPostAddress(String postAddress) {
		this.postAddress = postAddress;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getLocation() {
		return location;
	}
	public void setLocation(String location) {
		this.location = location;
	}
	public String getObject() {
		return object;
	}
	public void setObject(String object) {
		this.object = object;
	}
	
}

package src.com.zzf.designpattern.flyweightpattern.demo1;


public class ExtrinsicState {
	private String locationString;
	private String subject;
	
	public ExtrinsicState(){
		
	}

	public String getLocationString() {
		return locationString;
	}

	public void setLocationString(String locationString) {
		this.locationString = locationString;
	}

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}
	
	@Override
	public boolean equals(Object arg0) {
		// TODO Auto-generated method stub
		if (arg0 != null) {
			ExtrinsicState mExtrinsicState = (ExtrinsicState)(arg0);
			return locationString.equals(mExtrinsicState.getLocationString()) 
					&& mExtrinsicState.subject.equals(subject);
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return subject.hashCode() +locationString.hashCode();
	}
}

package src.com.zzf.designpattern.flyweightpattern.demo1;


public class SignInfo4Pool extends SignInfo {
	private String key;
	
	public SignInfo4Pool(String key) {
		// TODO Auto-generated constructor stub
		this.key = key;
	}
	
	public String getKey() {
		return key;
	}
	
	public void setKey(String key){
		this.key = key;
	}
}

package src.com.zzf.designpattern.flyweightpattern.demo1;


import java.util.HashMap;

public class SignFactory {
	
	@Deprecated
	public static SignInfo getSignInfo(){
		return new SignInfo();
	}
	
	
	private static HashMap<String, SignInfo> pool = new HashMap<String, SignInfo>();
	
	public  static SignInfo getSignInfo(String key) {
		SignInfo resultInfo = null;
		if (!pool.containsKey(key)) {
			System.out.println(key+"-----创建对象放进对象池中");
			resultInfo = new SignInfo4Pool(key);
			pool.put(key, resultInfo);
		}else {
			System.out.println(key+"-----从对象池中获取对象");
			resultInfo = pool.get(key);
		}
		return resultInfo;
	}
} 

package src.com.zzf.designpattern.flyweightpattern.demo1;


/**
 * 享元模式其实就是对对象的一种复用操作
 * @author Administrator
 *	
 *重要概念:对象池,享元模式中有一个极其重要的component就是对象池,对象池是为了能够复用对象来产生的
 *对象池的两个要素:----->容器+对外提供的接口
 *
 *
 *享元模式是池技术的重要实现方式;
 *
 *
 *注意:对象分为内部状态和外部状态:
 *内部状态:内部共有的属性,可以改变
 *
 *外部状态:是一批对象的统一标识,也是唯一的索引值
 */
public class Client {
	public static void main(String[] args) {
//		SignInfo signInfo = SignFactory.getSignInfo();
//		for (int i = 0; i < 4; i++) {
//			String subjectString = "科目" + i;
//			for (int j = 0; j < 30; j++) {
//				String key = subjectString +"考试地点" + j;
//				SignFactory.getSignInfo(key);
//			}
//		}
//		SignInfo mSignInfo = SignFactory.getSignInfo("科目1"+"考试地点"+10);
		
		
		ExtrinsicState mExtrinsicState_1 = new ExtrinsicState();
		ExtrinsicState2 mExtrinsicState2 = new ExtrinsicState2();
		mExtrinsicState_1.setSubject("subject");
		mExtrinsicState_1.setLocationString("location");
		mExtrinsicState2.setSubject("subject");
		mExtrinsicState2.setLocationString("location");
		System.out.println("-----1-1:--"+mExtrinsicState_1.toString());	
		System.out.println("-----1-2:--"+mExtrinsicState2.toString());	
		
		
		
		ExtrinsicState mExtrinsicState_1_2 = new ExtrinsicState();
		ExtrinsicState2 mExtrinsicState2_2 = new ExtrinsicState2();
		mExtrinsicState_1_2.setSubject("subject");
		mExtrinsicState_1_2.setLocationString("location");
		mExtrinsicState2_2.setSubject("subject");
		mExtrinsicState2_2.setLocationString("location");
		System.out.println("-----2-1:--"+mExtrinsicState_1_2.toString());	
		System.out.println("-----2-2:--"+mExtrinsicState2_2.toString());	
		
		
//		System.out.println(mExtrinsicState_1.equals(mExtrinsicState_1_2));
		System.out.println(mExtrinsicState2.equals(mExtrinsicState2_2));
	}
}

注意:前一个使用复用的对象,在后面是不能够继续使用的,否则就会出现异常,所以外部需要及时置为null,当外部引用置为null的时候,并不会去影响池子中内部对象的使用。

场景二

场景

现实生活中对于笔筒中笔的使用,比如,现在笔筒中有钢笔,有毛笔等等一些笔,也有相应不同颜色的墨水,如果我想使用不同颜色的钢笔,首先我需要从笔筒中拿出钢笔,然后吸入墨水,然后当我不用的时候,这个就是 我可以把钢笔放入笔筒,当我需要使用其他颜色的钢笔的时候,这个时候我就可以从笔筒中拿出钢笔,然后吸入其它颜色的墨水,这个其实就是复用的场景。

代码
package src.com.zzf.designpattern.flyweightpattern.demo3;

public abstract class PenFlyWeight {
	private String color ;
	private final String type;
	
	public PenFlyWeight(String _type){
		this.type = _type;
	}
	
	public abstract void draw();
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return super.toString()+"\t"+color+"\ttype:"+type;
	}
	
	public void setColor(String _color){
		this.color = _color;
	}
	
	
}

package src.com.zzf.designpattern.flyweightpattern.demo3;

public class ConcretePenFlyweight extends PenFlyWeight{

	public ConcretePenFlyweight(String _type) {
		super(_type);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void draw() {
		// TODO Auto-generated method stub
		
	}

}

package src.com.zzf.designpattern.flyweightpattern.demo3;

import java.util.HashMap;

public class FlyweightFactory {
	
	private static HashMap<String, PenFlyWeight> poolHashMap = new HashMap<String, PenFlyWeight>();
	public static PenFlyWeight getFlyWeight(String key){
		PenFlyWeight mFlyWeight = null;
		if (!poolHashMap.containsKey(key)) {
			mFlyWeight = new ConcretePenFlyweight(key);
			poolHashMap.put(key, mFlyWeight);
			System.out.println("创建新的对象");
		}else {
			mFlyWeight = poolHashMap.get(key);
			System.out.println("重复利用了");
		}
		return mFlyWeight;
	}
	
}
package src.com.zzf.designpattern.flyweightpattern.demo3;

/**
 * 享元模式的注意事项:后一次复用某个对象时,前一次的创建出来的 一定不能再次使用,否则必然会导致数据错乱问题。
 * @author zhouzhangfei
 * 多线程中去使用的话,同时复用一个对象的时候,修改的话很容易产生线程安全的问题,这个就需要对操作都设计成原子性或者类似生命周期的设定,严格禁止同时获取一个对象进行相关操作
 *
 */
public class Clinet {
	public static void main(String[] args) {
		PenFlyWeight flyWeight = FlyweightFactory.getFlyWeight("钢笔");
		flyWeight.setColor("黑色墨水");
		System.out.println(flyWeight.toString());
		
		PenFlyWeight flyWeight2 = FlyweightFactory.getFlyWeight("钢笔");
		flyWeight2.setColor("红色墨水");
		System.out.println(flyWeight2.toString());
		//此时打印输出flyweight1,可以看到flyweight的颜色变成了红色墨水
		//所以需要注意的是在使用flyWeight2后,一定不能再次使用flyweigt1,否则会出现数据错乱的问题
		System.out.println(flyWeight.toString());
		
		
		PenFlyWeight flyWeight3 = FlyweightFactory.getFlyWeight("毛笔");
		flyWeight3.setColor("黄色颜料");
		System.out.println(flyWeight3.toString());
		
	}
}

享元模式应用和注意事项

使用场景:
1系统有大量相似对象。 2、需要缓冲池的场景。3:用后即焚

注意事项
注意事项一:
在场景一种的代码中,其实已经添加了,就是对于池子复用对象利用的效率问题,因为map集合的key可以是基本数据类型,也可以是引用数据类型,那么就要考虑在使用引用数据类型的时候,需要重写equal和hash方法了,这样可以确保key的唯一特性。注意必须得重写
同事也需要考虑的是基本数据类型和引用数据类型在匹配的时候的效率问题。总结下来其实就是 基本数据类型作为key的效率要远远高于引用数据类型的匹配效率。

注意事项二:
使用享元模式的时候,前一个复用的对象,在后一个复用之后,一定不能再继续使用。举个例子,我现在通过享元模式去创建一个钢笔,并且添加了红色的模式,为Pen1对象,然后过了一段时间后,通过享元拿到这个钢笔,然后添加了黑色墨水,为Pen2对象。其在享元模式的池子中 都是一个对象,所以,其实真正钢笔已经变成了黑色,如果此时再继续拿Pen1使用的话一定要注意,其颜色其实已经修改为了黑色,如果继续使用,必定会导致数据相关的混乱

注意事项三:
线程相关的安全问题,有共享问题必然会引发一些线程安全的问题,因为对象只有一个嘛,所谓线程安全,其实就是多个线程对同一个对象进行并发操作,可能会导致脏数据的情况,比如,此时我有很多个线程同时去通过享元获取钢笔对象,这个时候有的添加了黑色墨水,有的添加了红色墨水,所以可能导致的是真正想用黑色的却使用了红色的墨水,




欢迎继续访问,我的博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值