享元模式(Flyweight Pattern)
定义
享元模式通过共享技术来支持大量细粒度的对象,以减少内存中的对象数量。其核心思想是将对象的状态分为内部状态和外部状态,内部状态是不变的,可以被多个对象共享;外部状态是随环境改变而改变的,不能共享,必须由客户端保持。
属于结构型模式。
适用场景
- 系统有大量相似对象:这些对象除了几个参数外基本相同,可以通过共享来减少内存消耗。
- 需要缓冲池的场景:如数据库连接池、线程池等,通过共享对象池中的对象来提高资源利用率。
- 对象创建成本较高:创建对象的成本较高时,使用享元模式可以减少对象的创建数量,降低成本。
标准示例
Flyweight
抽象的享元角色
这是所有具体享元类的超类,为这些类规定出需要实现的公共接口。通常包含一些与内部状态相关的操作,这些内部状态是可以在多个享元对象之间共享的。
/**
* 抽象享元角色
*/
public interface IFlyweight {
/**
* 业务方法
* @param extrinsicState 外在状态
*/
void operation(String extrinsicState);
}
ConcreteFlyweight
具体的享元角色
实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享的。
/**
* 具体享元角色
*/
public class ConcreteFlyweight implements IFlyweight{
//内在状态,内在状态不会改变
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState){
this.intrinsicState = intrinsicState;
}
/**
* 业务方法
* @param extrinsicState 外在状态
*/
public void operation(String extrinsicState) {
System.out.println("Object address:" + System.identityHashCode(this));
System.out.println("IntrinsicState:" + this.intrinsicState);
System.out.println("ExtrinsicState:" + extrinsicState);
}
}
FlyweightFactory
享元工厂角色
负责创建和管理享元角色。当客户端请求一个享元对象时,享元工厂会检查系统中是否已经存在适当的享元对象,如果存在则直接返回该对象,否则创建一个新的享元对象。
通常通过哈希表(如HashMap)来存储和管理享元对象,以提高对象的检索和创建效率。
/**
* 享元工厂
*/
public class FlyweightFactory {
private static Map<String,IFlyweight> pool = new HashMap<String, IFlyweight>();
//内部状态作为缓存的key
public static IFlyweight getFlyweight(String intrinsicState){
if (!pool.containsKey(intrinsicState)){
IFlyweight flyweight = new ConcreteFlyweight(intrinsicState);
pool.put(intrinsicState,flyweight);
}
return pool.get(intrinsicState);
}
}
ClientTest
调用类:
public class ClientTest {
public static void main(String[] args) {
IFlyweight flyweight = FlyweightFactory.getFlyweight("黑桃A");
flyweight.operation("地主出的");
IFlyweight flyweight1 = FlyweightFactory.getFlyweight("黑桃A");
flyweight1.operation("地主出过的");
}
}
执行结果输出为:
Object address:1360875712
IntrinsicState:黑桃A
ExtrinsicState:地主出的
Object address:1360875712
IntrinsicState:黑桃A
ExtrinsicState:地主出过的
内部状态与外部状态:
享元模式的关键在于区分内部状态和外部状态。
内部状态是可以在多个对象中共享的,而外部状态是随环境改变而改变的,不能共享。
举一个下五子棋的例子。
棋子的颜色为内部状态:黑和白;
下棋坐标位置为外部状态:Point(x,y)
假设一局五子棋,黑白双方各落子30枚,如果不用享元,就要new 60个对象。
如果采用享元,只需要2个对象即可。
请看如下设计:
ChessPiece
棋子,作为抽象享元角色,包含了一个落子的方法 placingPiece(Point p)
/**
* 抽象棋子
*/
public interface ChessPiece {
/**
* 落子
* @param point 落子坐标
*/
void placingPiece(Point point);
}
WhitePiece
白色棋子,是具体享元角色。
/**
* 白色棋子
*/
public class WhitePiece implements ChessPiece{
private String color = "white";
public void placingPiece(Point point) {
System.out.println( color +" piece named "+System.identityHashCode(this) + ", placing on "+point.toString());
}
}
BlackPiece
黑色棋子,是具体享元角色。
/**
* 黑色棋子
*/
public class BlackPiece implements ChessPiece{
private String color = "black";
public void placingPiece(Point point) {
System.out.println( color +"piece named "+System.identityHashCode(this) + ", placing on "+point.toString());
}
}
Point
是非享元角色,即外部状态。
/**
* 棋盘坐标点
*/
@Data
public class Point {
private String x;
private String y;
public Point(String x,String y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "" +
"x='" + x + '\'' +
", y='" + y + '\'';
}
}
GobangFactory
五子棋工厂,用来提供棋子。
/**
* 五子棋工厂类
*/
public class GobangFactory {
private static Map<String,ChessPiece> pieces = new HashMap<String, ChessPiece>(2);
public static ChessPiece getPiece(String color){
if(!StringUtils.equalsAny(color,"black","white")){
throw new UnsupportedOperationException("拿错棋子了!");
}
//如果map中还没有棋子,那么添加进去。
if(!pieces.containsKey(color)){
//如果是白色,就把白子加进map
if("white".equals(color)){
ChessPiece piece =new WhitePiece();
pieces.put("white",piece);
}
//如果是黑色,就把黑子加进map
if("black".equals(color)){
ChessPiece piece =new BlackPiece();
pieces.put("black",piece);
}
}
//取子返回
return pieces.get(color);
}
}
ClientPlay
调用类:
public class ClientPlay {
public static void main(String[] args) {
ChessPiece piece1 = GobangFactory.getPiece("white");
piece1.placingPiece(new Point("10","20"));
ChessPiece piece2 = GobangFactory.getPiece("black");
piece2.placingPiece(new Point("10","30"));
ChessPiece piece3 = GobangFactory.getPiece("white");
piece3.placingPiece(new Point("20","40"));
ChessPiece piece4 = GobangFactory.getPiece("black");
piece4.placingPiece(new Point("20","50"));
}
}
执行结果输出为:
white piece named 1725154839, placing on x='10', y='20'
blackpiece named 1670675563, placing on x='10', y='30'
white piece named 1725154839, placing on x='20', y='40'
blackpiece named 1670675563, placing on x='20', y='50'
以上为享元模式全部内容,感谢阅读。