设计模式-享元模式在Java中的使用示例-围棋软件

news2025/1/11 7:39:40

场景

享元模式

简介

当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题。

例如在一个文本字符串中存在很多重复的字符,如果每一个字符都用一个单独的对象来表示,将会占用

较多的内存空间,那么我们如何去避免系统中出现大量相同或相似的对象,同时又不影响客户端程序。

通过面向对象的方式对这些对象进行操作?

享元模式正为解决这一类问题而诞生。享元模式通过共享技术实现相同或相似对象的重用,

在逻辑上每一个出现的字符都有一个对象与之对应,然而在物理上它们却共享同一个享元对象,

这个对象可以出现在一个字符串的不同地方,相同的字符对象都指向同一个实例,在享元模式中,

存储这些共享实例对象的地方称为享元池(Flyweight Pool)。我们可以针对每一个不同的字符创建一个享元对象,

将其放在享元池中,需要时再从享元池取出。

享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部

状态(Intrinsic State)和外部状态(Extrinsic State)。

下面将对享元的内部状态和外部状态进行简单的介绍:

(1) 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。如字符的内容,

不会随外部环境的变化而变化,无论在任何环境下字符“a”始终是“a”,都不会变成“b”。

(2) 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,

并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态

之间是相互独立的。如字符的颜色,可以在不同的地方有不同的颜色,例如有的“a”是红色的,

有的“a”是绿色的,字符的大小也是如此,有的“a”是五号字,有的“a”是四号字。而且字符的颜色和大

小是两个独立的外部状态,它们可以独立变化,相互之间没有影响,客户端可以在使用时将外部状态

注入享元对象中。

正因为区分了内部状态和外部状态,我们可以将具有相同内部状态的对象存储在享元池中,享元池中

的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对

象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。

享元模式结构图

 

享元模式角色

Flyweight(抽象享元类):

通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),

同时也可以通过这些方法来设置外部数据(外部状态)。

ConcreteFlyweight(具体享元类):

它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,

为每一个具体享元类提供唯一的享元对象。

UnsharedConcreteFlyweight(非共享具体享元类):

并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;

当需要一个非共享具体享元类的对象时可以直接通过实例化创建。

FlyweightFactory(享元工厂类):

享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,

享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;

当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),

返回新创建的实例并将其存储在享元池中。

在享元模式中引入了享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,当用户需要对象时,

首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

应用实例

通过对围棋软件进行分析,发现在围棋棋盘中包含大量的黑子和白子,它们的形状、大小都一模一样,只是出现的位置不同而已。

如果将每一个棋子都作为一个独立的对象存储在内存中,将导致该围棋软件在运行时所需内存空间较大,

如何降低运行代价、提高系统性能是一个问题。为了解决这个问题,决定使用享元模式来设计该围棋软件的棋子对象。

IgoChessman充当抽象享元类,BlackIgoChessman和WhiteIgoChessman充当具体享元类,IgoChessmanFactory充当享元工厂类。

注:

博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主

实现

1、新建围棋棋子类作为抽象享元类

//围棋棋子类:抽象享元类
abstract class IgoChessman {
    public abstract String getColor();
    public void display(Coordinates coordinates){
        System.out.println("棋子颜色:"+this.getColor()+",棋子位置:"+coordinates.getX()+coordinates.getY());
    }
}

这里棋子的位置作为外部状态。

附坐标位置类

//坐标类:外部状态类
public class Coordinates {
    private int x;
    private int y;

    public Coordinates(int x, int y) {
        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;
    }
}

2、新建黑色棋子类作为具体享元类

//黑色旗子类:具体享元类
public class BlackIgoChessman extends IgoChessman{

    @Override
    public String getColor() {
        return "黑色";
    }
}

3、新建白色棋子类作为具体享元类

//白色旗子类:具体享元类
public class WhiteIgoChessman extends IgoChessman{

    @Override
    public String getColor() {
        return "白色";
    }
}

4、新建围棋棋子工厂类作为享元工厂类,使用单例模式进行设计

