设计模式-享元模式-结构型

定义:

使用共享对象,可有效支持大量的细粒度对象

类型:

结构型

理解:

当需要创建大量相似对象时,会导致性能下降,可以考虑将相似的对象进行复用;复用的方法就是:将公共不变的内部状态和变化的外部状态分离,通过享元工厂类来获取对象,如果对象已经存在缓存池中,则直接返回缓存池中的对象,如果不存在则创建并存入缓存池中。

  • 核心思想就是复用已经存在的对象

有些文章里使用游戏里英雄的例子,认为英雄这个对象是可以使用享元模式复用的,把英雄的基本属性作为内部状态,英雄坐标、装备、状态等作为外部状态,在使用享元工厂类获取到对象后,修改它的外部状态,就得到了一个新的英雄,这是没问题,但是由于复用的是同一个对象,改变了外部状态,之前创建的英雄的外部状态也会一起改变,这显示是会有问题的

class ChessPieces(private val color: String) {
    fun downPieces(x: Int,y:Int) {
        Tool.print("${color}棋子,落子位置:${x},${y}")
    }
}
class PiecesColor {
    companion object{
        val WHITE ="红色"
        val BLACK ="黑色"
    }
}
class PiecesFactory {
    val mCachePieces = HashMap<String, ChessPieces>()

    fun getChessPieces(color: String): ChessPieces {
        var chessPieces = mCachePieces[color]
        if (chessPieces == null) {
            chessPieces = ChessPieces(color)
            mCachePieces[color] = chessPieces
        }
        return chessPieces
    }
}
fun main(args: Array<String>) {
    val factory = PiecesFactory()
    var chessPieces = factory.getChessPieces(PiecesColor.WHITE)
    chessPieces.downPieces(1, 2)

    chessPieces = factory.getChessPieces(PiecesColor.BLACK)
    chessPieces.downPieces(7, 8)

    chessPieces = factory.getChessPieces(PiecesColor.WHITE)
    chessPieces.downPieces(3, 4)

    chessPieces = factory.getChessPieces(PiecesColor.BLACK)
    chessPieces.downPieces(9, 10)

    println("缓存的对象:${factory.mCachePieces}")
}

运行结果:

2019-08-28 16:27:24.122:红色棋子,落子位置:1,2
2019-08-28 16:27:24.123:黑色棋子,落子位置:7,8
2019-08-28 16:27:24.123:红色棋子,落子位置:3,4
2019-08-28 16:27:24.123:黑色棋子,落子位置:9,10
缓存的对象:{黑色=com.example.designpattern.flyweight.ChessPieces@5451c3a8, 红色=com.example.designpattern.flyweight.ChessPieces@2626b418}

Process finished with exit code 0

上面例子可以看到,棋子的颜色是属于内部不容易发生变化的状态,而位置而是会变化的,当想要获取某个颜色棋子的时候,先从缓存对象池里找,如果有的话直接返回,没有则创建并加入缓存对象池中,从而复用不同颜色棋子,可以看到虽然获取了很多个棋子,但是最终缓存池中就只有黑色和白色两个颜色的棋子对象

类结构图如下:
设计模式-享元模式-结构型_第1张图片

Android中的享元模式:

只要是有对象复用的地方就会有享元模式的影子,比如:ListView、GridView、RecycleView对应的Adapter中的getView方法就会复用已有的View,从而避免大量的创建View;
我们在发送消息的时候,通常都是建议通过Message.obtain()来获取一个Message对象,而不是直接new Message(),这是因为Message.obtain方法里用到了享元模式,会复用已经存在的Message对象,先来看看Message的obtain方法:

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();
    }
    .. ..
}

从代码里可以看到,obtain方法就相当于享元工厂类,先判断对象池里是否有缓存的对象,没有的话就创建一个新的对象,Mesage里的对象池采用的是链表结构实现,sPool总是指向链表的第一个元素,跟标准的享元模式不一样的地方是它创建新的对象之后,并没有直接加入到缓存池当中,通过源码分析可以知道,当在Looper中Message处理完毕之后会调用recycleUnchecked方法,在这个方法里才会回收Message对象,加入到缓冲池里:
Looper.java

public static void loop() {
    .. ..
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        .. ..
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            .. ..
        }
        .. ..
        msg.recycleUnchecked();//回收Message
    }
}

Message.java

 void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

优点:

  • 享元模式使得相似对象得以复用,需要创建的对象大大的减少

缺点:

  • 享元模式需要分离出内部不变的状态和外部变化的状态,使得系统变得复杂
  • 读取享元对象的外部状态使得运行时间变得更长

你可能感兴趣的:(设计模式,Android,设计模式,享元模式)