一 基本介绍
- 享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量
细粒度
的对象 - 常用于系统底层开发,解决系统的
性
能问题。像数据库连接池
,里面都是创建好的连接对象,在这些连接对象 中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个 - 享元模式能够解决
重复对象的内存浪费
的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对 象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率 - 享元模式经典的应用场景就是
池技术
了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享 元模式是池技术的重要实现方式
二 uml类图
image.png2.1 角色说明
- FlyWeight 是抽象的享元角色, 他是产品的
抽象类
, 同时定义出对象的外部状态
和内部状态
(后面介绍) 的接口 或实现 - ConcreteFlyWeight 是具体的
享元角色
,是具体的产品类,实现抽象角色定义相关业务 - UnSharedConcreteFlyWeight 是
不可共享
的角色,一般不会出现在享元工厂
。(不需要太关心) - FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从
池中获取对象
方法
2.2 内部状态和外部状态
比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态
;而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化
的,所以棋子坐标就是棋子的外部状态
- 享元模式提出了
两个要求
:细粒度
和共享对象
。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分
:内部状态和外部状态 - 内部状态指对象
共享出来
的信息,存储在享元对象内部且不会随环境的改变而改变 -
外部状态
指对象得以依赖的一个标记,是随环境改变而改变的、不可共享
的状态。
三 围棋例子
image.png3.1 抽象享元类
public interface ChessFlyWeight {
String getColor();//获取颜色,内部状态
void display(Coordinate c);//展示位置,外部状态
}
3.2 UnSharedConcreteFlyweight 非共享享元类
外部状态,作为方法的参数使用
/**
* 外部状态:棋子坐标位置
*/
public class Coordinate {
private int x,y;
public Coordinate(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
3.3 ConcreteFlyWeight具体享元类
setcolor不要了,因为是内部状态
class ConcreteChess implements ChessFlyWeight{
private String color;
public ConcreteChess(String color) {
super();
this.color = color;
}
public String getColor() {
return color;
}
public void display(Coordinate c) {
System.out.println("棋子颜色:"+color);
System.out.println("棋子位置"+c.getX()+"-----"+c.getY());
}
}
3.4 享元工厂
public class ChessFlyWeightFactory {
//享元池
private static Map<String,ChessFlyWeight> map=new HashMap<String,ChessFlyWeight>();
public static ChessFlyWeight getChess(String color){
if(map.get(color)!=null){
return map.get(color);
}else{
ChessFlyWeight cfw=new ConcreteChess(color);
map.put(color, cfw);
return cfw;
}
}
}
3.5 客户端
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1=ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2=ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
System.out.println("-----增加外部状态的处理----");
chess1.display(new Coordinate(10,10));
chess2.display(new Coordinate(20,20));
}
}
//输出结果
flyweight.ConcreteChess@15db9742
flyweight.ConcreteChess@15db9742
-----增加外部状态----
棋子颜色:黑色
棋子位置10-----10
棋子颜色:黑色
棋子位置20-----20
四 注意事项和细节
- 在享元模式这样理解,“享”就表示共享,“元”表示对象
- 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元 模式
- 用
唯一标识码判断
,如果在内存中有,则返回这个唯一标识码所标识的对象
,用 HashMap/HashTable 存储 - 享元模式大大
减少了对象的创建
,降低了程序内存的占用,提高效率 - 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
- 使用享元模式时,
注意划分内部状态和外部状态
,并且需要有一个工厂类加以控制。 - 享元模式经典的应用场景是需要
缓冲池
的场景,比如String 常量池、数据库连接池
网友评论