美文网首页程序员编程设计模式Java设计模式
【设计模式笔记】(二十)- 享元模式

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

作者: MrTrying | 来源:发表于2018-07-05 20:26 被阅读47次

    简介

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

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

    public abstract class RvBaseViewHolder<T> extends RecyclerView.ViewHolder {
    
        private SparseArray<View> mViews;
    
        public RvBaseViewHolder(@NonNull View itemView) {
            super(itemView);
            mViews = new SparseArray<>();
        }
    
        public abstract void bindData(int position, @Nullable T data);
    
        /**快速获取view*/
        public <V extends View> 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图

    享元模式.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<String,Ticket> 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对象。这样子,这段代码大概就能理解了。

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

    总结

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

    相关文章

      网友评论

        本文标题:【设计模式笔记】(二十)- 享元模式

        本文链接:https://www.haomeiwen.com/subject/rqkmuftx.html