文章目录
享元模式(Flyweight Pattern)
应用
使用模板(Java)
复合享元模式
public class FlyweightPattern {
public static void main(String[] args) {
//创建两个外部状态
UnsharedClass unsharedClassA = new UnsharedClass("外部操作A");
UnsharedClass unsharedClassB = new UnsharedClass("外部操作B");
//获取内部状态X的具体享元,并通过该享元执行外部操作A
Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
flyweightX.operate(unsharedClassA);
//获取内部状态Y的具体享元,并通过该享元执行外部操作A
Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
flyweightY.operate(unsharedClassA);
//获取内部状态Z的具体享元,并通过该享元执行外部操作B
Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z");
flyweightZ.operate(unsharedClassB);
//获取内部状态X的具体享元,并通过该享元执行外部操作A
Flyweight flyweightReX = FlyweightFactory.getFlyweight("X");
flyweightReX.operate(unsharedClassA);
}
}
//抽象享元角色,在此处设置独立的享元角色抽象类
abstract class Flyweight {
//内部状态
public String intrinsic;
public Flyweight(String intrinsic) {
this.intrinsic = intrinsic;
}
//要求享元角色必须接受外部状态
public abstract void operate(UnsharedClass unsharedClass);
}
//具体的享元角色
class ConcreteFlyweight extends Flyweight {
//确定内部状态,并使用该状态构件相应的享元对象
public ConcreteFlyweight(String intrinsic) {
super(intrinsic);
}
//通过传入外部对象来实现具体的操作
@Override
public void operate(UnsharedClass unsharedClass) {
System.out.println("外部操作:");
unsharedClass.operate();
System.out.println("内部操作" + intrinsic);
System.out.println("========================");
}
}
//非享元角色,即变动不会影响享元角色的部分
class UnsharedClass {
public String param;
public UnsharedClass(String param) {
this.param = param;
}
public void operate() {
System.out.println(param);
}
}
//享元工厂角色
class FlyweightFactory {
//定义一个池容器
private static HashMap<String, Flyweight> pool = new HashMap<>();
//静态方法,用来根据条件返回对应的享元对象
public static Flyweight getFlyweight(String intrinsic) {
Flyweight flyweight = null;
//池中是否有该对象,如果有
if (pool.containsKey(intrinsic)) {
//从池中获取
flyweight = pool.get(intrinsic);
System.out.print("已有 " + intrinsic + " 直接从池中取---->");
} else {
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight(intrinsic);
//放入池中
pool.put(intrinsic, flyweight);
System.out.print("创建 " + intrinsic + " 并从池中取出---->");
}
//返回享元角色
return flyweight;
}
}
单纯享元模式
public class FlyweightPattern {
public static void main(String[] args) {
//获取内部状态X的具体享元
Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
flyweightX.operate();
//获取内部状态Y的具体享元
Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
flyweightY.operate();
//获取内部状态Z的具体享元
Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z");
flyweightZ.operate();
//获取内部状态X的具体享元
Flyweight flyweightReX = FlyweightFactory.getFlyweight("X");
flyweightReX.operate();
}
}
abstract class Flyweight {
public String intrinsic;
public Flyweight(String intrinsic) {
this.intrinsic = intrinsic;
}
//享元角色无须接受外部状态
public abstract void operate();
}
class ConcreteFlyweight extends Flyweight {
public ConcreteFlyweight(String intrinsic) {
super(intrinsic);
}
@Override
public void operate( ) {
System.out.println("内部操作" + intrinsic);
}
}
//享元工厂角色
class FlyweightFactory {
//定义一个池容器
private static HashMap<String, Flyweight> pool = new HashMap<>();
//静态方法,用来根据条件返回对应的享元对象
public static Flyweight getFlyweight(String intrinsic) {
Flyweight flyweight = null;
//池中是否有该对象,如果有
if (pool.containsKey(intrinsic)) {
//从池中获取
flyweight = pool.get(intrinsic);
System.out.print("已有 " + intrinsic + " 直接从池中取---->");
} else {
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight(intrinsic);
//放入池中
pool.put(intrinsic, flyweight);
System.out.print("创建 " + intrinsic + " 并从池中取出---->");
}
//返回享元角色
return flyweight;
}
}
类似于懒汉式的枚举,在使用的时候创建对应的对象。
应用实例
- 类
Integer
- 连接池技术
- 本质上
Redis
也是一种享元模式
简介
享元模式(Flyweight Pattern)又称轻量级模式属于结构型模式。主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于,它提供了减少对象数量从而改善应用所需的对象结构的方式。
考虑系统性能的设计模式,通过使用享元模式可以节约内存空间,提高系统的性能。
模式分类:
- 单纯享元模式(即该对象没有外部状态,整个对象可以共享,类似于单例模式)
- 复合享元模式(该对象有内蕴状态,也有外蕴状态,内蕴状态共享)
UML
角色
- 享元工厂(FlyweightFactory)角色:用于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。
- 抽象享元(Flyweight)角色:定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.
- 具体享元(ConcreteFlyweight)角色:实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。
- 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中**(复合享元模式)**。
前提解释:
享元模式的本质: 分离变与不变,并将不变共享
- 内部状态(Intrinsic State):在享元模式中可以共享的相同内容。
- 外部状态(Extrinsic State):那些需要外部环境来设置的不能共享的内容。
- 其中外部状态和内部状态是相互独立的。
- 内部状态在各个对象间共享
- 外部状态由客户端传入。
- 外部状态的变化不会引起内部状态的变化。
意图:
在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为 细粒度对象
- 运用共享技术支持大量细粒度的对象的复用。
主要解决:
- 在创建出大量相同或相似对象实例时,有可能造成过多的资源消耗。
何时使用:
- 系统中有大量对象。
- 这些对象消耗大量内存。
- 这些对象的状态大部分可以外部化。
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
- 系统不依赖于这些对象身份,这些对象是不可分辨的。
如何解决:
- 享元工厂(FlyweightFactory)是享元模式的核心,它需要确保系统可以共享相同的对象。
- 它会维护一个对象列表,当我们想要获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类
- 若没有被创建,则创建一个新的享元对象,并将它加入到维护队列中。
应用实例:
- JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
- 数据库的数据池。
优点:
- 降低系统的内存,使效率提高。
缺点:
- 提高了设计难度,需要在设计阶段便进行合理的配置。
使用场景:
- 系统有大量相似对象。
- 需要缓冲池的场景。
注意事项:
1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。
JDK使用分析
类Integer
JDK中的Integer
中使用了该模式
Integer部分UML图
角色对应
- **享元工厂(FlyweightFactory)角色:**方法
Integer.valueOf(int i)
与类IntegerCache
结合。 - **抽象享元(Flyweight)角色:**无,该工厂直接对应了具体享元。
- **具体享元(ConcreteFlyweight)角色:**包含不同值的
Integer
实例。
代码分析
public final class Integer extends Number implements Comparable<Integer> {
/**
* 与缓存类结合扮演了工厂角色,此处为获得实例
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
* 返回一个整数实例,该实例表示指定的 int 值。如果不需要新的 Integer 实例,
* 则此方法通常应优先于构造函数 Integer(int)使用,因为此方法可能通过缓存
* 经常请求的值来产生明显更好的空间和时间性能。此方法将始终缓存 -128
* 到 127(包括 )范围内的值,并可能缓存此范围之外的其他值。
*/
public static Integer valueOf(int i) {
//判断是否存在于缓存中的值(-128~127)
if (i >= IntegerCache.low && i <= IntegerCache.high)
//从缓存中获取
return IntegerCache.cache[i + (-IntegerCache.low)];
//新建实例
return new Integer(i);
}
/**
* 与方法valueOf(int i)结合扮演了工厂角色,此处为存储实例
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
//静态代码块加载最大值到最小值的缓存
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
}