首页 > 编程笔记 > Java笔记

使用享元模式实现资源共享池

本节我们使用享元模式来实现资源共享池。举个例子,每年春节为了买到一张回家的火车票,大家都大费周章。为了解决这一问题,12306 网站提供了自动查票的功能。如果开启自动查票的功能,则系统会将我们填写的信息缓存起来,然后定时查询余票信息。

在买票的时候,我们肯定要查询一下有没有我们需要的车票。假设一张火车票包含出发站、目的站、价格、座位类别等信息。现在要求编写查询火车票的模拟代码。

比如要求可以通过出发站、目的站来查询火车票的相关信息,那么只需构建出火车票类对象,向用户提供一个查询出发站、目的站的接口进行查询即可。

首先创建 Ticket 接口。
public interface Ticket {
    void showInfo(String bunk);
}
然后创建 TrainTicket 类实现 Ticket 接口。
public class TrainTicket implements Ticket {
    private String from;
    private String to;
    private int price;

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

    @Override
    public void showInfo(String bunk) {
        this.price = new Random().nextInt(500);
        System.out.println(this.from + "->" + this.to + ":" + bunk + "价格:" + price + "元");
    }
}
接着创建 TicketFactory 类。
public class TicketFactory {
     public static Ticket queryTicket(String from,String to){
         return new TrainTicket(from,to);
     }
}
最后编写客户端代码如下:
public static void main(String[] args) {
    Ticket ticket = TicketFactory.queryTicket("北京西","石家庄");
    ticket.showInfo("硬座");
}
运行结果如下:

首次查询,创建对象:北京西->石家庄
北京西->石家庄:硬座价格:450元

由上面代码可以知道,当客户端进行查询时,系统通过 TicketFactory 直接创建一个火车票对象,但是这样做的话,如果某个瞬间有大量用户查询同一张票的信息时,系统就会创建出大量该火车票对象,内存压力骤增。

其实更好的做法应该是缓存该火车票对象,然后提供给其他查询请求复用,这样一个对象就足以支撑数以千计的查询请求,内存完全无压力。使用享元模式就可以很好地解决这个问题。

下面我们继续优化代码,只需要在 TicketFactory 类中进行更改,增加缓存机制。
public class TicketFactory {

    private static Map<String, Ticket> sticketPool = new ConcurrentHashMap<String, Ticket>();

    public static Ticket queryTicket(String from, String to) {
        String key = from + "->" + to;
        if (TicketFactory.sticketPool.containsKey(key)) {
            System.out.println("使用缓存:" + key);
            return TicketFactory.sticketPool.get(key);
        }
        System.out.println("首次查询,创建对象:" + key);
        Ticket ticket = new TrainTicket(from, to);
        TicketFactory.sticketPool.put(key, ticket);
        return ticket;
    }
}
测试代码如下:
public class Test {
    public static void main(String[] args) {
        Ticket ticket = TicketFactory.queryTicket("北京西","石家庄");
        ticket.showInfo("硬座");
        Ticket ticket2 = TicketFactory.queryTicket("北京西","石家庄");
        ticket.showInfo("软座");
        Ticket ticket3 = TicketFactory.queryTicket("北京西","石家庄");
        ticket.showInfo("硬卧");
    }
}
运行结果如下:

首次查询,创建对象:北京西->石家庄
北京西->石家庄:硬座价格:48元
使用缓存:北京西->石家庄
北京西->石家庄:软座价格:98元
使用缓存:北京西->石家庄
北京西->石家庄:硬卧价格:118元

可以看到,除了第一次查询创建对象,后续查询相同车次票的信息都使用缓存对象,不需要创建对象。


其中,Ticket 是抽象享元角色,TrainTicket 是具体享元角色,TicketFactory 是享元工厂。有些小伙伴一定会有疑惑了,这不就是注册式单例模式吗?虽然在结构上很像,但是享元模式的重点在结构上,而不是在创建对象上。后面结合享元模式在 JDK 源码中的应用,大家应该就能彻底明白了。

所有教程

优秀文章