享元模式
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.享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长
相关文章推荐
- mysql数据库常用数据类型
- 权限设计(上) - 数据库表设计
- C++第七次实验—作业(项目3:杨辉三角)
- kali 虚拟机 联网问题解决办法
- php cookie用法之 setcookie()函数
- 《C专家编程》读书笔记
- C++第二次实验
- 团队作业七——团队作业分配
- 冒泡排序的优化及最好最差情况
- C#可扩展编程之MEF(四):见证奇迹的时刻
- java class的兼容问题
- java web(SSM框架)实现博客的上一篇、下一篇功能
- python之列表、元祖、集合、字典基础篇
- Linux下安装Nginx详细图解教程
- 基于PCA的人脸识别_Matlab实现(个人研读之后的一些总结)
- 3.5判断出栈和入栈操作序列是否合法
- Google I/0 2016 Highlights
- 团队作业之个人总结篇
- nth_element()方法总结
- 如何在博客中添加背景音乐(博客园必须获得脚本权限)