内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存.
核心:
享元模式以共享的方式高效地支持大量细粒度对象的重用。
享元对象能做到共享的关键是区分了内部状态和外部状态。
• 内部状态:可以共享,不会随环境变化而改变
• 外部状态:不可以共享,会随环境变化而改变
享元模式实现的UML图
享元模式实现:
(1)Flyweight:抽象享元类,所有具体享元类的超类或者接口,通过这个接口,Flyweight 可以接受并作用于外部专题;
(2)ConcreteFlyweight:具体享元类。指定内部状态,为内部状态增加存储空间。
(3)UnsharedConcreteFlyweight:非共享具体享元类。指出那些不需要共享的Flyweight子类。
(4)FlyweightFactory:享元工厂类,用来创建并管理Flyweight对象,它主要用来确保合理地共享Flyweight。
在Java语言中,String类型就使用了享元模式。String对象是final类型,对象一旦创建就不可改变,同时JAVA会确保一个字符串常量,在常量池中只能存在一份拷贝。
package com.lq.flyWeight;
/**
* @author lq
* @PACKAGE_NAME: com.lq.flyWeight
* @CLASS_NAME: Client
* @date 2022/11/8 23:03
* @Description:
*/
public class Client {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3 == s4);
//intern 取常量池中的值,虽然对象存在,指定的字符串也是同一块区域
System.out.println(s3.intern() == s1);
System.out.println(s3.intern() == s4.intern());
}
}
打印结果:
true
false
false
true
true
抽象享元类
package com.lq.flyWeight;
/**
* @author lq
* @PACKAGE_NAME: com.lq.flyWeight
* @CLASS_NAME: ChessFlyWeight
* @date 2022/11/8 22:28
* @Description: 抽象享元类
*/
public interface FlyWeight {
/**
* 用以规定所有具体享元角色需要实现的方法.
*/
void operation(String extrinsicState);
}
具体享元类
package com.lq.flyWeight;
/**
* @author lq
* @PACKAGE_NAME: com.lq.flyWeight
* @CLASS_NAME: ConcreteChess
* @date 2022/11/8 22:29
* @Description: 具体享元类
*/
public class ConcreteFlyWeight implements FlyWeight {
private String intrinsicState = null;
/**
* 构造函数中内蕴状态作为参数传入.
*/
public ConcreteFlyWeight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
/**
* 外蕴状态作为参数传入方法中,可改变方法的行为,但并不改变对象的内蕴状态.
*/
@Override
public void operation(String extrinsicState) {
System.out.println();
System.out.println("内蕴状态:" + intrinsicState);
System.out.println("外蕴状态:" + extrinsicState);
System.out.println("-------------------------");
}
@Override
public String toString() {
return this.intrinsicState;
}
}
享元工厂类
package com.lq.flyWeight;
import java.util.HashMap;
import java.util.Map;
/**
* @author lq
* @PACKAGE_NAME: com.lq.flyWeight
* @CLASS_NAME: ChessFlyWeightFactory
* @date 2022/11/8 22:30
* @Description: 享元工厂类
*/
public class FlyWeightFactory {
// 用来登记和存储已经创建过的享元对象
private Map<String, FlyWeight> flyWeightMap = new HashMap<String, FlyWeight>();
// 采用单例模式
private static FlyWeightFactory flyWeightFactory = new FlyWeightFactory();
// 私有化享元工厂的构造方法
private FlyWeightFactory() {
}
// 获取单例享元工厂的实例
public static FlyWeightFactory getFlyWeightFactory() {
return flyWeightFactory;
}
public FlyWeight getFlyWeight(String intrinsicState) {
FlyWeight flyWeight = flyWeightMap.get(intrinsicState);
if (null == flyWeight) {
flyWeight = new ConcreteFlyWeight(intrinsicState);
flyWeightMap.put(intrinsicState, flyWeight);
}
return flyWeight;
}
public Map<String, FlyWeight> getFlyWeightMap() {
return flyWeightMap;
}
}
package com.lq.flyWeight;
import java.util.Map;
/**
* @author lq
* @PACKAGE_NAME: com.lq.flyWeight
* @CLASS_NAME: Main
* @date 2022/11/8 22:31
* @Description:
*/
public class Main {
public static void main(String[] args) {
FlyWeightFactory flyWeightFactory = FlyWeightFactory.getFlyWeightFactory();
flyWeightFactory.getFlyWeight("A").operation("位置");
flyWeightFactory.getFlyWeight("B").operation("位置");
flyWeightFactory.getFlyWeight("A").operation("位置");
System.out.println("已存储的享元对象个数:" + flyWeightFactory.getFlyWeightMap().size() + "个");
for (Map.Entry<String, FlyWeight> entry : flyWeightFactory.getFlyWeightMap().entrySet()) {
System.out.println(entry.getValue());
}
}
}
结果
内蕴状态:A
外蕴状态:位置
-------------------------
内蕴状态:B
外蕴状态:位置
-------------------------
内蕴状态:A
外蕴状态:位置
-------------------------
已存储的享元对象个数:2个
A
B
享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池。 String类的设计也是享元模式
1、极大减少内存中对象的数量
2、相同或相似对象内存中只存一份,极大的节约资源,提高系统性能
3、外部状态相对独立,不影响内部状态
1、模式较复杂,使程序逻辑复杂化
2、为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态
3、使运行时间变长。用时间换取了空间。