DesignPattern - 享元模式【结构型】

884 阅读4分钟

欢迎关注微信公众号:FSA全栈行动 👋

一、享元模式介绍

享元模式(Flyweight Pattern)属于结构型模式,主要用于减少创建对象的数量,以减少内存占用和提高性能,它提供了减少对象数量从而改善应用所需的对象结构的方式;享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

  • 状态

    • 内部状态:不会随环境的改变而所有不同,是可以共享的
    • 外部状态:它随环境的改变而改变的,是不可以共享的,因此外部状态是由客户端来保持(因为环境的变化一般是由客户端引起)
  • 核心组成

    • 抽象享元角色:为具体享元角色规定了必须实现的方法,而外部状态就是以参数的形式通过些方法传入
    • 具体享元角色:实现抽象角色规定的方法,如果存在内部状态,就负责为内部状态提供存储空间
    • 享元工厂角色:负责创建和管理享元角色。(要想达到共享的目的,这个角色很关键)
    • 客户端角色:维护对所有享元对象的引用,而且还需要存储对应的外部状态
  • 应用场景

    • Java 中的 String,如果字符串常量池里有则返回,如果没有则创建一个字符串保存在字符串常量池里
    • 数据库连接池、线程池等
    • 如果系统有大量相似的对象,或者需要用缓冲池的时候可以使用享元模式,也就是大家常说的池化技术
    • 如果发现某个对象生成了大量细粒度的实例,并且这些实例除了几个参数外基本是相同的,如果把那些共享参数移到类外面,在方法调用时将他们传递进来,就可以通过共享对象,减少实例的个数
  • 优点

    • 大大减少了对象的创建,降低了程序内存的占用,提高效率
  • 缺点

    • 提高了系统的复杂度,需要分离出内部状态和外部状态

注意划分内部状态和外部状态,否则可能会引起线程安全问题,必须有一个工厂类加以控制

二、享元模式代码实现

创建 抽象享元角色,规定具体享元角色必须实现的方法:

/**
 * 抽象享元角色:sql数据库连接
 *
 * @author GitLqr
 */
public abstract class SqlConnection {

	protected boolean isConnected;

	// 连接数据库
	public abstract void connect();

	// 断开连接
	public abstract void disconnect();

	// 增
	public abstract void create(String sql);

	// 删
	public abstract void delete(String sql);

	// 改
	public abstract void update(String sql);

	// 查
	public abstract String select(String sql);
}

说明:其中成员属性 isConnected 是内部状态,方法参数 sql 是外部状态

创建 具体享元角色:

/**
 * 具体享元角色:Mysql数据库连接
 *
 * @author GitLqr
 */
public class MySqlConnection extends SqlConnection {

	@Override
	public void connect() {
		if (isConnected)
			return;
		try {
			System.out.println("准备连接 mysql数据库...");
			Thread.sleep(1000);
			System.out.println("成功连接 mysql数据库");
			isConnected = true;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void disconnect() {
		System.out.println("已断开 mysql数据库连接");
		isConnected = false;
	}

	@Override
	public void create(String sql) {
		System.out.println("使用mysql创建数据");
	}

	@Override
	public void delete(String sql) {
		System.out.println("使用mysql删除数据");
	}

	@Override
	public void update(String sql) {
		System.out.println("使用mysql更新数据");
	}

	@Override
	public String select(String sql) {
		System.out.println("使用mysql查询数据");
		return null;
	}
}

/**
 * 具体享元角色:Oracle数据库连接
 *
 * @author GitLqr
 */
public class OracleConnection extends SqlConnection {

	@Override
	public void connect() {
		if (isConnected)
			return;
		try {
			System.out.println("准备连接 Oracle数据库...");
			Thread.sleep(500);
			System.out.println("成功连接 Oracle数据库");
			isConnected = true;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void disconnect() {
		System.out.println("已断开 Oracle数据库连接");
		isConnected = false;
	}

	@Override
	public void create(String sql) {
		System.out.println("使用oracle创建数据");
	}

	@Override
	public void delete(String sql) {
		System.out.println("使用oracle删除数据");
	}

	@Override
	public void update(String sql) {
		System.out.println("使用oracle更新数据");
	}

	@Override
	public String select(String sql) {
		System.out.println("使用oracle查询数据");
		return null;
	}
}

创建 享元工厂角色:

注意:这个工厂类是享元模式的核心,由它管理所有享元对象

/**
 * 享元工厂角色:sql连接工厂类
 *
 * @author GitLqr
 */
public class SqlConnectionFactory {

	// 数据库连接缓存(存放各种数据库的连接对象)
	private static Map<String, SqlConnection> connectionCache = new HashMap<>();

	private SqlConnectionFactory() {
	}

	public static SqlConnection getConnection(String type) {
		// 缓存池里有,直接提取返回
		if (connectionCache.containsKey(type)) {
			return connectionCache.get(type);
		} else {
			// 缓存池里没有,先创建享元对象
			SqlConnection sqlConnection = null;
			switch (type) {
			case "mysql":
				sqlConnection = new MySqlConnection();
				break;
			case "oracle":
				sqlConnection = new OracleConnection();
				break;
			default:
				break;
			}
			// 再将享元对象缓存起来
			if (sqlConnection != null) {
				connectionCache.put(type, sqlConnection);
			}
			// 最后返回享元对象
			return sqlConnection;
		}
	}
}

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~