//围棋棋子工厂类:享元工厂类,使用单例模式进行设计
public class IgoChessmanFactory {
    private static IgoChessmanFactory instance = new IgoChessmanFactory();
    //使用Hashtable来存储享元对象,充当享元池
    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;
    }

    //通过key来获取存储在Hashtable中的享元对象
    public static IgoChessman getIgoChessman(String color){
        return (IgoChessman) ht.get(color);
    }
}

5、客户端调用示例

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,1));
        black2.display(new Coordinates(2,1));
        black3.display(new Coordinates(3,2));
        white1.display(new Coordinates(4,3));
        white2.display(new Coordinates(5,2));
    }
}

6、总结

当系统中存在大量相同或者相似的对象时,享元模式是一种较好的解决方案,它通过共享技术实现相

同或相似的细粒度对象的复用,从而节约了内存空间,提高了系统性能。相比其他结构型设计模式,

享元模式的使用频率并不算太高,但是作为一种以“节约内存,提高性能”为出发点的设计模式,它在

软件开发中还是得到了一定程度的应用。

享元模式的主要优点如下:

(1) 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。

(2) 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

主要缺点

(1) 享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

(2) 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

适用场景

(1) 一个系统有大量相同或者相似的对象,造成内存的大量耗费。

(2) 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。

(3) 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,

因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/774196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络】网络编程套接字

目录 一、预备知识 1、网络通信理解 2、源IP地址和目的IP地址 3、端口号 二、网络字节序 三、socket编程接口 1、socket常见API 2、sockaddr结构 3、sockaddr结构体 3.1、sockaddr结构体 3.2、sockaddr_in结构体 四、简单的UDP网络程序 1、创建套接字接口 2、绑定…

C# OpenCvSharp 直方图均衡化 图像去雾

直方图 直方图均衡化 自适应的直方图均衡化 全局直方图均衡化 局部直方图均衡化 对比度调整 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; us…

TRT3-trt-basic - 5.1 封装插件

之前TRTbasic4是新增插件,这次我们看看不新增插件,仅凭封装可不可以达到一样的功能 首先可以看到这次的g.op不再是MYSELU了,而是plugin,那为什么.cu还能识别出来呢? 是因为在这里做了一个通用的plugin DEFINE_BUILTIN…

常用API学习03(Java)

String char charAt(int index) 返回char指定索引处的值 char[] toCharArray() 将此字符串转换为新的字符数组 int compareTo(String anotherString) 按字典顺序比较两个字符串 boolean contains(CharSequence s) 当且仅当此字符串包含指定的char值序列才返…

osg osgQt模块 基于 QGLWidget 源码分析

osgQt基于QGLWidget实现了在Qt窗口内OSG渲染操作。Qt以其开源、跨平台、方便快捷、现代化的界面风格等优点,已经成为了目前桌面版CAD/CAE/CAM等软件开发的首选组件。因此,非常有必要在OSG的基础之上,研究Qt桌面系统内集成OSG渲染功能的相关技…

Linux查看机器内存空间

执行 fdisk命令查看磁盘空间 fdisk -l更多方法参考: Linux检查磁盘空间

一种工业机器人绝对精度的提升方法

摘要:一种新的校准方法,使用动作捕捉作为测量工具,利用ELM神经网络作为非几何误差源补偿,提升工业机器人的绝对精度。 工业机器人的绝对精度是评估其综合性能的重要指标之一。然而,由于机器人受到多种因素的影响&#…

概率论的学习和整理19:条件概率我知道,但什么是条件期望?---用来解决递归问题(草稿)

目录 1 目标问题: 什么是条件期望? 条件期望有什么用? 2 条件期望,全期望公式 3 条件期望,全期望公式 和 条件概率,全概率公式的区别和联系 3.1 公式如下 3.2 区别和联系 3.3 概率和随机过程 4 有什…

8年软件测试工程师感悟 —— 写给还在迷茫中的朋友

去年还在全网声讨互联网企业996呢,今年突然没声音了,也不用讨论在哪个路灯上吊死互联网资本家了,因为都被裁了。 继教育培训领域大幅度裁员之后,大厂裁员消息也开始陆续传出,百度AIG,MEG多条业务线进行精简&#xff0…

