享元模式(Flyweight Pattern)

发布于:2024-07-05 ⋅ 阅读:(20) ⋅ 点赞:(0)

享元模式(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'


以上为享元模式全部内容,感谢阅读。