背景
面向对象可以非常方便的解决一些扩展性的问题,但是在这个过程中系统务必会产生一些类或者对象,如果系统中存在对象的个数过多时,将会导致系统的性能下降。对于这样的问题解决最简单直接的办法就是减少系统中对象的个数。
享元模式提供了一种解决方案,使用共享技术实现相同或者相似对象的重用。也就是说实现相同或者相似对象的代码共享。
定义
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。 也就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。
在了解享元模式之前我们先要了解两个概念:内部状态、外部状态。
- 内部状态:在享元对象内部不随外界环境改变而改变的共享部分。
- 外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。
模式结构

抽象享元角色:
public abstract class FlyWeight {
//内部状态
private String innerState;
//外部状态
private String outerState;
public FlyWeight(String innerState) {
this.innerState = innerState;
}
public abstract void operate();
public String getOuterState() {
return outerState;
}
public void setOuterState(String outerState) {
this.outerState = outerState;
}
}
具体享元角色:
public class ConcreteFlyWeight1 extends FlyWeight {
public ConcreteFlyWeight1(String innerState) {
super(innerState);
}
@Override
public void operate() {
System.out.println("具体享元角色1");
}
}
享元工厂:
public class FlyWeightFactory {
private static Map<String, FlyWeight> pool = new HashMap<String, FlyWeight>();
public static FlyWeight getFlyweight(String innerState) {
if(pool.get(innerState) != null) {
return pool.get(innerState);
} else {
FlyWeight weight = new ConcreteFlyWeight1(innerState);
pool.put(innerState, weight);
return weight;
}
}
}
模式实现
场景:围棋的棋子, 如果用编程去实现的话,势必要new出很多实例,整盘棋结束时估计内存也快爆了。如果使用享元模式,把围棋的共性共性出来,一些外部状态则外部动态控制,那么这个效率才是最优的。比如共享的应该是这个棋子的形状、大小、图片,外部动态修改的应该是棋子的位置。那么,只要定义两个类,黑棋和白棋,而且工厂中或说整个系统中也就只维护两个对象,一个是黑棋一个是白棋。
外部状态UnSharedConcreteFlyWeight非共享享元类:
public class Coordinate {
private int x, y;
public Coordinate(int x, int y) {
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;
}
}
棋子享元类:
public interface ChessFlyWeight {
void setColor(String c);
String getColor();
void display(Coordinate coordinate);
}
具体享元类:
public class ConcreteChess implements ChessFlyWeight {
private String color;
public ConcreteChess(String color) {
super();
this.color = color;
}
@Override
public void setColor(String c) {
}
@Override
public String getColor() {
return null;
}
@Override
public void display(Coordinate c) {
System.out.println("棋子颜色:"+color);
System.out.println("棋子位置:"+c.getX()+"------"+c.getY());
}
}
工厂类:
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;
}
}
}
场景类:
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1 == chess2);
System.out.println("增加外部状态的处理============");
chess1.display(new Coordinate(10,10));
chess2.display(new Coordinate(20,20));
}
}
模式优缺点
优点
- 享元模式的优点在于它能够极大的减少系统中对象的个数。 -享元模式由于使用了外部状态,外部状态相对独立,不会影响到内部状态,所以享元模式使得享元对象能够在不同的环境被共享。
缺点
- 由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。
- 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
模式使用场景
- 如果一个系统中存在大量的相同或者相似的对象,由于这类对象的大量使用,会造成系统内存的耗费,可以使用享元模式来减少系统中对象的数量。
- 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
- 需要缓冲池的场景。
应用
- 在Java中,String类中用了享元模式。在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。
- 数据库连接池,线程池等即是用享元模式的应用。