第二十二章、享元模式
享元模式是结构型设计模式之中的一个。是对对象池的一种实现。就像它的名字一样,共享对象。避免反复的创建。
我们经常使用的
String
就是使用了共享模式。所以String
类型的对象创建后就不可改变,假设当两个String
对象所包括的内容同样时,JVM仅仅创建一个String
对象相应这两个不同的对象引用。
1.定义
採用一个共享来避免大量拥有同样内容对象的开销。使用享元模式可有效支持大量的细粒度对象。
2.使用场景
(1)系统中存在大量的类似对象。
(2)细粒度的对象都具备较接近的外部状态,并且内部状态与环境不关,也就是说对象没有特定身份。
(3)须要缓冲池的场景。
PS:内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分。能够称之为享元对象的内部状态,反之随着环境改变而改变的。不可共享的状态称之为外部状态。
3.UML类图
享元模式分为单纯享元模式和复合享元模式,上图是复合享元模式。
(1)Flyweight
:享元对象抽象基类或者接口。
(2)ConcreateFlyweight
:详细的享元对象,假设有内部状态的话。必须负责为内部状态提供存储空间。
(3)UnsharadConcreateFlyweight
:复合享元角色所代表的对象是不能够共享的,并且能够分解成为多个单纯享元对象的组合。
单纯享元模式没有此项,这也是两者在结构上的差别。
(4)FlyweightFactoiy
:享元工厂,负责管理享元对象池和创建享元对象。
(5)Client
:维护对全部享元对象的引用,并且还须要存储相应的外蕴状态。
4.简单实现
情景:过春节买火车票的时候,我们须要查询车票的情况。那么假设每次查询车票时都创建一个结果,那么必定会大量的创建出很多反复的对象。频繁的去销毁他们,使得GC任务繁重。那么这时我们能够使用享元模式,将这些对象缓存起来,查询时优先使用缓存,没有缓存在又一次创建。
首先是Ticket接口(Flyweight):
public interface Ticket {
public void showTicketInfo(String bunk);
}
TrainTicket详细实现类(ConcreateFlyweight):
//火车票
public class TrainTicket implements Ticket{
public String from; // 始发地
public String to; // 目的地
public String bunk; //铺位
public int price; //价格
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
@Override
public void showTicketInfo(String bunk) {
price = new Random().nextInt(300);
System.out.println("购买 从 " + from + " 到 " + to + "的" + bunk + "火车票" + ", 价格:" + price);
}
}
TicketFactory 管理查询火车票(FlyweightFactoiy):
public class TicketFactory {
static Map<String, Ticket> sTicketMap = new ConcurrentHashMap<String, Ticket>();
public static Ticket getTicket(String from ,String to){
String key = from + "-" + to;
if(sTicketMap.containsKey(key)){
System.out.println("使用缓存 ==> " + key);
return sTicketMap.get(key);
}else{
System.out.println("创建对象 ==> " + key);
Ticket ticket = new TrainTicket(from, to);
sTicketMap.put(key, ticket);
return ticket;
}
}
}
查询:
final class Client {
public static void main(String[] args) {
Ticket ticket01 = TicketFactory.getTicket("北京", "青岛");
ticket01.showTicketInfo("上铺");
Ticket ticket02 = TicketFactory.getTicket("北京", "青岛");
ticket02.showTicketInfo("下铺");
Ticket ticket03 = TicketFactory.getTicket("北京", "西安");
ticket03.showTicketInfo("坐票");
}
}
结果
创建对象 ==> 北京-青岛
购买 从 北京 到 青岛的上铺火车票, 价格:71
使用缓存 ==> 北京-青岛
购买 从 北京 到 青岛的下铺火车票, 价格:32
创建对象 ==> 北京-西安
购买 从 北京 到 西安的坐票火车票, 价格:246
5.Android源代码中的实现
1.Message
由于Android是事件驱动的,因此假设通过new创建
Message
就会创建大量的Message
对象,导致内存占用率高,频繁GC等问题。那么Message
就採用了享元模式。
Message
通过next
成员变量保有对下一个Message
的引用。最后一个可用Message
的next
则为空。从而构成了一个Message链表。
Message Pool
就通过该链表的表头管理着全部闲置的Message
,一个Message
在使用完后能够通过recycle()
方法进入Message Pool
,并在须要时通过obtain
静态方法从Message Pool
获取。
Message
承担了享元模式中3个元素的职责,即是Flyweight
抽象。又是ConcreateFlyweight
角色。同一时候又承担了FlyweightFactoiy
管理对象池的职责。
所以使用Message推荐obtain(),不要去new了。
//1。使用new Message()
//Message mess = new Message();
//2。使用Message.obtain()
Message mess = Message.obtain();
mess.what = 1;
//Message mess = mHandler.obtainMessage(1); 与上两行的代码一样。能够參考源代码查看
mHandler.sendMessage(mess);
6.总结
1.长处
(1)大大降低应用程序创建的对象,降低程序内存的占用。增强程序的性能。
(2)使用享元模式,能够让享元对象能够在不同的环境中被共享。
2.缺点
(1)使得系统更加复杂。为了使对象能够共享,须要将一些状态外部化。这使得程序的逻辑复杂化。
(2)享元模式将需、享元对象的状态外部化,而读取外部状态使得执行时间略微变长。
7.參考
1. 深入浅出享元模式