【设计模式笔记】(二十)- 享元模式

【设计模式笔记】(二十)- 享元模式_第1张图片

简介

享元模式,是对象池的一种实现,主要用于减少创建对象的数量,以减少内存占用和提高性能。定义:运用共享技术有效地支持大量细粒度的对象。意思就是采用对象共享的形式来实现大量对象的情况。有大量对象的情况,有可能导致内存溢出或者重复创建之前已经创建的相同对象。

先举个简单的例子,android中的ViewHolder缓存view,为了优化性能存在一种写法,使用一个集合存储已经被实例化过的view,就不需要每次都去创建了,这就是享元模式的一种简单应用。

public abstract class RvBaseViewHolder extends RecyclerView.ViewHolder {

    private SparseArray mViews;

    public RvBaseViewHolder(@NonNull View itemView) {
        super(itemView);
        mViews = new SparseArray<>();
    }

    public abstract void bindData(int position, @Nullable T data);

    /**快速获取view*/
    public  V findViewById(int viewId){
        View view = mViews.get(viewId);
        if(null == view){
            view = itemView.findViewById(viewId);
            if(null != view){
                mViews.put(viewId,view);
            }else{
                return null;
            }
        }
        return (V) view;
    }
}

UML图

【设计模式笔记】(二十)- 享元模式_第2张图片
享元模式.png
  • Flyweight:享元模式抽象类或接口
  • ConcreateFlyweight:具体的享元对象
  • FlyweightFactory:享元工厂,负责管理享元对象池和创建享元对象

Flyweight代表轻量级的意思。

简单示例

过年抢票,大家肯定都不陌生,各种刷票插件、软件什么的。在用户设置好出发和到达之后,每次查询请求都返回一系列的车票结果。当数千万的用户在不断请求查询结果时,如果每次查询结果都是重新创建返回的,可想而知,肯定会有大量的重复对象的创建、销毁,内存占用和GC的压力都会随之增大。而享元模式就能很好的应对这种情况,车次都是固定的,根据出发地和到达地查询出来的车次基本都是相同的(当然你还可以添加更多的筛选条件)。

我们可以将这些共享的对象缓存起来,用户查询时优先使用缓存,如果没有缓存则重新创建,这样就不必要在重复创建和销毁对象了。

首先,创建一个Ticket接口,定义输出车票信息的方法

public interface Ticket {
    public void showTicketInfo(String info);
}

再是具体的实现TrainTicket

public class TrainTicket implements Ticket {
    //出发地
    private final String from;
    //到达地
    private final String to;
    //铺位
    private String bunk;
    //价格
    private int price;

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

    @Override
    public void showTicketInfo(String bunk) {
        price = new Random().nextInt(200);
        System.out.println("查询 从 "+from+" 到 " + to + " 的 " + bunk + " 车票,价格:" + price);
    }
}

接着就是TicketFactory类,不同于之前的工厂模式,工厂模式每个返回的对象都是新创建的,而享元模式需要做缓存,具体代码如下:

public class TicketFactory {
    private static Map ticketMap = new ConcurrentHashMap<>();

    public static Ticket getTicket(String from,String to){
        final String key = from + "-" + to;
        if(ticketMap.containsKey(key)){
            return ticketMap.get(key);
        }else{
            Ticket ticket = new TrainTicket(from, to);
            ticketMap.put(key,ticket);
            return ticket;
        }
    }
}

这就面了,每次获取车票对象时的创建,享元模式有效的减少了重复对象的创建。

Android中的享元模式

当我们需要用到Handler发送信息的时候可能会注意到一点,并不是每次都是创建一个Message对象,Handler有个obtainMessage方法其实还有几个重写方法,这个方法可以从已经创建过的Message中重新获取一个Message对象,以此来降低创建Message对象的内存开销。

HandlerobtainMessage方法调用了Messageobtain方法,最后具体的代码如下:

public final class Message implements Parcelable {
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    
    //代码省略...

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    //代码省略...
}

其中sPoolSync为同步锁对象,而sPool是一个Message对象,这里可能会有点奇怪为什么会叫sPool却是一个Message对象。其实这里Message的实现是以列表的形式实现的,next也是一个Message对象指向的就是下一个Message对象。这样子,这段代码大概就能理解了。

这里源码只说这么多,有兴趣的可以阅读源码。

总结

享元模式还是比较简单的,在创建重复对象的情况中大大降低了内存的消耗,提高了程序的性能。同时也提高了系统的复杂性,需要分离出外部状态和内部状态,而且外部状态应具有固化性,不应该随内部状态改变而改变,这样就使得程序变的很混乱。

你可能感兴趣的:(【设计模式笔记】(二十)- 享元模式)