7享元模式

享元模式

享元模式(Flyweight Pattern) 是池技术的重要方式,可以降低大量的重复的、细粒度的类在内存中的开销。

1享元模式的定义

享元模式的英文原文是:
Use sharing to support large numbers of fine-grained objects efficiently.

意思是:使用共享对象可有效的支持大量细粒度的对象。

享元模式是以共享模式高效的支持大量的细粒度对象。享元对象能做到共享的关键是区分内部状态(Internal State) 和外部状态(External State)。
  • 内部状态是存储在享元对象内部的、可以共享的信息,并且不会随环境改变而改变。
  • 外部状态是随环境改变而改变且不可以共享的状态。享元对象的外部状态必须有客户端保存,并且在享元对象被创建之后,在需要使用的时候再传入到享元对象的内部。

享元模式有4个角色:
  • 抽象享元(Flyweight )角色:该角色对享元类进行抽象,需要外部状态的操作可以通过参数的形式将外部状态传入。
  • 具体享元(Concrete Flyweight)角色:该角色实现抽象享元定义的业务,注意享元对象可以在系统内共享。
  • 享元工厂(FlyweightFactory)角色:构造一个池容器,并负责创建和管理享元角色,提供从池容器中获得对象的方法,保证享元对象可以被系统适当的共享。当一个客户端对象请求一个享元对象时,享元工厂角色会去检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂则提供这个已有的享元对象;否则创建一个合适的享元对象。
  • 客户端(Client)角色:该角色需要自行存储所有享元对象的外部状态。

注意:除了上面的4个角色,享元模式还会涉及符合享元角色,该角色是将一些单纯享元使用合成模式加以复合,形成复合享元对象,这些复合享元对象本身不能共享,不会出现在享元工厂中,因此也称为“不可共享的享元角色”。但可以降负荷享元对象分解成单纯的享元对象,而后者则可以共享。

享元的类图
创建抽象享元角色
Flyweight.java
package com.eric.结构型模式.享元模式.引例;

/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 抽象享元角色
 * @CreateTime 2020-12-03 11:27:05
 */
public interface Flyweight {
    //业务方法
    public abstract void operation(String  extrinsicState);
}
抽象享元角色声明一个业务方法operation(),该方法的参数书享元对象的外部状态。
具体享元ConcreteFlyweight实现抽象享元Flyweight接口。

创建具体享元
Concrete.java
package com.eric.结构型模式.享元模式.引例;


/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 具体享元
 * @CreateTime 2020-12-03 11:29:24
 */
public class ConcreteFlyweight implements Flyweight{
    private String intrinsicState; //内部状态
    ConcreteFlyweight(String intrinsicState){
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(String extrinsicState) {
        System.out.println("内部状态"+intrinsicState+"---外部状态"+extrinsicState);
    }
}
具体享元类定义了一个IntrnsicState属性,该属性用于村相互享元对象的内部状态,并通过构造函数中的参数对内部状态进行赋值。

享元工厂
FlyweightFactory.java
package com.eric.结构型模式.享元模式.引例;

import java.util.HashMap;
import java.util.Map;
/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 享元工厂
 * @CreateTime 2020-12-03 11:33:03
 */
public class FlyweightFactory {
    private static Map<String,Flyweight> pool = new HashMap<String, Flyweight>();
    private FlyweightFactory(){}//私有构造方法
    public static Flyweight getFlyweight(String intrinsicState){
        Flyweight flyweight = pool.get(intrinsicState);
        if(flyweight == null){
            flyweight = new ConcreteFlyweight(intrinsicState);
            pool.put(intrinsicState,flyweight);
        }
        return flyweight;
    }
}
享元工厂使用一个静态的Map集合来存放享元对象,该集合是池容器。静态方法getFlyWeight()可以根据内部的状态值获取享元对象。因为内部状态不改变,所以可以作为主键,并根据气质从池容器中获取享元对象即可;如何池容器中无对应的享元对象,则创建一个新的享元对象并保存到池容器中。

2享元模式的应用

a.享元模式的优缺点
享元模式的优点在于答复减少内存中对象的数量,降低程序内存的占用,提高性能。但是,相应付出的代价也很高。
  • 享元模式增加了系统的复杂性,需要分出外部状态和内部状态,而且内部状态具有固化特性,不应该随外部状态改变而改变,这使得程序的逻辑复杂化。
  • 享元模式将享元对象的状态外部化,而读取外部状态使得运行时间变长。
b.享元模式的使用场景
使用享元模式的典型场景。
  • 系统中有大量的相似对象,这些对象耗费大量内存。
  • 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,几对象没有特定的身份。
Java基础类库中大量使用了享元模式,如String、Integer、Boolean、Character等类都通过享元模式提供了内部的池化机制。

3享元模式的实例

利用享元模式模拟下棋的过程。
定义棋子接口,规范落子方法put(),对棋子进行定位。
Chesspiece.java
package com.eric.结构型模式.享元模式.例1;

/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 棋子接口规范
 * @CreateTime 2020-12-03 13:17:38
 */
public interface Chesspiece  {
    //落子
    void put(int x,int y);
}
ChesspieceFlyweight.java
package com.eric.结构型模式.享元模式.例1;

/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 棋子具体享元
 * @CreateTime 2020-12-03 13:48:48
 */
public class ChesspieceFlyweight implements Chesspiece {
    private String color;

    public ChesspieceFlyweight(String color){
        this.color = color;
    }

    @Override
    public void put(int x, int y) {
        System.out.println("在("+x+","+y+")位置放了一个"+color+"子");
    }
}
color属性是棋子的内部状态,构造函数带参数对color赋值,并实现落子put()方法;
棋子工厂
棋子工厂是享元工厂,负责创建和管理棋子。
ChesspieceFactory.java
package com.eric.结构型模式.享元模式.例1;

/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 棋子工厂
 * @CreateTime 2020-12-03 13:55:23
 */
public class ChesspieceFactory {
   private static final Chesspiece WHITE = new ChesspieceFlyweight("白");
   private static final Chesspiece BLACK = new ChesspieceFlyweight("黑");

    public static Chesspiece getChesspiece(String color){
        if("白".equals(color))return WHITE;
        else if("黑".equals(color))return BLACK;
        return null;
    }
}
下棋时只需要黑白两种棋子,所以在棋子工厂ChesspieceFactory中直接创建黑白棋子对象,因此没有用Map集合来存放棋子对象。getChesspiece()方法根据颜色返回相应的棋子对象。
测试类
Game.java
package com.eric.结构型模式.享元模式.例1;

/**
 * @author Eric
 * @ProjectName my_design_23
 * @description 开始游戏
 * @CreateTime 2020-12-03 14:00:09
 */
public class Game {
    public static void main(String[] args) {
        Chesspiece chesspiece1 = ChesspieceFactory.getChesspiece("黑");
        chesspiece1.put(3,3);
        Chesspiece chesspiece2 = ChesspieceFactory.getChesspiece("白");
        chesspiece2.put(3,4);
        Chesspiece chesspiece3= ChesspieceFactory.getChesspiece("黑");
        chesspiece3.put(2,4);
        Chesspiece chesspiece4 = ChesspieceFactory.getChesspiece("白");
        chesspiece4.put(4,4);
    }
}
测试结果






posted @ 2020-12-29 08:58  喵酱张-Eric  阅读(74)  评论(0编辑  收藏  举报