线程系列 3 - 关于 CompletableFuture

线程系列3-关于 CompletableFuture 1、从 Future 接口说起2、CompletableFuture 对 Future 的改进2.1、CompletionStage 接口类2.2、runAsync 和 supplyAsync 创建子任务2.3、 whenComplete 和 exceptionally 异步任务回调钩子2.4、调用 handle() 方法统一处理异常和结果2.5、异…

重温黑盒、白盒与灰盒测试方法

黑盒、白盒和灰盒测试方法是软件测试中常用的测试策略,用于评估系统的功能和质量。 对于黑盒、白盒与灰盒测试方法的理解,几年前我在某乎做过一个概念性的回答,当时提问者询问:如何跟非技术人员解释黑盒、白盒、灰盒测试的区别&a…

UG\NX二次开发 使用exception类,异常处理的方法

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介 异常处理是一种编程中常见的错误处理机制,它允许程序在遇到错误或异常情况时优雅地处理。在C++中,异常类是一种重要的异常处理工具。 当程序发生错误或异常时,我们可以使用excepti…

查看maven包依赖关系,一行命令搞定。

1:以logback-classic包为例,在命令端运行,下面命令 mvn dependency:tree -Dincludesch.qos.logback:logback-classic 2.会出现以下日志,就可以清楚的知道这个jar包,是在谁的下面。

python 批量下载图片(协程)

要下载的图片网站 1、总共多少页,得到每页的 url 列表 2、每页的图片详情的 ulr 列表(因为该高清大图在图片详情页,因此需要去图片详情页拿图片的url) ​​​​​​​ 3、进入图片详情页,获取到图片url 然后下载。 完整代码如下&…

玩转代码|详细盘点JavaScript 数据类型

目录 什么是JavaScript JavaScript 拥有动态类型 JavaScript 字符串 JavaScript 数字 JavaScript 布尔 JavaScript 数组 JavaScript 对象 Undefined 和 Null JS 中如何判断 undefined JS 中如何判断 null 声明变量类型 什么是JavaScript JavaScript(简称…

“nacos is starting with cluster”之nacos启动报错问题

下载并解压nacos后,通过点击startup.cmd启动nacos,出现nacos is starting with cluster的错误,导致nacos未能启动成功。 这是因为,通过startup.cmd命令启动nacos,默认是以集群的方式进行启动的,我们可以改…

为3.7亿用户提供优质服务的微众银行,如何保障应用安全、及时上线

微众银行成立于2014年,是国内首家数字银行。作为银行业改革创新的产物,开业八年多来,微众银行积极把握数字经济时代发展新机遇,运用科技手段为小微企业及普罗大众提供特色化、差异化的优质金融服务,在以数字普惠金融服…

地下供水管漏水监测-供水管道漏水监测设备

地下供水管道作为城市供水系统的重要组成部分,承载着为居民和企业提供清洁饮用水的重要使命。然而,由于管道老化、施工质量、外力损伤等因素,地下供水管道泄漏问题时有发生,这不仅造成了宝贵的水资源浪费,还会导致供水…

流程编排及可视化

写在前面 这里只介绍liteflow的简单基础使用以及作者对liteflow进行可视化扩展的相关阐述 一、背景及意义 背景:对于拥有复杂业务逻辑的系统承载着核心业务逻辑,这些核心业务逻辑涉及内部逻辑运算,缓存操作,持久化操作&#xf…

LiveQing视频点播RTMP推流直播功能-点播拉转在线资源拉转转推到鉴权直播间云端录像集中录像存储

LiveQing点播拉转在线资源拉转转推到鉴权直播间云端录像集中录像存储 1、基本功能2、拉转直播2.1、点播资源拉转2.2、在线资源拉转2.3、服务器本地文件拉转 3、拉转直播如何录像?4、RTMP推流视频直播和点播流媒体服务 1、基本功能 LiveQing RTMP直播点播流媒体服务…