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

刚开始看到这个模式的名字的时候,还感觉看不懂的样子,以为很难,但是看了它的介绍后就没这么恐惧了。享就是共享,元就是一个单元,也就是对象的意思,所以享元就是共享对象,从这点来看享元模式跟单例模式挺像的,或者说享元模式是单例模式的一种扩展。享元模式是对象池的一种实现,它的目的也是达到对象共享,避免创建多对象,以此来提升性能。它并不是只有一个对象,享元对象中的部分状态可以共享,这部分状态称为内部状态,内部状态不会随着环境变化而变化;不会共享的状态称为外部状态,外部状态随着环境变化而变化。享元模式中有一个容器,一般为 Map,它的键就是享元对象的状态,值就享元对象本身,客户端通过这个内部状态来获取享元对象,如果有缓存对象则直接获取,如果没有则创建享元对象并存储到容器中。


第二十二章 享元模式

1.定义

使用共享对象可以有效地支持大量细粒度的对象。( Java 中细粒度的定义:细粒度模型,通俗的讲就是将业务模型中的对象加以细分,从而得到更科学合理的对象模型,直观的说就是划分出很多对象)。

2.使用场景

1).系统中存在大量的相似对象。
2).细粒度的对象都具备接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。
3).需要缓冲池的场景。

3.简单实现

我自己倒没想出什么好的别的例子,就借用书中的例子。买火车票是件很困难的事情,特别是春运期间,现在抢票软件很多,无数人用抢票软件向服务端发出请求,对于每一次请求服务端都得作出回应,但是如果每次查询的车票结果都新建一个对象的话,势必会造成大量对象的创建,而这些对象其实是有些相似之处的,所以我们并不需要创建重复的对象,一般人买火车票,出发地和目的地是不会变的,我们先假设车次只有一趟,所以只有席别不同,然后导致价格不同。这样我们就可以借用享元模式来解决这种场景的问题。

抽象车票接口:
public interface Ticket {
    void queryTicket(String bunk);
}

火车票类:
public class TrainTicket implements Ticket {
    private String from;
    private String to;
    private String bunk;
    private int price;

    public TrainTicket(String from, String to) {
        this.from = from;
        this.to = to;
    }

    @Override
    public void queryTicket(String bunk) {
        if (bunk.equals("无座")) {
            price = 229;
        } else if (bunk.equals("硬座")) {
            price = 229;
        } else if (bunk.equals("硬卧")) {
            price = 328;
        } else if (bunk.equals("软卧")) {
            price = 613;
        }
        System.out.println("查询从 " + from + " 到 " + to + " 的车票,席别为 " + bunk + " 票价为:" + price + " 元");
    }
}

火车票工厂:
public class TrainTicketFactory {
    //创建一个容器来保存对象,避免重复创建
    private static Map<String, Ticket> sTicketMap = new HashMap<String, Ticket>();

    public static Ticket getTicket(String from, String to) {
        //将出发地和目的地连起来作为键
        String key = from.concat("-").concat(to);
        if (sTicketMap.containsKey(key)) {
            System.out.println("已经有该对象:" + key);
            return sTicketMap.get(key);
        } else {
            Ticket mTicket = new TrainTicket(from, to);
            sTicketMap.put(key, mTicket);
            System.out.println("没有该对象:" + key + ",创建一个新对象");
            return mTicket;
        }
    }
}

客户端调用:
        Ticket ticket1 = TrainTicketFactory.getTicket("北京", "重庆");
        ticket1.queryTicket("无座");
        System.out.println(ticket1.toString()+","+ticket1.hashCode());
        Ticket ticket2 = TrainTicketFactory.getTicket("北京", "重庆");
        ticket2.queryTicket("硬座");
        System.out.println(ticket2.toString()+","+ticket1.hashCode());
        Ticket ticket3 = TrainTicketFactory.getTicket("北京", "重庆");
        ticket3.queryTicket("硬卧");
        System.out.println(ticket3.toString()+","+ticket1.hashCode());
        Ticket ticket4 = TrainTicketFactory.getTicket("北京", "重庆");
        ticket4.queryTicket("软卧");
        System.out.println(ticket4.toString()+","+ticket1.hashCode());

运行程序,打印结果如下:

可以看到内存地址和 hashCode 都是一样的,所以这个火车票对象只创建了一次,后面都是使用容器中缓存的对象。这样一来,即使我们查询成百上千次,那么还是只会有一个对象,就避免了大量的内存占用和 GC 频繁的对象回收。

4.总结

享元模式的实现比较简单,但是它的作用是极为重要的。
优点:
1).减少应用程序创建的对象,降低程序内存的占用,增强程序性能。
缺点:
1).让程序更复杂,为了让对象实现共享,需要将一些状态定义为外部状态,使逻辑变得复杂。
2).将享元对象的状态外部化,但是读取外部状态会使运行时间稍长。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值