设计模式篇---享元模式

文章目录

概念

享元模式:运用共享技术有效地支持大量细粒度对象的复用。

当系统中出现大量相同或者相似的对象时,可以考虑使用享元模式。享元模式中提供了一个享元池用于存储已经创建好的对象。享元对象能做到共享的关键是区分了内部状态和外部状态。

内部状态:内部状态是存储在享元对象内部并且不会随环境改变而改变。

外部状态:外部状态是随环境改变而改变的、不可共享的状态。

举个例子,比如五子棋的大小形状就是内部状态 ,它不会随着环境而改变,而五子棋的位置就是外部状态

结构

享元,就是共享元素的意思。但是英文名字叫做FlyWeight,翻译成中文也不叫享元,直接翻译大概意思是轻量级的意思。

FlyWeight(抽象享元类) :声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部状态,也可以通过这些方法设置外部状态。
ConcreteFlyweight(具体享元类) :具体享元类实现了抽象享元类,在具体享元类中为内部状态提供了存储空间。
UnsharedConcreteFlyweight(非共享具体享元类) :不能共享的子类可设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
FlyweightFactory(享元工厂类):用于创建并管理享元对象,将各种类型的具体享元对象存储在一个享元池中。当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在),返回新创建的实例并将其存储在享元池中。

实例

现在要开发一个围棋软件。棋盘中有大量的黑子和白子,它们的形状、大小一样,只是位置不同。现在用享元模式来完成这个demo。

IgoChessman 类,抽象享元类。

java 复制代码
public abstract class IgoChessman {

    public abstract String getColor();

    public void display(){
        System.out.println("棋子颜色:" + this.getColor());
    }

}

黑色围棋类。

java 复制代码
public class BlackIgoChessman extends IgoChessman{
    @Override
    public String getColor() {
        return "黑色";
    }
}

黑色围棋类。

java 复制代码
public class WhiteIgoChessman extends IgoChessman {
    @Override
    public String getColor() {
        return "白色";
    }
}

围棋棋子工厂类。

java 复制代码
public class IgoChessmanFactory {
   //饿汉式单例类
    private static IgoChessmanFactory instance = new IgoChessmanFactory();
    private static Hashtable ht;

    private IgoChessmanFactory() {
        ht = new Hashtable();
        IgoChessman black, white;
        black = new BlackIgoChessman();
        ht.put("b", black);
        white = new WhiteIgoChessman();
        ht.put("w", white);
    }

    //返回唯一实例
    public static IgoChessmanFactory getInstance() {
        return instance;
    }

    //获取享元对象
    public static IgoChessman getIgoChessman(String color) {
        return (IgoChessman) ht.get(color);
    }
}

客户端

java 复制代码
public class Client {
    public static void main(String[] args) {
        IgoChessman black1,black2,black3,white1,white2;
        IgoChessmanFactory factory;
        //获取享元工厂对象
        factory = IgoChessmanFactory.getInstance();
        //通过享元工厂获取三颗黑子
        black1 = factory.getIgoChessman("b");
        black2 = factory.getIgoChessman("b");
        black3 = factory.getIgoChessman("b");
        System.out.println("判断两颗黑子是否相同:" + (black1==black2));
        //通过享元工厂获取两颗白子
        white1 = factory.getIgoChessman("w");
        white2 = factory.getIgoChessman("w");
        System.out.println("判断两颗白子是否相同:" + (white1==white2));
        //显示棋子
        black1.display();
        black2.display();
        black3.display();
        white1.display();
        white2.display();
    }

}

打印结果:

上面是没有外部状态的享元模式,如果有外部状态,如围棋需要加一个坐标类。

坐标类代码

java 复制代码
public class Coordinates {
    private int x;
    private int y;
    
    public Coordinates(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public Coordinates() {
    }
    
    public int getX() {
        return x;
    }
    
    public Coordinates setX(int x) {
        this.x = x;
        return this;
    }
    
    public int getY() {
        return y;
    }
    
    public Coordinates setY(int y) {
        this.y = y;
        return this;
    }
}

然后再修改一下IgoChessman,依赖坐标对象

java 复制代码
public abstract class IgoChessman {

    public abstract String getColor();

    public void display(Coordinates coordinates){
        System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coordinates.getX() + "," + coordinates.getY());
    }

}

客户端代码修改

java 复制代码
public class Client {
    public static void main(String[] args) {
        IgoChessman black1,black2,black3,white1,white2;
        IgoChessmanFactory factory;
        //获取享元工厂对象
        factory = IgoChessmanFactory.getInstance();
        //通过享元工厂获取三颗黑子
        black1 = factory.getIgoChessman("b");
        black2 = factory.getIgoChessman("b");
        black3 = factory.getIgoChessman("b");
        System.out.println("判断两颗黑子是否相同:" + (black1 == black2));
        //通过享元工厂获取两颗白子
        white1 = factory.getIgoChessman("w");
        white2 = factory.getIgoChessman("w");
        System.out.println("判断两颗白子是否相同:" + (white1 == white2));
        //显示棋子
        black1.display(new Coordinates(1,2));
        black2.display(new Coordinates(3,4));
        black3.display(new Coordinates(1,3));
        white1.display(new Coordinates(2,5));
        white2.display(new Coordinates(2,4));
    }

}

打印结果:

总结

当需要创建大量重复对象的时候,可以考虑使用享元模式。java中的String类,也是用了享元模式。当我们创建一个字符串,如果这个字符串对象第一次创建的话,就会放到享元池中,下次再创建内容相同的字符串对象,新创建的对象就会将它的引用指向第一次创建的对象,不会分配新的内存,以此来实现字符串的共享。

java 复制代码
    String str1 = "abcd";
        String str2 = "abcd";
        String str3 = "ab" + "cd";
        String str4 = "ab";
        str4 += "cd";


        System.out.println(str1 == str2);
        System.out.println(str1 == str3);
        System.out.println(str1 == str4);

打印结果:

第三个为false,是因为一开始str4是一个对象,和str1的初始值不同,所以这两个的地址是不一样的。

相关推荐
秋野酱几秒前
基于javaweb的SpringBoot高校图书馆座位预约系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端
举一个梨子zz26 分钟前
Java—— 可变参数、集合工具类、集合嵌套、不可变集合
java·开发语言·intellij-idea·需求分析
算法给的安全感27 分钟前
bfs-最小步数问题
java·算法·宽度优先
jstart千语44 分钟前
【消息队列】RabbitMQ基本认识
java·服务器·分布式·rabbitmq
泽020244 分钟前
C++类和对象之相关特性
java·开发语言·c++
唐僧洗头爱飘柔95271 小时前
【SSM-SpringMVC(二)】Spring接入Web环境!本篇开始研究SpringMVC的使用!SpringMVC数据响应和获取请求数据
java·spring·文件上传·页面跳转·数据响应·获取请求数据·静态资源访问
-曾牛1 小时前
Spring AI 集成 Mistral AI:构建高效多语言对话助手的实战指南
java·人工智能·后端·spring·microsoft·spring ai
在未来等你1 小时前
互联网大厂Java求职面试:电商商品推荐系统中的AI技术应用
java·缓存·kafka·推荐系统·向量数据库·jvm调优·spring ai
@ chen2 小时前
常见排序算法及其java实现
java·算法·排序算法
带刺的坐椅2 小时前
SpringBoot2 可以使用 SolonMCP 开发 MCP(江湖救急)
java·spring·ai·solon·mcp