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

【Java 设计模式】Bytecode 模式:使用自定义虚拟机解释指令

文章目录

  • 【Java设计模式】Bytecode模式:使用自定义虚拟机解释指令
    • 一、概述
    • 二、Bytecode设计模式的意图
    • 三、Bytecode模式的详细解释及实际示例
    • 四、Java中Bytecode模式的编程示例
    • 五、Java中何时使用Bytecode模式
    • 六、Java中Bytecode模式的实际应用
    • 七、Bytecode模式的优点和权衡

【Java设计模式】Bytecode模式:使用自定义虚拟机解释指令

一、概述

在Java开发中,Bytecode模式是一种强大的设计模式,它允许将行为编码为虚拟机的指令,在游戏开发和其他应用中具有重要作用。本文将详细介绍Bytecode模式的意图、解释、编程示例、适用场景以及实际应用。同时,还将提供示例代码的下载链接,方便读者进行学习和实践。

二、Bytecode设计模式的意图

Java中的Bytecode设计模式允许将行为编码为虚拟机的指令,使其成为游戏开发和其他应用中的强大工具。

三、Bytecode模式的详细解释及实际示例

  1. 实际示例
    • Bytecode设计模式的一个类似的现实世界示例可以在将一本书翻译成多种语言的过程中看到。与其直接将书从原始语言翻译成每一种其他语言,不如首先将书翻译成一种常见的中间语言,如世界语。这种中间版本更容易翻译,因为它更简单、更结构化。然后,每个目标语言的翻译人员从世界语翻译成他们的特定语言。这种方法确保了一致性,减少了错误,并简化了翻译过程,类似于字节码作为中间表示来优化和促进高级编程语言在不同平台上的执行。
  2. 通俗解释
    • Bytecode模式使行为由数据而不是代码驱动。
  3. gameprogrammingpatterns.com文档说明:
    • 指令集定义了可以执行的低级操作。一系列指令被编码为字节序列。虚拟机一次执行一个指令,使用栈来存储中间值。通过组合指令,可以定义复杂的高级行为。

四、Java中Bytecode模式的编程示例

在这个编程示例中,我们展示了Java中的Bytecode模式如何通过一组定义良好的操作来简化复杂虚拟机指令的执行。这个现实世界的示例展示了Java中的Bytecode设计模式如何通过允许通过字节码指令轻松调整巫师的行为来简化游戏编程。
一个团队正在开发一款新游戏,其中巫师相互战斗。巫师的行为需要通过游戏测试进行数百次的仔细调整和迭代。每次游戏设计师想要改变行为时都要求程序员进行更改是不理想的,因此巫师的行为被实现为数据驱动的虚拟机。
其中最重要的游戏对象之一是Wizard类。

@AllArgsConstructor
@Setter
@Getter
@Slf4j
public class Wizard {private int health;private int agility;private int wisdom;private int numberOfPlayedSounds;private int numberOfSpawnedParticles;public void playSound() {LOGGER.info("Playing sound");numberOfPlayedSounds++;}public void spawnParticles() {LOGGER.info("Spawning particles");numberOfSpawnedParticles++;}
}

接下来,我们展示了我们的虚拟机可用的指令。每个指令都有自己关于如何操作栈数据的语义。例如,ADD指令从栈中取出顶部的两个项目,将它们相加,并将结果推到栈上。

@AllArgsConstructor
@Getter
public enum Instruction {LITERAL(1),         // 例如:"LITERAL 0",将0推到栈上SET_HEALTH(2),      // 例如:"SET_HEALTH",弹出健康值和巫师编号,调用设置健康值SET_WISDOM(3),      // 例如:"SET_WISDOM",弹出智慧值和巫师编号,调用设置智慧值SET_AGILITY(4),     // 例如:"SET_AGILITY",弹出敏捷值和巫师编号,调用设置敏捷值PLAY_SOUND(5),      // 例如:"PLAY_SOUND",弹出值作为巫师编号,调用播放声音SPAWN_PARTICLES(6), // 例如:"SPAWN_PARTICLES",弹出值作为巫师编号,调用生成粒子GET_HEALTH(7),      // 例如:"GET_HEALTH",弹出值作为巫师编号,推巫师的健康值GET_AGILITY(8),     // 例如:"GET_AGILITY",弹出值作为巫师编号,推巫师的敏捷值GET_WISDOM(9),      // 例如:"GET_WISDOM",弹出值作为巫师编号,推巫师的智慧值ADD(10),            // 例如:"ADD",弹出2个值,推它们的和DIVIDE(11);         // 例如:"DIVIDE",弹出2个值,推它们的除法// 其他属性和方法...
}

我们示例的核心是VirtualMachine类。它接受指令作为输入并执行它们以提供游戏对象的行为。

