前言
享元模式是对象池的一种实现,用来尽可能减少内存使用量,适合用于可能存在大量重复对象的场景,来缓存可共享的对象;
定义:
使用共享对象可有效地支持大量的细粒度的对象;
使用场景:
- 系统中存在大量的相似对象;
- 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份;
- 需要缓冲池的场景;
UML类图:
Flyweight:
享元对象抽象基类或者接口;
ConcreteFlyweight:
具体的享元对象;
FlyweightFactory:
享元工厂,负责管理享元对象池和创建享元对象;
示例代码
这里以读书
进行举例,现在想阅读三国演义
,但家里没有那就需要去买一本(享元工厂生产一本三国),读完了又想看水浒传
,家里也没有那也需要去买一本(享元工厂生产一本水浒),回过头,又想要在读一遍三国,那就不再在去买了,直接找到原来的一本读就好了;
下面用享元模式实现:
- 定义抽象享元类,
Book
/**
* 享元对象抽象基类
*/
interface Book {
/**
* readBook
*/
fun readBook()
}
- 定义具体享元对象,
ConcreteBook
/**
* 具体的书籍,具体的享元对象
* @param name 图书名称
*/
class ConcreteBook(private val name: String) : Book {
override fun readBook() {
println("当前正在阅读:$name")
}
}
- 定义享元工厂,
BookFactory
/**
* 图书工厂
*/
object BookFactory {
private val bookMaps = hashMapOf<String, Book>()
fun getBook(name: String): ConcreteBook {
return if (bookMaps.containsKey(name)) {
println("家里有该书籍,直接找到")
bookMaps[name] as ConcreteBook
} else {
println("家里没有该书籍,去买一本")
val book = ConcreteBook(name)
bookMaps[name] = book
book
}
}
}
- 编写调用类,验证功能
object Test {
@JvmStatic
fun main(args: Array<String>) {
//阅读三国演义
val book1 = BookFactory.getBook("三国演义")
book1.readBook()
println("============================")
//阅读水浒传
val book2 = BookFactory.getBook("水浒传")
book2.readBook()
println("============================")
//再读一遍三国演义
val book3 = BookFactory.getBook("三国演义")
book3.readBook()
}
}
输出结果如下:
家里没有该书籍,去买一本
当前正在阅读:三国演义
============================
家里没有该书籍,去买一本
当前正在阅读:水浒传
============================
家里有该书籍,直接找到
当前正在阅读:三国演义
Android源码中的享元模式
Message
,熟悉Android消息机制的同学,对于Message
肯定不陌生,我们废话不多说,直接看源码:
public final class Message implements Parcelable {
//message相关信息
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
//可以看出message是一个链表结构
Message next;
public static final Object sPoolSync = new Object();
private static Message sPool;
//当前消息池的大小
private static int sPoolSize = 0;
//消息池中最多缓存50个message
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
//如果sPool不为null,则从消息池中取消息,否则直接new Message()对象;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//从链表头取出Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
//进行回收
public void recycle() {
//如果当前正在使用,则调用recycle方法抛异常
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
//回收处理,清除相关信息,并将message插入到消息池中
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//判断消息池不满50时,将消息插入到链表头部
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
从上面我们可以看出,调用Message.obtain()
方法会从消息缓存池中取消息,这也是为什么推荐大家使用Message.obtain()
方法而不是直接new Message()
的原因,当Message
被回收的时候,会清除相关信息,将消息存放到消息池中,从而实现消息的重复利用,典型的享元模式
!!!
总结
优点:
- 大幅度地降低内存中对象的数量,降低程序内存占用;
缺点:
- 使得系统更加复杂,为了使对象可以共享,需要将一些状态外部化,使得程序的逻辑复杂化;
- 将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长;
结语
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )