享元模式(FlyWeight)
一、享元模式介绍
享元模式(FlyWeight):运用共享技术有效的支持大量细粒度对象的重用。
应用场景:如果项目中有很多完全相同或相似的对象,则可以使用享元模式,节省内存。
享元对象能做到共享的关键就是区分了内部状态和外部状态。
内部状态:可以共享,不会随环境变化而变化
外部状态:不可以共享,会随环境变化而变化
享元模式UML图
FlyweightFactory(享元工厂类):创建并管理享元对象,享元池一般设计成键值对
FlyWeight(抽象享元类):通常是一个接口或者抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
ConcreteFlyWeight(具体享元类):为内部状态提供成员变量进行存储
UnsharedConcreteFlyWeight(非共享享元类):不能被共享的子类可以设计为非共享享元类
例如:围棋中的享元模式,在围棋中,所有的白色或黑色的(形状、大小,颜色都一样)只是位置不同,那像这样的情况,可以使用享元模式。
把颜色、形状、大小给共享出来(内部状态)。位置不共享(外部状态)
二、享元模式代码实现
将棋子例子用代码实现
FlyWeight(抽象享元类):
1
2
3
4
5
6
7
8
9
10
11
|
/**
*
享元类接口:可以共享的有颜色,大小,形状
*
FlyWeight(抽象享元类):通常是一个接口或者抽象类,声明公共方法,
*
这些方法可以向外界提供对象的内部状态,设置外部状态。
*/
public interface ChessFlyWeight
{
//这里只设置一个颜色,大小和形状省略
void setColor(String
c);
String
getColor();
void display(Coordinate
c); //显示棋子
}
|
ConcreteFlyWeight(具体享元类):为内部状态提供成员变量进行存储
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//享元的具体实现:ConcreteFlyWeight(具体享元类):为内部状态提供成员变量进行存储
class concreteFlyWeight implements ChessFlyWeight{
private String
color; //这里就是为内部状态提供成员变量进行存储
//构造的时候初始化color属性
public concreteFlyWeight(String
color) {
super ();
this .color
= color;
}
@Override
public void setColor(String
c) {
this .color
= c;
}
@Override
public String
getColor() {
return color;
}
@Override
public void display(Coordinate
c) {
System.out.println( "棋子颜色:" +color);
System.out.println( "棋子位置:(" +c.getX()+ "," +c.getY()+ ")" );
}
}
|
UnsharedConcreteFlyWeight(非共享享元类):不能被共享的子类可以设计为非共享享元类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
*
坐标类(外部状态):棋子位置
*
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;
}
}
|
定义一个FlyweightFactory(享元工厂类):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/**
*
享元工厂
*
FlyweightFactory(享元工厂类):创建并管理享元对象,享元池一般设计成键值对
*/
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
chess = new concreteFlyWeight(color);
map.put(color,
chess);
return chess;
}
}
}
|
测试享元模式代码:
1
2
3
4
5
6
7
8
9
10
|
public static void main(String[]
args) {
ChessFlyWeight
chess1 = ChessFlyWeightFactory.getChess( "黑色" ); //黑1
ChessFlyWeight
chess2 = ChessFlyWeightFactory.getChess( "黑色" ); //黑2
System.out.println(chess1==chess2); //结果为true,相同或相似对象内存中只存在一份
//使用享元的外部状态
chess1.display( new Coordinate( 10 , 10 )); //黑1在10,10的位置
chess2.display( new Coordinate( 20 , 20 )); //黑2在20,20的位置
}
|
结果为:
true
棋子颜色:黑色
棋子位置:(10,10)
棋子颜色:黑色
棋子位置:(20,20)
三、享元模式应用场景
享元模式开发中应用场景:
比如线程池,数据库连接池,这些都利用享元模式共享了部分属性,在池中操作。
String类的设计也是享元模式
优点:
1.极大的减少内存中对象的数量
2.相同或相似对象内存中只存在一份,极大的节约资源,提高系统性能
3.外部状态相对独立,不影响内部状态
缺点:
1.模式较复杂,使程序逻辑复杂化
2.为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。