享元模式

定义:使用共享对象有效地支持大量细粒度的对象。
享元模式是结构型设计模式的一种,运用共享技术来减少对象的创建数量,从而提升系统性能。
享元模式主要有三个角色:
Flyweight:抽象享元角色。
ConcreteFlyweight:具体享元。
FlyweightFactory:享元工厂。享元工厂内部维护了一个对象池,每次获取对象都先从池中获取,获取不到再创建,并将对象保存在在池中。
享元对象的信息可以分为两种状态,内部状态:内部状态是可以共享的部分,它不会随外界环境的变化而变化,内部状态一般都作为对象池的key,而值就是享元对象本身。外部状态:会随着外界环境改变而改变的部分,它不被共享。

每当逢年过节我们都要买车票回家,如果用户每次购买车票都生成一个对象,想象一下会生成多少对象,这很有可能会导致内存被耗尽,并且,对象的产生和销毁都是需要消耗资源的。而享元模式模式的好处就在这里,它可以实现对象的缓存和复用,这样就不用每次都生成新对象。

//抽象享元角色
public abstract class Ticket {
	abstract void showInfo();
	//用来设置外部状态
	abstract void setGrade(int grade);
}
//具体享元对象
public class ConcreteTicket extends Ticket{
	//内部状态
    private final String from;
    private final String to;
    //外部状态
    private int grade;
    
    public ConcreteTicket(String from,String to) {
    	this.from=from;
    	this.to=to;
    }
    
    @Override
	public void setGrade(int grade) {
		this.grade = grade;
	}

	@Override
	void showInfo() {
		System.out.println("始发地:"+from+"---目的地:"+to+"  "+grade+"等座");
	}

}

可以看到对于具体的享元对象,它的内部状态我们可以用final修饰,在构造函数中初始化后就不再随外界变化而变化了,它是可以共享的。而外部状态是根据外界环境的需要而变化的,它不可共享。
享元工厂如下:

public class TicketFactory {
   //享元工厂维护的对象池
	private static Map<String , Ticket> pool=new HashMap<>();

	public static Ticket getTicket(String from,String to) {
		Ticket ticket=null;
		String key=from+"-"+to;
		if(!pool.containsKey(key)) {
			ticket=new ConcreteTicket(from, to);
			pool.put(key, ticket);
			System.out.println("创建对象:"+key);
		}
		return ticket;
	}
}

代码很简单,因为内部状态from,to是不变的,所以可以用它来作为pool的key。如果缓存中有享元对象就直接返回,否则就先创建对象,再将它缓存,最后再返回对象。享元模式就是通过缓存复用来减少大量相似对象的创建。
最后看看客户端的调用。

public class Test {
	public static void main(String args[]) {
		Ticket t1=TicketFactory.getTicket("上海", "南京");
		t1.setGrade(2);
		t1.showInfo();
		
		Ticket t2=TicketFactory.getTicket("上海", "合肥");
		t1.setGrade(2);
		t1.showInfo();
		
		Ticket t3=TicketFactory.getTicket("上海", "南京");
		t1.setGrade(1);
		t1.showInfo();
		
		Ticket t4=TicketFactory.getTicket("上海", "南京");
		t1.setGrade(3);
		t1.showInfo();
	}
}

结果:

创建对象:上海-南京
始发地:上海---目的地:南京  2等座
创建对象:上海-合肥
始发地:上海---目的地:南京  2等座
始发地:上海---目的地:南京  1等座
始发地:上海---目的地:南京  3等座

可以看到5张票但是只创建了两次对象。这就是享元模式的好处,在这里对于相同from,to的对象来说可以复用而不必从重新创建一个对象。

在java源码中也有很多的地方使用到了享元模式,比如我们常用的Integer这个类,在它的内部维护了一个对象数组。

 private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        static {
            int h = 127;
            //省略部分代码...
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
                //...
        }
    }

可以看到在类加载的时候就创建了缓存数据,默认缓存了从-128到127的Integer对象。经常会遇到类似下面这种题目。

		Integer i1=Integer.valueOf(127);
		Integer i2=127;
		System.out.println(i1==i2);  //true
		
		Integer i3=Integer.valueOf(128);
		Integer i4=128;
		System.out.println(i3==i4);  //false

没用new的情况下:i1和i2是一个对象,因为会先从缓存中取,没有超过-128到127都是可以获取到的,i1、i2获取到的是同一个对象。超过这个范围,缓存中就没有了,此时只能创建因此i3、i4不相等。

 public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

可以看出这里的享元模式去除了享元工厂,享元的目的就是为了复用。设计模式只是指导我们的思想,具体怎么做还是要看我们自己。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值