@Getter
@Slf4j
public class VirtualMachine {private final Stack<Integer> stack = new Stack<>();private final Wizard[] wizards = new Wizard[2];public VirtualMachine() {wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),0, 0);wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),0, 0);}public VirtualMachine(Wizard wizard1, Wizard wizard2) {wizards[0] = wizard1;wizards[1] = wizard2;}public void execute(int[] bytecode) {for (var i = 0; i < bytecode.length; i++) {Instruction instruction = Instruction.getInstruction(bytecode[i]);switch (instruction) {case LITERAL:// 从字节码中读取下一个字节。int value = bytecode[++i];// 将下一个值推到栈上stack.push(value);break;case SET_AGILITY:var amount = stack.pop();var wizard = stack.pop();setAgility(wizard, amount);break;case SET_WISDOM:amount = stack.pop();wizard = stack.pop();setWisdom(wizard, amount);break;case SET_HEALTH:amount = stack.pop();wizard = stack.pop();setHealth(wizard, amount);break;case GET_HEALTH:wizard = stack.pop();stack.push(getHealth(wizard));break;case GET_AGILITY:wizard = stack.pop();stack.push(getAgility(wizard));break;case GET_WISDOM:wizard = stack.pop();stack.push(getWisdom(wizard));break;case ADD:var a = stack.pop();var b = stack.pop();stack.push(a + b);break;case DIVIDE:a = stack.pop();b = stack.pop();stack.push(b / a);break;case PLAY_SOUND:wizard = stack.pop();getWizards()[wizard].playSound();break;case SPAWN_PARTICLES:wizard = stack.pop();getWizards()[wizard].spawnParticles();break;default:throw new IllegalArgumentException("Invalid instruction value");}LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());}}public void setHealth(int wizard, int amount) {wizards[wizard].setHealth(amount);}// 其他属性和方法...
}

现在我们可以展示使用虚拟机的完整示例。

public static void main(String[] args) {var vm = new VirtualMachine(new Wizard(45, 7, 11, 0, 0),new Wizard(36, 18, 8, 0, 0));vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));vm.execute(InstructionConverterUtil.convertToByteCode(GET_AGILITY));vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));vm.execute(InstructionConverterUtil.convertToByteCode(GET_WISDOM));vm.execute(InstructionConverterUtil.convertToByteCode(ADD));vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_2));vm.execute(InstructionConverterUtil.convertToByteCode(DIVIDE));vm.execute(InstructionConverterUtil.convertToByteCode(ADD));vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
}

以下是控制台输出。

16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0]
16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18]
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2]
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9]
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54]
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains []

使用Java中的Bytecode设计模式可以显著增强基于虚拟机的应用程序的灵活性和可维护性。

五、Java中何时使用Bytecode模式

当你有很多需要定义的行为,并且你的游戏的实现语言不太适合,因为:

  1. 它太低级,使得编程变得繁琐或容易出错。
  2. 由于编译时间慢或其他工具问题,迭代它需要太长时间。
  3. 它有太多的信任。如果你想确保定义的行为不会破坏游戏,你需要将其与代码库的其他部分进行沙盒化。

六、Java中Bytecode模式的实际应用

  1. Java虚拟机(JVM)使用字节码允许Java程序在任何安装了JVM的设备上运行。
  2. Python将其脚本编译为字节码,然后由Python虚拟机解释。
    3…NET框架使用一种称为微软中间语言(MSIL)的字节码形式。

七、Bytecode模式的优点和权衡

  1. 优点
    • 可移植性:程序可以在任何具有兼容虚拟机的平台上运行。
    • 安全性:虚拟机可以对字节码执行安全检查。
    • 性能:即时编译器可以在运行时优化字节码,潜在地提高了性能,超过解释代码。
  2. 权衡
    • 开销:运行字节码通常比运行本机代码涉及更多的开销,可能会影响性能。
    • 复杂性:实现和维护虚拟机增加了系统的复杂性。

通过本文的介绍,相信大家对Java中的Bytecode模式有了更深入的了解。在实际开发中,合理运用该模式可以提高游戏开发的效率和灵活性,同时增强程序的可移植性和安全性。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • GAMES202——作业1 实时阴影(ShadowMap,PCF,PCSS)
  • windows C++-Lambda表达式(三)
  • 数学基础 -- 微积分之三角恒等式的积分
  • day57-graph theory-part07-8.28
  • 深度强化学习算法(四)(附带MATLAB程序)
  • 基于imx6ull平台opencv的图像采集和显示屏LCD显示功能(不带Qt界面)
  • CMake Error at CMakeLists.txt (find_package)幕后真凶
  • Linux之ip命令详解
  • Dockerfile+私有仓库
  • 创新互动体验RAG:利用角色化AI技术增强影视评论的沉浸感
  • [mysql]mysql的演示使用
  • linux下使用xargs批量操作
  • 数据结构与算法的代码实现(C++版)
  • 设计模式 代理模式(Proxy Pattern)
  • 一个简单的CRM客户信息管理系统,提供客户,线索,公海,联系人,跟进信息和数据统计功能(附源码)
  • [LeetCode] Wiggle Sort
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【知识碎片】第三方登录弹窗效果
  • Codepen 每日精选(2018-3-25)
  • C语言笔记(第一章:C语言编程)
  • EventListener原理
  • gf框架之分页模块(五) - 自定义分页
  • JDK9: 集成 Jshell 和 Maven 项目.
  • js作用域和this的理解
  • magento2项目上线注意事项
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Odoo domain写法及运用
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • Python学习之路13-记分
  • Redux 中间件分析
  • SpiderData 2019年2月25日 DApp数据排行榜
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 关于for循环的简单归纳
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 基于组件的设计工作流与界面抽象
  • 看域名解析域名安全对SEO的影响
  • 离散点最小(凸)包围边界查找
  • 利用jquery编写加法运算验证码
  • 如何胜任知名企业的商业数据分析师?
  • 入口文件开始,分析Vue源码实现
  • 详解NodeJs流之一
  • 用jQuery怎么做到前后端分离
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 终端用户监控:真实用户监控还是模拟监控?
  • k8s使用glusterfs实现动态持久化存储
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • ​2021半年盘点,不想你错过的重磅新书
  • ​ubuntu下安装kvm虚拟机
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #systemverilog# 之 event region 和 timeslot 仿真调度(十)高层次视角看仿真调度事件的发生
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决