当前位置: 首页 > news >正文

【设计模式-3.3】结构型——享元模式

说明:说明:本文介绍设计模式中结构型设计模式中的,享元模式;

游戏地图

在一些闯关类的游戏,如超级玛丽、坦克大战里面,游戏的背景每一个关卡都不相同,但仔细观察可以发现,其都是用一些基础图标组成的,背景的变化实际上就是改变了其基础图标的位置。

如坦克大战,游戏背景实际上就是通过敌方坦克、我方坦克、砖墙、铁墙、海洋、草丛、基地等这些图标组成的。

坦克大战

基础图标,坦克、草地、河流……

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

现在,如果要我们来开发这样一个游戏地图。首先,我们会创建一个地图块类,如下:

(Tile,图块类)

/*** 图块类*/
public class Tile {/*** 图块名称*/private String image;/*** 图块的x坐标*/private int x;/*** 图块的y坐标*/private int y;/*** 创建图块对象*/public Tile(String image, int x, int y) {this.image = image;this.x = x;this.y = y;System.out.println("加载图片:" + image);}/*** 绘制图块*/public void draw() {System.out.println("绘制图片:" + image + ",坐标:" + x + "," + y);}
}

(Client,客户端,演示游戏地图创建过程)

/*** 客户端*/
public class Client {public static void main(String[] args) {// 在地图上绘制两个“海洋”图块new Tile("海洋", 1, 2).draw();new Tile("海洋", 2, 3).draw();// 在地图上绘制两个“草丛”图块new Tile("草丛", 1, 3).draw();new Tile("草丛", 2, 4).draw();// 在地图上绘制两“砖墙”图块new Tile("砖墙", 1, 4).draw();new Tile("砖墙", 2, 5).draw();}
}

运行程序,加载游戏地图

在这里插入图片描述

分析以上过程,会发现问题很大。每次创建一个图块对象,都需要new,加载时间特别长,且浪费资源。

为了解决这个问题,我们似乎可以考虑用原型设计模式解决。但是我们需要考虑,对于一个游戏地图来说,图块种类的数量可能是非常庞大的,使用原型模式对对象进行克隆,会有大量的内存开销,如果没有良好的内存回收机制,可能会导致内存溢出,造成系统崩溃。因此,使用原型模式来解决游戏地图的设计,不一定是合适的。

享元模式

享元,元指的是最小的单元,也就是上面坦克大战中草丛、海洋这些最小的图块,享元就是共享这些最小图块。通过享元模式对上面地图加载代码改进,如下:

(Drawable,绘画接口)

/*** 绘画接口*/
public interface Drawable {/*** 绘制图块*/void draw(int x, int y);
}

(Grass,草地图块)

/*** 草地图块*/
public class Grass implements Drawable {/*** 图块名称*/private String name;public Grass() {this.name = "草地";System.out.println("创建图块:" + name);}@Overridepublic void draw(int x, int y) {System.out.println("绘制【" + name + "】图块:" + name + ",位置:(" + x + ", " + y + ")");}
}

(Wall,墙壁图块)

/*** 墙壁图块*/
public class Wall implements Drawable {/*** 图块名称*/private String name;public Wall() {this.name = "墙壁";System.out.println("创建墙壁图块:" + name);}@Overridepublic void draw(int x, int y) {System.out.println("绘制【" + name + "】图块:" + name + ",位置:(" + x + ", " + y + ")");}
}

(River,河流图块)

/*** 河流图块*/
public class River implements Drawable {/*** 图块名称*/private String name;public River() {this.name = "河流";System.out.println("创建图块:" + name);}@Overridepublic void draw(int x, int y) {System.out.println("绘制【" + name + "】图块:" + name + ",位置:(" + x + ", " + y + ")");}
}

(Home,基地图块)

/*** 基地图块*/
public class Home implements Drawable {/*** 基地图块名称*/private String name;public Home() {this.name = "基地";System.out.println("创建图块:" + name);}@Overridepublic void draw(int x, int y) {System.out.println("绘制【" + name + "】图块:" + name + ",位置:(" + x + ", " + y + ")");}
}

(TileFactory,图块工厂)

import java.util.HashMap;
import java.util.Map;/*** 图块工厂*/
public class TileFactory {/*** 图块集合*/private Map<String, Drawable> Tiles;/*** 图块工厂构造函数*/public TileFactory() {Tiles = new HashMap<String, Drawable>();}/*** 根据图块名称获取图块,没有就创建并返回,有就直接从集合中获取并返回*/public Drawable getTile(String name) {// 没有就创建if (!Tiles.containsKey(name)) {switch (name) {case "河流":Tiles.put(name, new Wall());break;case "草地":Tiles.put(name, new Grass());break;case "基地":Tiles.put(name, new Home());break;case "墙壁":Tiles.put(name, new Wall());break;default:break;}}// 有就直接返回return Tiles.get(name);}
}

(Client,客户端,演示游戏地图加载过程)

/*** 客户端*/
public class Client {public static void main(String[] args) {// 创建图块工厂TileFactory tileFactory = new TileFactory();// 绘制地图tileFactory.getTile("草地").draw(0, 0);tileFactory.getTile("草地").draw(1, 0);tileFactory.getTile("草地").draw(2, 0);tileFactory.getTile("河流").draw(0, 1);tileFactory.getTile("河流").draw(1, 1);tileFactory.getTile("墙壁").draw(0, 2);tileFactory.getTile("墙壁").draw(1, 2);tileFactory.getTile("基地").draw(0, 3);}
}

可以发现,各个图块构造器内的输出语句只执行了一次,说明后续图块的创建没有调用其构造方法,实现了享元

在这里插入图片描述

以上就是通过享元模式对游戏地图设计的过程,实际上就是对最小单元对象的共享,使其不重复去创建对象。

需要注意区分于工厂设计模式,工厂模式是创建型设计模式,关注对象的创建,而享元模式是对已创建对象的一种利用,是结构型设计模式

总结

本文参考《设计模式的艺术》、《秒懂设计模式》两书

相关文章:

