基本介绍
享元模式(Flyweight Pattern):也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象
类图
1) FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
2) ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
3) UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
4) FlyWeightFactory 享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法
内部状态和外部状态
内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态
场景一:共享网站
类图
UnsharedConcreteWebSite在此处并未体现出其用途,本文仅将其代码展现出来
代码
WebSite
public abstract class WebSite {
public abstract void use(User user);
}
User
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ConcreteWebSite
public class ConcreteWebSite extends WebSite{
private String name="";
public ConcreteWebSite(String name) {
this.name = name;
}
@Override
public void use(User user) {
System.out.println("网站类别:"+name+",使用者:"+user.getName());
}
}
UnSharedConcreteWebSite
public class UnSharedConcreteWebSite extends WebSite{
@Override
public void use(User user) {
System.out.println("这是不共享的网站");
}
}
WebSiteFactory
public class WebSiteFactory {
private Hashtable<String,WebSite> webSites=new Hashtable<>();
public WebSite getFlyweight(String name){
if(!webSites.contains(name)){
webSites.put(name,new ConcreteWebSite(name));
}
return webSites.get(name);
}
public int getWebSiteCount(){
return webSites.size();
}
}
Main
public class Main {
public static void main(String[] args) {
WebSiteFactory webSiteFactory = new WebSiteFactory();
WebSite webSite01 = webSiteFactory.getFlyweight("新闻");
webSite01.use(new User("张三"));
webSite01.use(new User("李四"));
webSite01.use(new User("王五"));
WebSite webSite02 = webSiteFactory.getFlyweight("博客");
webSite02.use(new User("赵六"));
System.out.println("网站的数量:"+webSiteFactory.getWebSiteCount());
}
}
结果
网站类别:新闻,使用者:张三
网站类别:新闻,使用者:李四
网站类别:新闻,使用者:王五
网站类别:博客,使用者:赵六
网站的数量:2
场景二:JDK-Interger 的应用源码分析
测试
public static void main(String[] args) {
// TODOAuto-generated method stub
// 如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在
// 范围类,则仍然 new
//小结:
// 1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new), 否则,就直接从缓存池返回
// 2. valueOf 方法,就使用到享元模式 //3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
Integer x = Integer.valueOf(127); // 得到 x 实例,类型 Integer
Integer y = new Integer(127); // 得到 y 实例,类型 Integer
Integer z = Integer.valueOf(127);//..
Integer w = new Integer(127);
System.out.println(x.equals(y)); // 大小,true
System.out.println(x == y); // false
System.out.println(x == z); // true
System.out.println(w == x); // false
System.out.println(w == y); // false
}
源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
场景三:数据库连接池
代码
class Pool {
// 1. 连接池大小
private final int poolSize;
// 2. 连接对象数组
private Connection[] connections;
// 3. 连接状态数组 0 表示空闲, 1 表示繁忙
private AtomicIntegerArray states;
// 4. 构造方法初始化
public Pool(int poolSize) {
this.poolSize = poolSize;
this.connections = new Connection[poolSize];
this.states = new AtomicIntegerArray(new int[poolSize]);
for (int i = 0; i < poolSize; i++) {
connections[i] = new MockConnection("连接" + (i+1));
}
}
// 5. 借连接
public Connection borrow() {
while(true) {
for (int i = 0; i < poolSize; i++) {
// 获取空闲连接
if(states.get(i) == 0) {
if (states.compareAndSet(i, 0, 1)) {
log.debug("borrow {}", connections[i]);
return connections[i];
}
}
}
// 如果没有空闲连接,当前线程进入等待
synchronized (this) {
try {
log.debug("wait...");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 6. 归还连接
public void free(Connection conn) {
for (int i = 0; i < poolSize; i++) {
if (connections[i] == conn) {
states.set(i, 0);
synchronized (this) {
log.debug("free {}", conn);
this.notifyAll();
}
break;
}
}
}
}
class MockConnection implements Connection { // 实现略
}
测试
Pool pool = new Pool(2);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
Connection conn = pool.borrow();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.free(conn);
}).start();
}
场景四:String常量池
代码
String a="hello";
String b="hello";
System.out.println(a==b);//true
String a="hello";
String b=new String("hello");
System.out.println(a==b);//false
String a=new String("hello");
String b="hello";
System.out.println(a==b);//false
String a=new String("hello");
String b=new String("hello");
System.out.println(a==b);//false
总结
①如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
②对象的大多数状态可以是外部状态,如果删除了对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑享元模式
em.out.println(a==b);//false
```java
String a=new String("hello");
String b=new String("hello");
System.out.println(a==b);//false
总结
①如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
②对象的大多数状态可以是外部状态,如果删除了对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑享元模式