XI.结构(享乐模式)
享元模式(Flyweight Pattern)
概念
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享大量细粒度对象来减少内存占用。享元模式将内外部状态分离,内部状态可以共享,外部状态由客户端独立维护,从而避免重复创建相同内容的对象。
应用场景
-
需要大量相似对象且内存消耗较大:当系统需要生成大量相似的对象,而这些对象中的大部分内容是相同或可以共享的,通过享元模式可以减少内存开销。例如,绘图程序中的文字对象、图形对象等。
-
对象的部分状态是可共享的:如果一个对象的某些部分是可变的(外部状态),而其他部分是不可变的(内部状态),那么可以使用享元模式,将内部状态共享,外部状态由客户端独立处理。例如,在棋类游戏中,棋子的颜色和形状是共享的,而位置则是独立的。
-
系统中创建了大量细粒度对象:当系统中需要创建大量的细粒度对象时,可以使用享元模式来减少对象的数量,从而节省资源。
注意点
- 区分内外部状态:内部状态是对象可以共享的部分,外部状态是依赖于具体场景并且不可共享的部分。外部状态在使用时会被传递给享元对象。
- 使用享元工厂管理享元对象:由于共享对象的核心是避免重复创建,工厂模式通常与享元模式配合使用,用来管理享元对象的创建和共享。
- 节省内存的代价是增加了系统的复杂性:享元模式虽然可以减少对象的数量,但增加了状态管理的复杂性,外部状态需要由客户端管理。
核心要素
- Flyweight(享元接口):定义享元对象接口,通常包含操作内部状态的方法。
- ConcreteFlyweight(具体享元类):实现享元接口,封装可共享的内部状态。
- UnsharedConcreteFlyweight(非共享享元类):有时享元对象的某些实例不能共享,此时可以使用这个类表示非共享的对象。
- FlyweightFactory(享元工厂类):负责管理和创建享元对象,确保对象可以被共享。
Java代码完整示例
// 享元接口
interface Flyweight {
void operation(String externalState); // 接受外部状态
}
// 具体享元类,保存共享的内部状态
class ConcreteFlyweight implements Flyweight {
private String internalState; // 内部状态
public ConcreteFlyweight(String internalState) {
this.internalState = internalState;
}
@Override
public void operation(String externalState) {
System.out.println("内部状态: " + internalState + ", 外部状态: " + externalState);
}
}
// 享元工厂类,管理享元对象
class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String internalState) {
// 检查享元对象是否已经存在
if (!flyweights.containsKey(internalState)) {
flyweights.put(internalState, new ConcreteFlyweight(internalState));
System.out.println("创建享元对象,内部状态: " + internalState);
}
return flyweights.get(internalState);
}
public int getFlyweightCount() {
return flyweights.size();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
// 创建享元对象并共享内部状态
Flyweight flyweight1 = factory.getFlyweight("A");
flyweight1.operation("X");
Flyweight flyweight2 = factory.getFlyweight("B");
flyweight2.operation("Y");
Flyweight flyweight3 = factory.getFlyweight("A");
flyweight3.operation("Z");
System.out.println("享元对象的数量: " + factory.getFlyweightCount());
}
}
输出结果:
创建享元对象,内部状态: A
内部状态: A, 外部状态: X
创建享元对象,内部状态: B
内部状态: B, 外部状态: Y
内部状态: A, 外部状态: Z
享元对象的数量: 2
各种变形用法完整示例
-
非共享的享元对象
有时某些享元对象不能共享,需要为这些对象设计一个独立的类,这类对象称为“非共享享元对象”。例如,在某些系统中,特殊状态的对象不能被共享。代码示例:
// 非共享享元类 class UnsharedConcreteFlyweight implements Flyweight { private String allState; public UnsharedConcreteFlyweight(String allState) { this.allState = allState; } @Override public void operation(String externalState) { System.out.println("非共享对象状态: " + allState + ", 外部状态: " + externalState); } } public class ClientWithUnsharedFlyweight { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); Flyweight flyweight1 = factory.getFlyweight("A"); flyweight1.operation("X"); Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("Special"); unsharedFlyweight.operation("Y"); System.out.println("享元对象的数量: " + factory.getFlyweightCount()); } }
-
复合享元模式
复合享元模式是指将多个享元对象组合起来形成一个更大的享元对象,外部状态被管理成一个组合结构。这个组合结构可以递归地包含多个享元对象。代码示例:
// 复合享元类 class CompositeFlyweight implements Flyweight { private Map<String, Flyweight> flyweightMap = new HashMap<>(); public void add(String key, Flyweight flyweight) { flyweightMap.put(key, flyweight); } @Override public void operation(String externalState) { for (Map.Entry<String, Flyweight> entry : flyweightMap.entrySet()) { entry.getValue().operation(externalState); } } } public class ClientCompositeFlyweight { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); // 创建单个享元对象 Flyweight flyweightA = factory.getFlyweight("A"); Flyweight flyweightB = factory.getFlyweight("B"); // 创建复合享元对象 CompositeFlyweight compositeFlyweight = new CompositeFlyweight(); compositeFlyweight.add("A", flyweightA); compositeFlyweight.add("B", flyweightB); // 使用复合享元 compositeFlyweight.operation("CompositeState"); System.out.println("享元对象的数量: " + factory.getFlyweightCount()); } }
-
与工厂模式结合
享元模式通常与工厂模式结合使用,由工厂类负责管理和创建享元对象,确保对象可以被重复使用。工厂类通常通过缓存已创建的对象来避免重复创建。代码示例:
// 在前面的 FlyweightFactory 中已经使用了工厂模式
-
享元模式在Java标准库中的应用
在Java的标准库中,Integer.valueOf()
方法就是享元模式的一个实际应用。为了减少内存消耗,JDK对-128
到127
之间的整数进行了缓存。代码示例:
public class FlyweightInJava { public static void main(String[] args) { Integer int1 = Integer.valueOf(100); Integer int2 = Integer.valueOf(100); // 比较对象引用 System.out.println(int1 == int2); // true Integer int3 = Integer.valueOf(200); Integer int4 = Integer.valueOf(200); System.out.println(int3 == int4); // false } }
总结
享元模式是一种优化内存使用的有效方式,尤其适用于需要创建大量细粒度对象的系统。通过共享内部状态,享元模式可以减少重复对象的创建,从而降低内存开销。如果系统中对象数量巨大且状态重复较多,享元模式可以显著提升性能和内存使用效率。
推荐阅读
-
XI.结构(享乐模式)
-
Python Coding Series - Python 访问者模式:为对象结构添加新功能的艺术 - 1. 背景
-
最值得投资的服务O2O公司TOP10- http://www.chinaz.com/start/2014/1010/370056.shtml 创哥说:自2013年下半年开始,本地生活服务业里出现了众多O2O模式的创业公司。这是资本和舆论密切关注的热点,它们大多相信自己就是那个能改造本地生活服务业的人,资本和舆论差不多也这么想,所以它们创立没多久,就纷纷拿到了天使投资。现在大半年过去,它们要融A轮甚至B轮了。那么,哪些公司的发展前景更值得期待? 《创业家》跟踪了这波浪潮,并对其中的代表公司进行了深度采写。此外,《创业家》还通过微信群和“重度垂直-黑马O2O特训营”、线下沙龙等服务聚集起了中国最领先的O2O创业公司创始人,请加微信号korchagin,加群请注明姓名+公司名+职位,否则初审都不会过。 《创业家》采访了近10位投资人和大约30家从事本地生活服务的创业公司,试图找到每个细分行业里最优秀的早期公司。我们的评判标准与VC投早期项目时类似,主要看行业前景、团队结构、商业思路这三个维度。为了加强专业度和调查力度,我们还请投资人和业内公司作了互相评价。 有的人可能会想:我作为同行,明明三项指标都比上榜的公司好,为啥我榜上无名?因为很多概念,拆细了看才有价值。比如“行业前景”,创业者第一步切入的细分市场的规模与它所能延伸出的想象空间,其实是两回事。出行市场广阔无边,滴滴打车和易到用车虽然正在商务租车领域展开竞争,但它们最早切入的细分领域不同,所以发展节奏和今日的市值也就不同。又比如,二次创业者要比初次创业者有优势,因为阅历很值钱。但如果前者第一次创业时实现了财务*,那他二次创业时很可能饥饿感不够,这可能会抵销掉他的阅历优势。 NO.
-
Facade 外观模式简介与 C# 示例【结构型5】【设计模式来了_10】
-
Facade(外观模式) 结构型
-
九种设计模式之一:外观模式 (Facade, 结构型)
-
外观模式:结构型设计模式的一种
-
掌握23种设计模式:理解结构型模式的7大类别(适配器、装饰、代理、外观、桥接、组合和享元)
-
桥接模式:让结构变得更灵活
-
理解设计模式:初探适配器模式——结构型模式的一种应用