您的位置:首页 > 移动开发 > Android开发

《Android源码设计模式解析与实战》读书笔记(二十二)

2016-01-18 09:21 489 查看

第二十二章、享元模式

享元模式是结构型设计模式之一,是对对象池的一种实现。就像它的名字一样,共享对象,避免重复的创建。我们常用的
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):

[code]public interface Ticket {

    public void showTicketInfo(String bunk);

}


TrainTicket具体实现类(ConcreateFlyweight):

[code]//火车票
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):

[code]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;
        }

    }
}


查询:

[code]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("坐票");
    }
}


结果

[code]创建对象 ==> 北京-青岛
购买 从 北京 到 青岛的上铺火车票, 价格: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了。

[code]//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. 深入浅出享元模式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: