享元模式

来源:互联网 发布:php 分销系统源码 pc 编辑:程序博客网 时间:2024/05/31 19:01

一.基本介绍

享元模式(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):享元工厂,负责管理享元对象池和创建享元对象



四.JavaAndroid中的应用

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)); //trueSystem.out.println(str1.equals(str3)); //trueSystem.out.println(str3.equals(str2)); //true//等号判断,判定两个对象是不是同一个地址System.out.println(str1 == str2); //falseSystem.out.println(str1 == str3); //falseSystem.out.println(str3 == str2); //falseSystem.out.println(str4 == str2); //true

Equal只根据字符值进行判断,==判断两个对象指向的内存地址是否相同。所以str2str4是同一个字符串对象,因为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.享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长

0 0
原创粉丝点击