一、定义:
使用享元对象有效地支持大量的细粒度对象。
二、角色
- Flyweight:抽象的享元角色,是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。
- ConcreteFlyweight:具体的享元角色,是具体的产品类,实现抽象角色,定义相关业务。
- UnsharedConcreteFlyweight:不可共享的角色,一般不会出现在享元工厂。
- FlyweightFactory:享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象的方法。
三、使用场景
- 系统中存在大量的相似对象
- 细粒度的对象都具备较接近的外部状态,并且内部状态和环境无关,也就是对象没有特定身份
- 需要缓冲池的场景
四、案例代码
/**
* 享元对象抽象基类
*/
interface Book {
void readBook();
}
/**
* 具体的享元对象
*/
class ConcreteBook implements Book {
private String name;
public ConcreteBook(String name) {
this.name = name;
}
@Override
public void readBook() {
System.out.println("当前正在阅读:" + name);
}
}
/**
* 定义享元工厂 -- 图书工厂
*/
class BookFactory {
private static Map<String, Book> bookMaps = new HashMap<>();
public static ConcreteBook getBook(String name) {
if (bookMaps.containsKey(name)) {
System.out.println("有该书籍,直接找到");
return (ConcreteBook) bookMaps.get(name);
} else {
System.out.println("没有该书籍,去买一本,重新创建");
ConcreteBook book = new ConcreteBook(name);
bookMaps.put(name, book);
return book;
}
}
}
//客户端
public class Client {
public static void main(String[] args) {
ConcreteBook book1 = BookFactory.getBook("三国演义");
book1.readBook();
ConcreteBook book2 = BookFactory.getBook("三国演义");
book2.readBook();
}
}
五、在Android中的使用
- Android Handler消息机制,Message
发送消息的时候最终调用sendEmptyMessageAtTime,通过Message.obtain();创建message并发送。享元模式就是从obtain这里切入。
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
Message.obtain
message要么是从最后new Message返回一个新的对象,要么返回sPool。当返回sPool时就是返回已创建的重复对象
private static int sPoolSize = 0;
Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
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();
}
Message是一个单链表对象
Message包含一个next的Message对象。sPoolSize表示个数,以此形成单链表结构
-
JDK中的String
jdk中的String也是类似的消息池。一个String被定义过后就存在于常量池中。当其他地方使用相同的字符串时,实际使用的是缓存。
六、优缺点
优点:
- 大幅度地降低内存中对象的数量,降低程序内存占用;
缺点:
- 使得系统更加复杂,为了使对象可以共享,需要将一些状态外部化,使得程序的逻辑复杂化;
- 将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长;