一、享元模式
1、定义
享元模式(Flyweight Pattern)又称作轻量级模式
,是指提供减少对象数量从而改善应用所需的对象结构的方式。其宗旨是共享细粒度的对象,将多个对同一对象的访问集中起来,不必为每个访问者都创建一个单独的对象,以此来降低内存的消耗,属于结构型设计模式。
享元模式是对象池的一种实现,类似线程池,线程池可以避免不停地创建和销毁对个对象,消耗性能。
享元模式的本质是缓存共享对象,降低内存消耗。
2、结构
(1)模式的结构
主要角色如下:
- 抽象享元角色(IFlyweight):享元对象抽象基类或者接口,同时定义出了对象的外部状态和内部状态的接口或实现。
- 具体享元角色(ConcreteFlyweight):实现抽象角色定义的业务。该角色的内部状态处理应该与环境无关,不会出现一个操作改变内部状态、同时修改了外部状态的情况。
- 享元工厂(FlyweightFactory):负责管理享元对象池和创建享元对象。
(2)享元模式的内部状态和外部状态
享元模式把一个对象的状态分为内部状态和外部状态
。
- 内部状态:指对象共享出来的信息,存储在享元对象内部,并且不会随环境的改变而改变。
- 外部状态:指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
比如:连接池中的连接对象,保存在连接对象中的用户名、密码、URL等信息,在创建对象时就已经设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接对象要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
3、优缺点
优点:
- 减少对象的创建,降低了内存中对象的数量,降低系统的内存,提高效率。
- 减少内存之外的其他资源占用。
缺点:
- 关注内、外部状态,关注线程安全问题。
- 使系统、程序的逻辑复杂。
4、使用场景
- 经常应用于系统底层的开发,来解决系统的性能问题。
- 系统有大量相似对象,需要缓冲池的场景。
5、在框架源码中使用
- Java中的 String类,Integer类中的IntegerCache等。
- Apache源码中的 对象池(GenericObjectPoo l)。
二、模式的通用实现
代码如下:
public class FlyweightPattern {
public static void main(String[] args) {
IFlyweight flyweight1 = FlyweightFactory.getFlyweight("i-aaa");
IFlyweight flyweight2 = FlyweightFactory.getFlyweight("i-bbb");
IFlyweight flyweight3 = FlyweightFactory.getFlyweight("i-aaa");
flyweight1.operation("e-123");
flyweight1.operation("e-456");
flyweight3.operation("e2-123");
}
}
// 抽象享元角色
interface IFlyweight{
void operation(String extrinsicState);
}
// 具体享元角色
class ConcreteFlyweight implements IFlyweight{
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("对象 哈希地址:" + System.identityHashCode(this));
System.out.println("内部状态:" + this.intrinsicState);
System.out.println("外部状态:" + extrinsicState);
}
}
// 享元工厂
class FlyweightFactory{
private static Map<String, IFlyweight> pool = new HashMap<>();
private FlyweightFactory() {
}
public static IFlyweight getFlyweight(String intrinsicState){
// 由于内部状态具备不变性,所以作为缓存的键
if(!pool.containsKey(intrinsicState)){
IFlyweight flyweight = new ConcreteFlyweight(intrinsicState);
pool.put(intrinsicState, flyweight);
return flyweight;
}
return pool.get(intrinsicState);
}
}
三、模式的应用实例
以数据库连接池为例,Connection连接对象再使用之前我们需要提前创建好并缓存起来,使用完毕之后,需要在放回去,来达到资源重复利用的目的。
public class ConnectionPool {
private Vector<Connection> pool;
private String url = "jdbc:mysql://localhost:3306/test_db";
private String username = "root";
private String pazzword = "123456";
private String driverClassName = "com.mysql.cj.jdbc.Driver";
private int poolSize = 100;
public ConnectionPool() {
pool = new Vector<>(poolSize);
try {
Class.forName(driverClassName);
for (int i = 0; i < poolSize; i++) {
Connection conn = DriverManager.getConnection(url, username, pazzword);
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized Connection getConnection(){
if(pool.size() > 0){
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
}
return null;
}
public synchronized void release(Connection conn){
pool.add(conn);
}
}
测试:
public static void main(String[] args) throws SQLException {
ConnectionPool connectionPool = new ConnectionPool();
Connection conn = connectionPool.getConnection();
conn.setAutoCommit(false);
conn.createStatement();
//conn.commit();
connectionPool.release(conn);
}
– 求知若饥,虚心若愚。