论坛首页 Java企业应用论坛

JAVA设计模式:享元(Flyweight)

浏览 6131 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-07-13  

      享元模式以共享的方式高效地支持大量的细粒度对象。

      在面向对象的程序设计语言看来,一切事务都被描述成对象(Object)。对象拥有状态(属性)和行为(方法),我们将具有相同行为的对象抽象为类(Class),类可以被看作只保留行为的对象模板,类可以在运行时被重新赋予状态数据从而形成了对象。

      在运行时,对象占用一定的内存空间用来存储状态数据。如果不作特殊的处理,尽管是由同一个类生成的两个对象,而且这两个对象的的状态数据完 全相同,但在内存中还是会占用两份空间,这样的情况对于程序的功能也许并没有影响,但如果把状态相同的同一类对象在内存中进行合并,必然会大大减少存储空 间的浪费。

      举一个现实中的例子,某淘宝店经营一款畅销女式皮鞋,每天需要处理大量的订单信息,在订单中需要注明客户购买的皮鞋信息,我们将皮鞋产品抽象出来:

class Shoe{
	
	String color;//颜色
	int size;//尺寸
	String position;//库存位置
	
	public String getColor() {
		return color;
	}
	
	public void setColor(String color) {
		this.color = color;
	}
	
	public int getSize() {
		return size;
	}
	
	public void setSize(int size) {
		this.size = size;
	}
	
	public String getPosition() {
		return position;
	}
	
	public void setPosition(String position) {
		this.position = position;
	}
	
}
 

      正如上面的代码所描述,皮鞋分为颜色、尺寸和库存位置三项状态数据。其中颜色和尺寸为皮鞋的自然状态,我们称之为对象内部状态,这些状态数据只与对象本身 有关,不随外界环境的改变而发生变化。再来看库存位置,我们将这个状态称为对象的外部状态,外部状态与对象本身无必然关系,外部状态总是因为外界环境的改 变而变化,也就是说外部状态是由外界环境来决定的。在本例中,皮鞋今天放在A仓库,明天可能放在B仓库,但无论存放在哪个仓库,同一只皮鞋就是同一只皮 鞋,它的颜色和尺寸不会随着存放位置的不同而发生变化。

      享元模式的核心思想就是将内部状态相同的对象在存储时进行缓存。也就是说同一颜色同一尺寸的皮鞋,我们在内存中只保留一份实例,在访问对象时,我们访问的其实是对象缓存的版本,而不是每次都重新生成对象。

      享元模式仍然允许对象具有外部属性,由于我们访问的始终是对象缓存的版本,所以我们在使用对象前,必须将外部状态重新注入对象。由于享元模式禁止生成新的对象,所以在使用享元模式时,通常伴随着工厂方法的应用。我们来看下面的例子:

class ShoeFactory {

	Collection<Shoe> shoes = new ArrayList<Shoe>();

	Shoe getSheo(String color, int size, String position) {
		//首先在缓存中查找对象
		for (Shoe shoe : shoes) {
			if (shoe.getColor() == color && shoe.getSize() == size) {
				//在缓存中命中对象后还原对象的外部属性
				shoe.setPosition(position);
				return shoe;
			}
		}
		//如果缓存未命中则新建对象并加入缓存
		Shoe shoe = new Shoe();
		shoe.setColor(color);
		shoe.setSize(size);
		shoe.setPosition(position);
		shoes.add(shoe);
		return shoe;
	}
}
 

      通过ShoeFactory工厂,我们每次拿到的皮鞋都是缓存的版本,如果缓存中没有我们需要的对象,则新创建对象然后加入缓存中。注意上例中对象的外部属性position是如何注回对象的。

      当我们在自己的业务场景中应用享元模式时,一定要注意分清对象的内部状态和外部状态,享元模式强调缓存的版本只能包含对象的内部状态。

      事实上,Java中的String和Integer类都是享元模式的应用的例子,String类内部对所有的字符串对象进行缓存,相同的字符串在内存中只会保留一个版本。类似的,Integer类在内部对小于255的整数也进行了缓存。

      享元模式在企业级架构设计中应用的例子比比皆是,现代大型企业级应用中不可或缺的缓存体系也正是在享元模式的基础上逐步完善和发展起来的。

 

       阅读全文



       更多精彩原创文章请关注笔者的原创博客:http://www.coolfancy.com

   发表时间:2012-07-15  

两个大bug:

shoe.getColor()  == color 和 getShoe方法没有synchronized

完善一下例子吧

0 请登录后投票
   发表时间:2012-07-15  
我看你的代码就是四不像,反正你的代码实现不了你的想法,你觉的你写的工厂类里的shoe的集合会一直存在?
0 请登录后投票
   发表时间:2012-07-16  
这是工厂么?!
0 请登录后投票
   发表时间:2012-07-16  
这个思想挺好的。不过得小心内存泄漏,因为那个集合里的对象只在增加,没有减少。
0 请登录后投票
   发表时间:2012-07-16  
我例子里的代码主要是为了描述这个设计模式的思想,下面那工厂类只是伪代码,不一定可以运行。
如果要了解工厂设计模式可以考虑我博客里单独的文章。
对于字符串==和equals和问题我不想再解释了。
二楼的朋友明显没用过Spring
0 请登录后投票
   发表时间:2012-07-16  
fancy888 写道
我例子里的代码主要是为了描述这个设计模式的思想,下面那工厂类只是伪代码,不一定可以运行。
如果要了解工厂设计模式可以考虑我博客里单独的文章。
对于字符串==和equals和问题我不想再解释了。
二楼的朋友明显没用过Spring


== 和 equals的区别 难道是基于Spring么?我也没怎么用过Spring
0 请登录后投票
   发表时间:2012-07-16  
unique.wu 写道
fancy888 写道
我例子里的代码主要是为了描述这个设计模式的思想,下面那工厂类只是伪代码,不一定可以运行。
如果要了解工厂设计模式可以考虑我博客里单独的文章。
对于字符串==和equals和问题我不想再解释了。
二楼的朋友明显没用过Spring


== 和 equals的区别 难道是基于Spring么?我也没怎么用过Spring

报欠,看错楼层,三楼提出的问题可以通过Spring声明Singleton Bean解决。
0 请登录后投票
   发表时间:2012-07-16  
fancy888 写道
unique.wu 写道
fancy888 写道
我例子里的代码主要是为了描述这个设计模式的思想,下面那工厂类只是伪代码,不一定可以运行。
如果要了解工厂设计模式可以考虑我博客里单独的文章。
对于字符串==和equals和问题我不想再解释了。
二楼的朋友明显没用过Spring


== 和 equals的区别 难道是基于Spring么?我也没怎么用过Spring

报欠,看错楼层,三楼提出的问题可以通过Spring声明Singleton Bean解决。

singleton也解决不了并发访问的问题吧?
0 请登录后投票
   发表时间:2012-07-16  
fancy888 写道
unique.wu 写道
fancy888 写道
我例子里的代码主要是为了描述这个设计模式的思想,下面那工厂类只是伪代码,不一定可以运行。
如果要了解工厂设计模式可以考虑我博客里单独的文章。
对于字符串==和equals和问题我不想再解释了。
二楼的朋友明显没用过Spring


== 和 equals的区别 难道是基于Spring么?我也没怎么用过Spring

报欠,看错楼层,三楼提出的问题可以通过Spring声明Singleton Bean解决。



我不认为是这样,通过Spring声明Singleton Bean,那是只创建一个对象,是单例模式。
但是工厂不不是一定要创建单例的吧?

其实只是说一个工厂,也不管spring什么事

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics