您的位置:首页 > 其它

享元模式

2016-06-05 20:32 211 查看

一.基本介绍

享元模式(FlyWeight)是对象池的一种体现,享元模式用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,来缓存可共享的对象,达到对象共享,避免创建过多对象的效果,这样一来就可以提升性能,避免内存溢出等

 

 

二.使用场景

1.系统中存在大量的相似对象

2.细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关(对象没有特定身份,也就是大家都一样)。

3.需要缓冲池的场景

 

三.简单实现

过年回家买火车票,很多都是同一个区间(始发站和终点站一样),只是他们可能买的座位类型不一样(无座,硬座,卧铺,等等...)
//Ticket.java

/**
* Created by Administrator on 2016/6/5 0005.
*/
public interface Ticket {
public void showTicketInfo(String bunk);
}


//TranTicket.java

/**
* Created by Administrator on 2016/6/5 0005.
* 火车票
*/
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) {
int price = new Random().nextInt(300);
System.out.println("xcqw 购买 从"+from+"到"+to+"的"+bunk+"火车票"+",价格:"+price);
}
}


/**
* Created by Administrator on 2016/6/5 0005.
*/
public class TicketFactory {
//不合理的模式
// public static Ticket getTicket(String from,String to){
// return new TrainTicket(from,to);
// }

//享元模式
static Map<String , Ticket> sTicketMap = new ConcurrentHashMap<>();

public static Ticket getTicket(String from ,String to){
String key = from +"-"+to;
if(sTicketMap.containsKey(key)){
System.out.println("xcqw 使用缓存 ==>"+key);
return sTicketMap.get(key);
}else {
System.out.println("xcqw 创建对象 ==>"+key);
Ticket ticket = new TrainTicket(from,to);
sTicketMap.put(key,ticket);
return ticket;
}
}
}

享元模式的TicketFactory添加了一个Map,并且以出发地和目的地为键,以车票对象为值,这个map的键就是我们说的内部状态,这样即使有10000个请求北京到青岛的车票信息,那么出发地是北京,目的地是青岛的车票就只有一个,这样就从10000个对象减到1个,避免了大量的内存占用及频繁的GC操作

//MainActivity.java

<pre name="code" class="java">public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Ticket ticket01 = TicketFactory.getTicket("北京","青岛");
ticket01.showTicketInfo("上铺");
Ticket ticket02 = TicketFactory.getTicket("北京","青岛");
ticket01.showTicketInfo("下铺");
Ticket ticket03 = TicketFactory.getTicket("北京","青岛");
ticket01.showTicketInfo("坐铺");
}
}




创建对象 ==>北京-青岛
购买 从北京到青岛的上铺火车票,价格:265
使用缓存 ==>北京-青岛
购买 从北京到青岛的下铺火车票,价格:76
使用缓存 ==>北京-青岛
购买 从北京到青岛的坐铺火车票,价格:283
 
在这个例子,内部状态就是出发地和目的地,内部状态不会发生变化;外部价格就是铺位和价格,价格会随着铺位的变化而变化。
 
角色介绍
Ticket(FlyWeight):享元对象抽象基类或者接口
TranTicket(ConcreteFlyWeight):具体的享元对象。
TicketFacotry(FlyweightFactory):享元工厂,负责管理享元对象池和创建享元对象

四.Java及Android中的应用

1.在java中

String是存在于常量池中,也就是说一个String被定义之后他就被缓存到了常量池,当其他地方要使用到同样的字符串时,则直接使用的是缓存,而不会重复创建

String str1 = new String("abc");
String str2 = "abc";
String str3 = new String("abc");
String str4 = "ab"+"c";

//使用equals只判定字符值
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
System.out.println(str3.equals(str2)); //true
//等号判断,判定两个对象是不是同一个地址
System.out.println(str1 == str2); //false
System.out.println(str1 == str3); //false
System.out.println(str3 == str2); //false
System.out.println(str4 == str2); //true


Equal只根据字符值进行判断,==判断两个对象指向的内存地址是否相同。所以str2和
str4是同一个字符串对象,因为str4使用了缓存在常量池中的str2对象
,这是享元模式的重要案例

2.Android源码中的享元模式

大家都知道UI不能够在子线程中更新,这种说法不完全正确
因为并不是UI不可以在子线程更新,而是UI不可以在不是它的创建线程中进行更新。
只是绝大多数情况下UI都是从UI线程中创建的,因此其他线程更新时会抛出异常
UI只能在其被创建的线程中更新
 
(1)在Handler中的发消息我们都用的是Message.obtain();而不用new Message(),这就是用了享元模式,避免每次都new一个对象,然后又销毁,这样很浪费资源。
 
Message通过在内部构建一个链表来维护一个被回收的Message对象的对象池,当用户调用obtain会优先从池中去,如果池中没有可以复用的对象,则创建这个新的Message对象,然后这些新创建的Message对象在被使用完之后会被回收到这个对象池,当下调用obtain函数时,他们就会被复用。
 
这里的Message承担了享元模式三个元素的职责,即FlyWeight,ConcreteFlyweight,FlyweightFactory.
 
但是这里不是经典模式,他没有内部,外部状态,而且集各个职责于一身
 
 
注意:Looper封装了消息队列,Looper对象被封装在ThreadLocal中,这使得不同线程之间的Looper不能共享。而Handler通过与Looper对象绑定
来实现与执行线程的绑定,Handler会把Runnale(包装成Message)或者Message对象追加到与线程关联的消息队列中,然后在消息循环中逐个取出消息,并处理消息
 

五.总结

优点:大幅降低内存中对象的数量,但是,它坐到这点付出的代价也很高
 
缺点:
1.使得系统复杂,为了使对象共享,需要将一些状态外部化,这使得程序的逻辑复杂化
2.享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: