news 2026/3/25 16:18:44

【魔法森林冒险】2/14 抽象层设计:Figure/Person类(所有角色的基石)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【魔法森林冒险】2/14 抽象层设计:Figure/Person类(所有角色的基石)

🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生
✨ 从来绝巘须孤往,万里同尘即玉京


文章目录

  • 【魔法森林冒险】2/14 抽象层设计:Figure/Person类(所有角色的基石)🏗️
    • 📝 文章摘要
    • 🧱 一、为什么需要抽象层?—— 从「重复代码」到「优雅复用」
    • 📜 二、核心代码拆解:Figure类(顶级角色抽象类)
      • 2.1 完整代码(核心片段)
      • 2.2 关键知识点讲解💡
    • 👤 三、核心代码拆解:Person类(可交互角色抽象类)
      • 3.1 完整代码(核心片段)
      • 3.2 关键知识点讲解💡
    • 🚫 四、新手使用抽象类的3个高频易错点
      • 易错点1:试图实例化抽象类
      • 易错点2:子类未实现所有抽象方法
      • 易错点3:忽略构造方法的调用顺序
    • 🌐 五、抽象层如何支撑所有角色的拓展?
      • 核心逻辑:
    • 📌 知识回顾
    • ✍️ 写在最后

【魔法森林冒险】2/14 抽象层设计:Figure/Person类(所有角色的基石)🏗️

📝 文章摘要

内容维度详情说明
核心摘要本文是「魔法森林冒险」Java项目系列第二篇,聚焦游戏的「角色抽象层」核心——Figure和Person类。带你吃透抽象类的设计逻辑:为什么要设计这两个顶层抽象类?通用属性/方法如何封装?Allen/Lia/老贤者等角色如何基于此扩展?同时拆解新手使用抽象类的高频坑点。
阅读时长10分钟
适合人群1. Java新手:想理解抽象类的实际应用场景;2. 面向对象学习者:想掌握「代码复用+拓展性」的设计思路;3. 项目复刻者:想搞懂角色体系的底层逻辑。
阅读重点1. Figure/Person类的核心属性与方法设计;2. 抽象方法的预留扩展逻辑;3. 新手使用抽象类的3个易错点;4. 抽象类如何支撑Allen/Lia等角色的拓展。

🧱 一、为什么需要抽象层?—— 从「重复代码」到「优雅复用」

在开发游戏角色时,你会发现:Allen、Lia、老贤者、甚至敌人,都有共同的属性/行为(比如都有生命值、都能判断是否存活、都有名称)。

如果为每个角色单独写这些逻辑,会出现大量重复代码❌:

  • Allen写一遍isAlive()(判断存活);
  • Lia再写一遍isAlive()
  • 老贤者还要写一遍…

而抽象类(abstract class)就是解决这个问题的「利器」✅:

抽象类 = 通用属性/方法(实现) + 抽象方法(只定义,子类实现)
作用:为子类提供「模板」,既复用通用逻辑,又强制子类实现专属功能。

本项目中,Figure是「所有角色的顶级抽象模板」,Person是「可交互角色的专属模板」,二者共同构成了所有角色的底层基石。

📜 二、核心代码拆解:Figure类(顶级角色抽象类)

先看你提供的Figure.java核心代码(未修改,仅拆分讲解):

2.1 完整代码(核心片段)

/** * 所有角色的顶级抽象类 * 定义所有角色的通用状态和基础行为 */publicabstractclassFigure{// 通用属性:角色名称privateStringname;// 通用属性:是否存活(所有角色都有这个状态)privatebooleanisAlive;// 通用属性:角色描述privateStringdescription;// 构造方法:初始化通用属性publicFigure(Stringname,Stringdescription){this.name=name;this.description=description;this.isAlive=true;// 初始默认存活}// 通用方法:判断是否存活(所有角色通用,直接实现)publicbooleanisAlive(){returnthis.isAlive;}// 通用方法:设置存活状态(比如战斗死亡后调用)publicvoidsetAlive(booleanalive){isAlive=alive;}// 抽象方法:角色的专属行为(强制子类实现)// 比如Allen的行为是「冒险」,Lia的行为是「辅助」,敌人的行为是「攻击」publicabstractvoidact();// Getter/Setter(封装属性,新手必学)publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetDescription(){returndescription;}publicvoidsetDescription(Stringdescription){this.description=description;}}

2.2 关键知识点讲解💡

  1. 通用属性设计

    • name/description:所有角色都有「名字」和「描述」,无需子类重复定义;
    • isAlive:所有角色都有「存活状态」,初始值true(出生即存活)。
  2. 通用方法设计

    • isAlive()/setAlive():判断/修改存活状态,所有角色逻辑完全一致,直接实现;
    • 这就是「代码复用」—— 子类继承Figure后,直接能用这些方法,不用自己写。
  3. 抽象方法act()

    • abstract修饰,只有方法签名,没有方法体;
    • 作用:强制所有子类必须实现「角色专属行为」(比如Allen的act()是选择冒险行动,Goblin的act()是选择攻击/逃跑);
    • 新手坑:如果子类不实现抽象方法,编译直接报错!

👤 三、核心代码拆解:Person类(可交互角色抽象类)

Person继承自Figure,是「玩家/盟友/NPC」的专属抽象类(敌人不继承Person,因为敌人的交互逻辑不同)。

3.1 完整代码(核心片段)

/** * 可交互角色抽象类(玩家/盟友/老贤者等) * 继承Figure,扩展可交互角色的专属属性/方法 */publicabstractclassPersonextendsFigure{// 专属属性:生命值(HP)privateinthp;// 专属属性:魔法值(MP)privateintmp;// 专属属性:金币(用于交易)privateintgold;// 专属属性:背包(存储道具,用数组实现)privateItem[]backpack;// 背包容量privatestaticfinalintBACKPACK_CAPACITY=10;// 构造方法:先调用父类Figure的构造,再初始化专属属性publicPerson(Stringname,Stringdescription,inthp,intmp,intgold){super(name,description);// 必须先调用父类构造this.hp=hp;this.mp=mp;this.gold=gold;this.backpack=newItem[BACKPACK_CAPACITY];// 初始化背包数组}// 通用方法:获取HP(封装,避免直接修改)publicintgetHp(){returnhp;}// 通用方法:设置HP(带校验,避免HP为负)publicvoidsetHp(inthp){if(hp<0){this.hp=0;// HP最低为0,不能为负this.setAlive(false);// HP为0则死亡}else{this.hp=hp;}}// 通用方法:获取MPpublicintgetMp(){returnmp;}// 通用方法:设置MP(带校验)publicvoidsetMp(intmp){this.mp=Math.max(mp,0);// 简化校验:MP最低为0}// 通用方法:拾取道具(背包逻辑,所有可交互角色通用)publicbooleanpickItem(Itemitem){// 遍历背包,找空位置for(inti=0;i<backpack.length;i++){if(backpack[i]==null){backpack[i]=item;System.out.println(this.getName()+"拾取了:"+item.getName());returntrue;}}// 背包满了System.out.println(this.getName()+"的背包已满,无法拾取"+item.getName());returnfalse;}// 抽象方法:使用道具(不同角色使用道具逻辑不同,强制子类实现)publicabstractbooleanuseItem(Itemitem);// 抽象方法:交易(Allen和老贤者交易,Lia不交易,逻辑不同)publicabstractbooleantrade(Persontarget,Itemitem,intgold);// 其他Getter/Setter省略(和Figure类逻辑一致)}

3.2 关键知识点讲解💡

  1. 继承的核心用法

    • extends Figure:Person继承Figure的所有非私有属性/方法(name/isAlive()等);
    • super(name, description):构造方法中必须先调用父类构造,这是Java继承的强制规则。
  2. 专属属性设计

    • hp/mp/gold:玩家/盟友/NPC的核心资源,敌人虽然也有HP,但因为交互逻辑不同,敌人继承Figure而非Person;
    • backpack:用数组实现背包(容量10),所有可交互角色都有背包,复用这个逻辑。
  3. 属性封装(重点)

    • 所有属性用private修饰,通过getter/setter访问;
    • setHp()带校验:HP不能为负,HP为0时自动设置为死亡(setAlive(false));
    • 新手坑:直接暴露属性(比如public int hp)会导致数据混乱,封装是面向对象的核心!
  4. 抽象方法的拓展性

    • useItem():Allen使用治疗药水是恢复HP,Lia使用魔法花是提升信任度,逻辑不同,所以定义为抽象方法;
    • trade():Allen可以和老贤者交易,Lia不能交易,强制子类按自身逻辑实现。

🚫 四、新手使用抽象类的3个高频易错点

结合本项目,总结新手最容易踩的坑:

易错点1:试图实例化抽象类

// 错误示例:抽象类不能new对象!Figurefigure=newFigure("测试角色","测试描述");Personperson=newPerson("Allen","魔法学徒",100,50,10);

✅ 正确做法:只能实例化「继承抽象类的子类」(比如Allen、Lia):

Allenallen=newAllen("艾伦","魔法学徒",100,50,10);

易错点2:子类未实现所有抽象方法

如果Allen类只实现了act(),没实现useItem(),编译报错:

Error: Allen is not abstract and does not override abstract method useItem(Item) in Person

✅ 正确做法:子类必须实现父类所有抽象方法(除非子类也是抽象类)。

易错点3:忽略构造方法的调用顺序

如果Person的构造方法没写super(name, description),编译报错:

Error: Constructor call must be the first statement in a constructor

✅ 正确做法:子类构造方法第一行必须调用父类构造(super()),如果父类无参构造,可省略;如果父类有参构造,必须显式调用。

🌐 五、抽象层如何支撑所有角色的拓展?

用一张图看懂Figure/Person的「承上启下」作用:

Figure(顶级抽象)

Person(可交互角色抽象)

Allen(玩家,实现act/useItem/trade)

Lia(盟友,实现act/useItem/trade)

OldSage(老贤者,实现act/useItem/trade)

Enemy(敌人抽象,实现act)

Goblin(哥布林)

TreeSpirit(树灵BOSS)

Natural(中立角色,实现act)

Animal(受伤动物)

核心逻辑:

  1. 向上复用:Allen/Lia/老贤者都复用Person的「HP/MP/背包/拾取道具」逻辑;
  2. 向下拓展:每个角色通过实现抽象方法,拥有专属行为(比如Allen的useItem()是用治疗药水,Lia的useItem()是用魔法花);
  3. 横向隔离:敌人(Enemy)和可交互角色(Person)都继承Figure,但各自拓展不同属性,逻辑不混淆。

📌 知识回顾

  1. Figure是所有角色的顶级抽象类,定义「存活状态/名称」等通用属性/方法,抽象方法act()强制子类实现专属行为;
  2. Person继承Figure,是可交互角色的抽象类,扩展「HP/MP/金币/背包」等专属属性,抽象方法useItem()/trade()支撑角色差异化;
  3. 抽象类的核心价值是「复用通用逻辑+强制子类实现专属逻辑」,避免重复代码;
  4. 新手使用抽象类的3个坑:实例化抽象类、未实现所有抽象方法、构造方法调用顺序错误。

✍️ 写在最后

Figure和Person类看似简单,却是整个项目「面向对象设计」的灵魂——正是因为有了这两层抽象,后续新增角色(比如矮人、精灵盟友)时,只需继承对应抽象类,实现专属方法,无需重复写通用逻辑。

下一篇我们会聚焦「Allen类(一):主角核心属性与初始化」,带你看作为玩家的Allen,如何基于Person抽象类,实现专属的属性设计和初始化逻辑💪。

如果你是新手,建议:

  1. 把Figure/Person的代码复制到IDE中,尝试写一个「测试子类」(比如TestPerson extends Person),实现所有抽象方法,理解继承和抽象的用法;
  2. 思考:Enemy类继承Figure后,应该扩展哪些专属属性?(比如攻击力、防御力)。

🔥 系列文章导航:

  1. 项目总览:设计与架构
  2. 抽象层设计:Figure/Person类(本文)
  3. Allen类(一):核心属性与初始化
    …(后续篇章持续更新)

💬 评论区互动:你觉得抽象类和普通类最大的区别是什么?在这个游戏项目中,还有哪些地方可以用抽象类优化?

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/22 15:01:07

AI工程师成长必看:避开这些坑,比努力更重要!建议收藏

“兄弟&#xff0c;我今年一定要转 AI 工程师。” 如果你也这么想过&#xff0c;那我得先泼一盆冷水。 你现在的状态&#xff0c;大概率是这样的&#xff1a; 要学的东西成千上万&#xff0c;路线图一张比一张长&#xff1b; 刷着推特、公众号、YouTube&#xff0c;看着别人一个…

作者头像 李华
网站建设 2026/3/22 13:46:48

【值得收藏】深度智能体构建全攻略:LangChain与LangGraph Python实战教程

人工智能智能体&#xff08;AI Agents&#xff09;——即结合大型语言模型&#xff08;LLMs&#xff09;与外部工具以执行任务的程序——已从学术概念演变为生产应用的核心组件。然而&#xff0c;简单的智能体往往难以应对需要规划、上下文管理与子任务协调的复杂多步骤任务。为…

作者头像 李华
网站建设 2026/3/22 13:46:46

Excel财务实战:零钞估算备用表制作全攻略

&#x1f4b0; 一、业务场景&#xff1a;工资零钞准备系统 1.1 实际问题 假设您是一名财务人员&#xff0c;每月需要为员工发放现金工资。银行取款时需要准确知道每种面额的钞票各需要多少张&#xff0c;以便快速清点并减少找零麻烦。 1.2 数据模板 &#x1f9ee; 二、核心公…

作者头像 李华
网站建设 2026/3/25 11:25:54

45672388

8672345

作者头像 李华
网站建设 2026/3/17 19:26:09

BISHI13 九倍平方数

问题分析 由于每个数字的修改规则是仅x<10时可改&#xff0c;并且只有2和3的修改会改变“各位和的模9值”&#xff0c;其他数字修改后模9值是不变的。 假设初始各位和为sum&#xff0c;模9得rest sum %9&#xff1b; 如果rest0&#xff0c;直接返回true&#xff1b; 否则&a…

作者头像 李华
网站建设 2026/3/14 1:58:02

C++错误 “tolower”: 不是“std”的成员

【1】原因分析std::tolower 是 C 标准库中用于将字符转换为小写形式的函数&#xff0c;定义在 <cctype> 头文件中【2】解决方法#include <cctype>或者将std::去掉

作者头像 李华