  • 面向企业的 ChatGPT 究极手册:第三章到第四章
  • WebGL中开发AR应用
  • mybatis----小细节
  • rust跟我学七:获取外网IP地址
  • 【话题】边缘计算的挑战和机遇
  • Windows 项目从0到1的部署
  • 软件工程:黑盒测试等价分类法相关知识和多实例分析
  • Unity | 渡鸦避难所-7 | 攻击碰撞检测
  • axios的原理及源码解析
  • 一篇综述洞悉医学大型语言模型的原理,应用和挑战
  • c语言:用一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。
  • 李沐《动手学深度学习》线性神经网络 softmax回归
  • python/c++ Leetcode题解——118. 杨辉三角
  • uniapp写微信小程序实现电子签名
  • latex如何修改论文标题为罗马数字【已解决】
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • ➹使用webpack配置多页面应用(MPA)
  • 0x05 Python数据分析,Anaconda八斩刀
  • canvas 五子棋游戏
  • ComponentOne 2017 V2版本正式发布
  • css布局,左右固定中间自适应实现
  • Flex布局到底解决了什么问题
  • GitUp, 你不可错过的秀外慧中的git工具
  • LeetCode29.两数相除 JavaScript
  • nodejs实现webservice问题总结
  • Python3爬取英雄联盟英雄皮肤大图
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • vue.js框架原理浅析
  • Vue2 SSR 的优化之旅
  • yii2权限控制rbac之rule详细讲解
  • 阿里研究院入选中国企业智库系统影响力榜
  • 从0到1:PostCSS 插件开发最佳实践
  • 基于Android乐音识别(2)
  • 基于HAProxy的高性能缓存服务器nuster
  • 浅谈web中前端模板引擎的使用
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 怎样选择前端框架
  • zabbix3.2监控linux磁盘IO
  • ​如何在iOS手机上查看应用日志
  • "无招胜有招"nbsp;史上最全的互…
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #etcd#安装时出错
  • #HarmonyOS:基础语法
  • (13)Hive调优——动态分区导致的小文件问题
  • (2.2w字)前端单元测试之Jest详解篇
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (ZT)一个美国文科博士的YardLife
  • (规划)24届春招和25届暑假实习路线准备规划
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • *上位机的定义