【Java设计模式】结构型设计模式-享元模式(十)

享元模式

  • 享元模式运用共享技术来有效地支持大量细粒度对象的复用。
  • 它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

进一步阐述:

  • 享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。
  • 内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变。
  • 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。

享元模式包含四种角色:

  • Flyweight(抽象享元角色):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
  • Concrete Flyweight(具体享元角色):实现抽象享元角色中所规定的接口。
  • Unsharable Flyweight(非享元角色):是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
  • Flyweight Factory(享元工厂角色):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

案例:以一个简单的数据库连接池为例,我们不需要每次都创建数据库连接和销毁连接,这样可以避免资源开销,通过享元模式通过"池"的概念来进行,就是很好的方式。数据库连接DataBaseConnection接口就是抽象享元角色,JDBC数据库连接JDBCConnection类就是具体享元角色,JDBC数据库连接池JDBCConnectionPool类就是享元工厂角色,数据库的连接信息参数就是非享元角色。

UML类图:

客户端Client类:

java 复制代码
/**
 * 使用享元模式的客户端
 */
public class Client {
​
    public static void main(String[] args) {
        String driverClassName = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=GMT%2B8&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        //创建连接池
        JDBCConnectionPool jdbcConnectionPool = JDBCConnectionPool
                .getJdbcConnectionPoolInstance(driverClassName, url, username, password);
​
        //获取连接
        Connection connection = jdbcConnectionPool.getConnection();
        System.out.println(connection);
​
        //归还连接
        jdbcConnectionPool.closeConnection(connection);
    }
}

数据库连接DataBaseConnection接口:

java 复制代码
/**
 * 数据库连接接口(抽象享元角色)
 */
public interface DataBaseConnection {
​
    /**
     * 创建连接
     */
    Connection createConnection();
​
}

JDBC数据库连接JDBCConnection类:

java 复制代码
/**
 * JDBC数据库连接(具体享元角色)
 */
public class JDBCConnection implements DataBaseConnection {
    /**
     * 数据库驱动
     */
    private final String driverClassName;
​
    /**
     * 数据库url
     */
    private final String url;
​
    /**
     * 数据库密码
     */
    private final String username;
​
    /**
     * 数据库密码
     */
    private final String password;
​
    /**
     * 初始化数据库信息(非享元角色)
     */
    public JDBCConnection(String driverClassName, String url, String username, String password) {
        this.driverClassName = driverClassName;
        this.url = url;
        this.username = username;
        this.password = password;
    }
​
    @Override
    public Connection createConnection() {
        try {
            Class.forName(driverClassName);
            return DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

JDBC数据库连接池JDBCConnectionPool类:

java 复制代码
/**
 * JDBC数据库连接池(享元工厂角色)
 */
public class JDBCConnectionPool {
​
    //维护数据库连接的集合
    private final List<Connection> connectionList = new ArrayList<>();
​
    //持有一个静态的连接池对象
    private volatile static JDBCConnectionPool jdbcConnectionPool;
​
    /**
     * 私有构造器
     */
    private JDBCConnectionPool(String driverClassName, String url, String username, String password) {
        if (jdbcConnectionPool != null) {
            throw new RuntimeException();
        }
        //初始化连接数,这里默认先写死10个
        for (int i = 0; i < 10; i++) {
            JDBCConnection jdbcConnection = new JDBCConnection(driverClassName, url, username, password);
            Connection connection = jdbcConnection.createConnection();
            connectionList.add(connection);
        }
    }
​
    /**
     * 返回相同的连接池对象
     */
    public static JDBCConnectionPool getJdbcConnectionPoolInstance(String driverClassName
            , String url, String username, String password) {
        if (jdbcConnectionPool == null) {
            synchronized (JDBCConnectionPool.class) {
                if (jdbcConnectionPool == null) {
                    jdbcConnectionPool = new JDBCConnectionPool(driverClassName, url, username, password);
                }
            }
        }
        return jdbcConnectionPool;
    }
​
    /**
     * 获取数据库连接
     */
    public Connection getConnection() {
        //如果集合中还有连接,就获取一个返回给用户
        synchronized (JDBCConnectionPool.class) {
            if (!connectionList.isEmpty()) {
                Connection connection = connectionList.get(0);
                connectionList.remove(connection);
                return connection;
            }
        }
        //如果集合中没有连接了,那么就递归调用,等待有连接被归还
        return getConnection();
    }
​
    /**
     * 关闭连接(本质上是把连接放回集合中)
     */
    public void closeConnection(Connection connection) {
        connectionList.add(connection);
    }
}

总结:

  • 相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
  • 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
  • 读取享元模式的外部状态会使得运行时间稍微变长。
  • 享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。

典型运用场景举例: 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源,例如:数据库的连接池,线程池,对象池等。

相关推荐
快乐就好ya41 分钟前
Java多线程
java·开发语言
IT学长编程1 小时前
计算机毕业设计 二手图书交易系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·二手图书交易系统
CS_GaoMing1 小时前
Centos7 JDK 多版本管理与 Maven 构建问题和注意!
java·开发语言·maven·centos7·java多版本
艾伦~耶格尔2 小时前
Spring Boot 三层架构开发模式入门
java·spring boot·后端·架构·三层架构
man20172 小时前
基于spring boot的篮球论坛系统
java·spring boot·后端
2401_858120532 小时前
Spring Boot框架下的大学生就业招聘平台
java·开发语言
S hh2 小时前
【Linux】进程地址空间
java·linux·运维·服务器·学习
Java探秘者3 小时前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
攸攸太上3 小时前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway
2301_786964363 小时前
3、练习常用的HBase Shell命令+HBase 常用的Java API 及应用实例
java·大数据·数据库·分布式·hbase