003-结构型-07-享元模式(Flyweight)

◇◆丶佛笑我妖孽 提交于 2019-11-26 14:24:56

一、概述

  提供了减少对象数且从而改善应用所需的对象结构的方式。运用共享技术有效地支持大是细粒度的对象。 

  它通过与其他类似对象共享数据来减小内存占用。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。

  使用共享对象的方法,用来尽可能减少内存使用量以及分享资讯。通常使用工厂类辅助,不需要多次创建重复实例。

  享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

1.1、适用场景

  常常应用于系统底层的开发,以便解决系统的性能问题。

  系统有大量相似对象、需要缓冲池的场景。

比如:String常量池、Integer常量池、数据库连接池、线程池

1.2、优缺点

优点:

  • 减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率
  • 减少内存之外的其他资源占用

缺点:

  • 关注内/外部状态、关注线程安全问题
  • 使系统、程序的逻辑复杂化

模式对比

享元模式和代理模式

  • 代理模式生成的代理类,可以使用享元模式来生成代理类。

享元模式和单例模式

  • 享元模式可以再次创建对象,也可以取缓存对象。单例模式则是严格控制只有一个实例对象。
  • 享元模式可以通过自己实现对外部的单例,也可以在需要的使用创建更多的对象。单例模式是自身控制,需要增加不属于该对象本身的逻辑。

1.3、类图角色及其职责

  

  享元模式的角色和职责

    1、抽象享元角色:为具体享元角色规定了必须实现的方法,而外蕴状态就是以参数的形式通过此方法传入。在Java中可以由抽象类、接口来担当

    2、具体享元角色:实现抽象角色规定的方法。如果存在内蕴状态,就负责为内蕴状态提供存储空间。

    3、享元工厂角色:负责创建和管理享元角色。要想达到共享的目的,这个角色的实现是关键。

    4、客户端角色   :维护对所有享元对象的引用,而且还需要存储对应的外蕴状态。

  两个状态

    1、内蕴状态存储在享元内部,不会随环境的改变而有所不同,是可以共享的
    2、外蕴状态是不可以共享的,它随环境的改变而改变的,因此外蕴状态是由客户端来保持(因为环境的变化是由客户端引起的)。
 
  FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。

1.4、演进过程

  一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

  通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能

  

项目代码:https://github.com/bjlhx15/patterns.git 中的patterns-base 中 structure/flyweight

  示例

public class ConnectionPool {
    private Vector<Connection> pool;

    /*公有属性*/
    private String url = "jdbc:mysql://localhost:3306/test";
    private String username = "root";
    private String password = "root";
    private String driverClassName = "com.mysql.jdbc.Driver";

    private int poolSize = 100;
    private static ConnectionPool instance = null;
    Connection conn = null;

    /*构造方法,做一些初始化工作*/
    private ConnectionPool() {
        pool = new Vector<Connection>(poolSize);

        for (int i = 0; i < poolSize; i++) {
            try {
                Class.forName(driverClassName);
                conn = DriverManager.getConnection(url, username, password);
                pool.add(conn);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /* 返回连接到连接池 */
    public synchronized void release() {
        pool.add(conn);
    }

    /* 返回连接池中的一个数据库连接 */
    public synchronized Connection getConnection() {
        if (pool.size() > 0) {
            Connection conn = pool.get(0);
            pool.remove(conn);
            return conn;
        } else {
            return null;
        }
    }
}  

二、扩展

2.1、JDK

1、Integer类里面的valueOf方法;

2、IntegerCache

2.2、tomcat的连接池:GenericObjectPoolConfig

org.apache.commons.pool2.impl.GenericKeyedObjectPool

  

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!