享元模式是一种结构型设计模式。享元模式的思想是:以共享的方式高效地支持大量的细粒度对象。简单来说就是共享池的思想,如连接池、线程池、字符串常量池等。
享元模式涉及3个角色:
- 抽象享元(Flyweight):需要被共享的对象的抽象接口。
- 具体享元(Concrete Flyweight):需要被共享的实体对象。
- 享元工厂(Flyweight Factory):集中管理共享对象并为客户提供共享对象的工厂,也就是共享池。
下面以一个伪JDBC连接池为例演示享元模式的实现。
抽象享元、具体享元:
public interface Connection {
void showInfo(); // 显示连接信息的方法
}
public class ConcreteConnection implements Connection {
private String driver;
private String url;
private String user;
private String password;
public ConcreteConnection(String driver, String url, String user, String password) {
this.driver = driver;
this.url = url;
this.user = user;
this.password = password;
}
@Override
public void showInfo() {
System.out.printf("Conn[driver=%s; url=%s; user=%s; password=%s]\n",
driver, url, user, password);
}
// Getters and Setters
// ...
// 重写hashCode()
// ...
// 重写equals():所有属性相等同返回true
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConcreteConnection that = (ConcreteConnection) o;
return (driver != null ? driver.equals(that.driver) : that.driver == null)
&& (url != null ? url.equals(that.url) : that.url == null)
&& (user != null ? user.equals(that.user) : that.user == null)
&& (password != null ? password.equals(that.password) : that.password == null);
}
}
享元工厂(连接池):
public class Pool {
private Stack<Connection> stack = new Stack<>();
private Connection sample; // 保存一个连接样本作参照
private int size; // 连接池大小
// 初始化连接池
public Pool(int size, String driver, String url, String user, String password) {
this.size = size;
for (int i = 0; i < size; i++) {
Connection conn = new ConcreteConnection(driver, url, user, password);
stack.push(conn);
}
this.sample = stack.peek();
}
// 从连接池中获取一个连接
public Connection getConnection() {
return stack.isEmpty() ? null : stack.pop();
}
// 归还一个连接给连接池
public void release(Connection conn) {
if (stack.size() == size) // 超过连接池大小
throw new RuntimeException("pool is full!");
if (!conn.equals(this.sample)) // 要归还的连接与池中的连接不一样
throw new RuntimeException("this conn is not a Flyweight");
stack.push(conn);
}
// 获取连接池中剩余连接的数量
public int getRestCount() {
return stack.size();
}
// Getters and Setters
// 这里,作为实际实现功能的集合不对外公开
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
测试:
public class FlyweightTest {
public static void main(String[] args) {
Pool pool = new Pool(5, "mysql", "127.0.0.1:3306", "test", "123");
Connection conn1 = pool.getConnection();
Connection conn2 = pool.getConnection();
conn1.showInfo();
conn2.showInfo();
System.out.printf("connection rest in pool: %d\n", pool.getRestCount());
pool.release(conn1);
System.out.printf("connection rest in pool: %d\n", pool.getRestCount());
}
}
运行结果:
Conn[driver=mysql; url=127.0.0.1:3306; user=test; password=123]
Conn[driver=mysql; url=127.0.0.1:3306; user=test; password=123]
connection rest in pool: 3
connection rest in pool: 4
结构图:
下面给出另一种形式实现的代码演示,这种形式是类似于字符串常量池的实现形式,注意与上面的例子比较。
享元:
public interface Shape {
void draw();
}
public class Circle implements Shape {
private double radius;
private Color color;
public Circle(double radius, Color color) {
this.radius = radius;
this.color = color;
}
@Override
public void draw() {
System.out.printf("Circle[radius=%f, color=%s]\n", radius, color);
}
// Getters and Setters
// ...
}
享元工厂(共享池):
public class CirclePool {
private Map<Color, Shape> map = new HashMap<>(); // 实现共享池功能的数据结构。这里以颜色作为标识,共享颜色相同的圆形对象。
// 从共享池中获取一个圆形对象
public Shape getCircle(double radius, Color color) {
Shape c = map.get(color);
if (c == null) {
// 如果池里没有,则实例化一个,并放入池中
c = new Circle(radius, color);
map.put(color, c);
}
return c;
}
public void count() {
System.out.printf("size of pool: %d\n", map.size());
}
}
// 测试
class CirclePoolTest {
public static void main(String[] args) {
CirclePool pool = new CirclePool();
pool.count();
Shape c1 = pool.getCircle(1, Color.RED);
c1.draw();
Shape c2 = pool.getCircle(1, Color.VIOLET);
c2.draw();
pool.count();
Shape c3 = pool.getCircle(2, Color.RED);
c3.draw();
pool.count();
}
}
运行结果:
size of pool: 0
Circle[radius=1.000000, color=RED]
Circle[radius=1.000000, color=VIOLET]
size of pool: 2
Circle[radius=1.000000, color=RED]
size of pool: 2
结构图:
享元模式大大减少了对象的创建,降低系统的内存消耗,使效率提高。
但同时,享元模式提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。