7享元模式
享元模式
享元模式(Flyweight Pattern) 是池技术的重要方式,可以降低大量的重复的、细粒度的类在内存中的开销。
1享元模式的定义
享元模式的英文原文是:
Use sharing to support large numbers of fine-grained objects efficiently.
意思是:使用共享对象可有效的支持大量细粒度的对象。
享元模式是以共享模式高效的支持大量的细粒度对象。享元对象能做到共享的关键是区分内部状态(Internal State) 和外部状态(External State)。
- 内部状态是存储在享元对象内部的、可以共享的信息,并且不会随环境改变而改变。
- 外部状态是随环境改变而改变且不可以共享的状态。享元对象的外部状态必须有客户端保存,并且在享元对象被创建之后,在需要使用的时候再传入到享元对象的内部。
享元模式有4个角色:
- 抽象享元(Flyweight )角色:该角色对享元类进行抽象,需要外部状态的操作可以通过参数的形式将外部状态传入。
- 具体享元(Concrete Flyweight)角色:该角色实现抽象享元定义的业务,注意享元对象可以在系统内共享。
- 享元工厂(FlyweightFactory)角色:构造一个池容器,并负责创建和管理享元角色,提供从池容器中获得对象的方法,保证享元对象可以被系统适当的共享。当一个客户端对象请求一个享元对象时,享元工厂角色会去检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂则提供这个已有的享元对象;否则创建一个合适的享元对象。
- 客户端(Client)角色:该角色需要自行存储所有享元对象的外部状态。
注意:除了上面的4个角色,享元模式还会涉及符合享元角色,该角色是将一些单纯享元使用合成模式加以复合,形成复合享元对象,这些复合享元对象本身不能共享,不会出现在享元工厂中,因此也称为“不可共享的享元角色”。但可以降负荷享元对象分解成单纯的享元对象,而后者则可以共享。
享元的类图
创建抽象享元角色
Flyweight.java
package com.eric.结构型模式.享元模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 抽象享元角色
* @CreateTime 2020-12-03 11:27:05
*/
public interface Flyweight {
//业务方法
public abstract void operation(String extrinsicState);
}
抽象享元角色声明一个业务方法operation(),该方法的参数书享元对象的外部状态。
具体享元ConcreteFlyweight实现抽象享元Flyweight接口。
创建具体享元
Concrete.java
package com.eric.结构型模式.享元模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 具体享元
* @CreateTime 2020-12-03 11:29:24
*/
public class ConcreteFlyweight implements Flyweight{
private String intrinsicState; //内部状态
ConcreteFlyweight(String intrinsicState){
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("内部状态"+intrinsicState+"---外部状态"+extrinsicState);
}
}
具体享元类定义了一个IntrnsicState属性,该属性用于村相互享元对象的内部状态,并通过构造函数中的参数对内部状态进行赋值。
享元工厂
FlyweightFactory.java
package com.eric.结构型模式.享元模式.引例;
import java.util.HashMap;
import java.util.Map;
/**
* @author Eric
* @ProjectName my_design_23
* @description 享元工厂
* @CreateTime 2020-12-03 11:33:03
*/
public class FlyweightFactory {
private static Map<String,Flyweight> pool = new HashMap<String, Flyweight>();
private FlyweightFactory(){}//私有构造方法
public static Flyweight getFlyweight(String intrinsicState){
Flyweight flyweight = pool.get(intrinsicState);
if(flyweight == null){
flyweight = new ConcreteFlyweight(intrinsicState);
pool.put(intrinsicState,flyweight);
}
return flyweight;
}
}
享元工厂使用一个静态的Map集合来存放享元对象,该集合是池容器。静态方法getFlyWeight()可以根据内部的状态值获取享元对象。因为内部状态不改变,所以可以作为主键,并根据气质从池容器中获取享元对象即可;如何池容器中无对应的享元对象,则创建一个新的享元对象并保存到池容器中。
2享元模式的应用
a.享元模式的优缺点
享元模式的优点在于答复减少内存中对象的数量,降低程序内存的占用,提高性能。但是,相应付出的代价也很高。
- 享元模式增加了系统的复杂性,需要分出外部状态和内部状态,而且内部状态具有固化特性,不应该随外部状态改变而改变,这使得程序的逻辑复杂化。
- 享元模式将享元对象的状态外部化,而读取外部状态使得运行时间变长。
b.享元模式的使用场景
使用享元模式的典型场景。
- 系统中有大量的相似对象,这些对象耗费大量内存。
- 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,几对象没有特定的身份。
Java基础类库中大量使用了享元模式,如String、Integer、Boolean、Character等类都通过享元模式提供了内部的池化机制。
3享元模式的实例
利用享元模式模拟下棋的过程。
定义棋子接口,规范落子方法put(),对棋子进行定位。
Chesspiece.java
package com.eric.结构型模式.享元模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 棋子接口规范
* @CreateTime 2020-12-03 13:17:38
*/
public interface Chesspiece {
//落子
void put(int x,int y);
}
ChesspieceFlyweight.java
package com.eric.结构型模式.享元模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 棋子具体享元
* @CreateTime 2020-12-03 13:48:48
*/
public class ChesspieceFlyweight implements Chesspiece {
private String color;
public ChesspieceFlyweight(String color){
this.color = color;
}
@Override
public void put(int x, int y) {
System.out.println("在("+x+","+y+")位置放了一个"+color+"子");
}
}
color属性是棋子的内部状态,构造函数带参数对color赋值,并实现落子put()方法;
棋子工厂
棋子工厂是享元工厂,负责创建和管理棋子。
ChesspieceFactory.java
package com.eric.结构型模式.享元模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 棋子工厂
* @CreateTime 2020-12-03 13:55:23
*/
public class ChesspieceFactory {
private static final Chesspiece WHITE = new ChesspieceFlyweight("白");
private static final Chesspiece BLACK = new ChesspieceFlyweight("黑");
public static Chesspiece getChesspiece(String color){
if("白".equals(color))return WHITE;
else if("黑".equals(color))return BLACK;
return null;
}
}
下棋时只需要黑白两种棋子,所以在棋子工厂ChesspieceFactory中直接创建黑白棋子对象,因此没有用Map集合来存放棋子对象。getChesspiece()方法根据颜色返回相应的棋子对象。
测试类
Game.java
package com.eric.结构型模式.享元模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 开始游戏
* @CreateTime 2020-12-03 14:00:09
*/
public class Game {
public static void main(String[] args) {
Chesspiece chesspiece1 = ChesspieceFactory.getChesspiece("黑");
chesspiece1.put(3,3);
Chesspiece chesspiece2 = ChesspieceFactory.getChesspiece("白");
chesspiece2.put(3,4);
Chesspiece chesspiece3= ChesspieceFactory.getChesspiece("黑");
chesspiece3.put(2,4);
Chesspiece chesspiece4 = ChesspieceFactory.getChesspiece("白");
chesspiece4.put(4,4);
}
}
测试结果
只要你不停下来,慢一点也没关系。