大鸟给小菜的问题:
现编写一个代码,模拟游戏里的某个场景:游戏角色有生命力、攻击力、防御力等属性,他在打Boss前进行存档(满状态),然后去打Boss(被打成瓜皮)。体验完被虐的感受后,再读取之前满状态时的档案,又开始愉快地玩耍了。
ps:主要是体现存档、读档如何进行,而不是什么打斗场景。这是设计模式笔记!!!
1、小菜经过短暂的思考,快速地写出来他的答案:
- 直接来一个游戏角色类,生命力、攻击力、防御力用类的成员变量体现;
- 再编写几个方法:1、状态显示;2、角色属性初始化;3、战斗后角色属性变化
- 至于存档与读档的操作通过客户端来表示。
(1)游戏角色类
public class GameRole {
private int vit; // 生命力
private int atk; // 攻击力
private int def; // 防御力
public void Vitality(int vit) { // 得到生命力的值
this.vit = vit;
}
public int Vitality() { // 修改生命力的值
return vit;
}
public void Attack(int atk) { // 得到攻击力的值
this.atk = atk;
}
public int Attack() { // 修改攻击力的值
return atk;
}
public void Defense(int def) { // 得到防御力的值
this.def = def;
}
public int Defense() { // 修改防御力的值
return def;
}
// 显示当前状态
public void StateDisplay() {
System.out.println("角色当前状态:");
System.out.println("体力:" + this.vit);
System.out.println("攻击力:" + this.atk);
System.out.println("防御力:" + this.def);
System.out.println("……");
}
public void GetInitState() { // 状态初始化
this.vit = 100;
this.atk = 100;
this.def = 100;
}
public void Fight() { // 战斗后属性改变
this.vit = 0;
this.atk = 0;
this.def = 0;
}
}
(2)客户端代码
public class Memento1Demo {
public static void main(String[] args) {
// 大战Boss前
GameRole lixiaoyao = new GameRole();
lixiaoyao.GetInitState(); // 满状态
lixiaoyao.StateDisplay(); // 显示当前状态
// 进程存档
GameRole backup = new GameRole();
backup.Vitality(lixiaoyao.Vitality());
backup.Attack(lixiaoyao.Attack());
backup.Defense(lixiaoyao.Defense());
lixiaoyao.Fight(); // 大战
lixiaoyao.StateDisplay(); // 显示大战后的状态
// 读档
lixiaoyao.Vitality(backup.Vitality());
lixiaoyao.Attack(backup.Attack());
lixiaoyao.Defense(backup.Defense());
lixiaoyao.StateDisplay();
}
}
(3)输出结果
角色当前状态:
体力:100
攻击力:100
防御力:100
……
角色当前状态:
体力:0
攻击力:0
防御力:0
……
角色当前状态:
体力:100
攻击力:100
防御力:100
……
代码分析:
从功能实现来说,提出的要求都完成了。存档了,读档了,没别的遗漏。
但是从代码内容的结构来说,这编写极其糟糕!
- 首先,对于存档和读档来说,确实是从客户端发出这个操作,但请主要,这只能是发出请求,而不是执行这个请求。真正是在内部进行该操作,而上述代码就是犯了这个错!——简单来说,就是一点也不封闭!!!
// 进程存档
GameRole backup = new GameRole();
backup.Vitality(lixiaoyao.Vitality());
backup.Attack(lixiaoyao.Attack());
backup.Defense(lixiaoyao.Defense());
// 读档
lixiaoyao.Vitality(backup.Vitality());
lixiaoyao.Attack(backup.Attack());
lixiaoyao.Defense(backup.Defense());
- 其次,一点也不开发放!!!
例如,现在还有一个经验值(experience)的属性参数要加进去,安排小菜给的这份代码来说,那是不是需要加上:
backup.Experience(lixiaoyao.Experience());
lixiaoyao.Experience(backup.Experience());
修改方向:
客户端的调用在这份代码里的比重太大了,这就导致了一有什么改动,客户端也要进行修改,但客户端本来就不是需要经常修改的地方。套用一下课本的原话:显然,我们希望的是把这些“游戏角色”的存取状态封装起来,而且最好是封装在外部的类当中。
还有一点需要明白,客户端是发出操作请求的地方,不是进行具体操作内容的地方。
二、经过对“备忘录模式”的学习理解,小菜给出了他修改后的代码:
(1)、还是先“游戏角色类”说起
老版本的那些内容都保留了下来:几个属性参数(生命力、攻击力、防御力)还有几个函数(初始化角色、显示角色状态、角色大战后参数改变);当然,那些get、set方法这里就省略了。
在这些基础上,区别是把在客户端里进行的存档、读档操作封装到类中,各自对应一个方法。
- 如何去表示档案?:
就是将这些参数数据再包装起来,又存储到一个“备忘录”类中。
public class GameRole {
private int vit; // 生命力
private int atk; // 攻击力
private int def; // 防御力
public RoleStateMemento SaveState() { // 创建档案
return new RoleStateMemento(vit, atk, def);
}
public void RecoveryState(RoleStateMemento memento) { // 读档
this.vit = memento.Vitality();
this.atk = memento.Attack();
this.def = memento.Defense();
}
public void StateDisplay() { // 角色当前状态
System.out.println("角色当前状态:");
System.out.println("体力:" + this.vit);
System.out.println("攻击力:" + this.atk);
System.out.println("防御力:" + this.def);
System.out.println("……");
}
public void GetInitState() { // 初始化
this.vit = 100;
this.atk = 100;
this.def = 100;
}
public void Fight() { // 战斗
this.vit = 0;
this.atk = 0;
this.def = 0;
}
}
(2)、备忘录类——角色状态存储箱类
发起人类(游戏角色类)里有啥成员变量,备忘录类(角色状态存储箱类)就有啥成员变量。
public class RoleStateMemento { // 角色状态存储箱类
private int vit; // 生命力
private int atk; // 攻击力
private int def; // 防御力
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
// 修改、获得各个状态属性
public void Vitality(int vit) {
this.vit = vit;
}
public int Vitality() {
return vit;
}
public void Attack(int atk) {
this.atk = atk;
}
public int Attack() {
return atk;
}
public void Defense(int def) {
this.def = def;
}
public int Defense() {
return def;
}
}
(3)管理者类——角色状态管理者类
- 认真看下备忘录类和备忘录的管理者类,有什么细节发现?
管理者类虽然是管理者,当只能是选择备忘录,而不是修改备忘录,准确是无法修改备忘录里的属性参数。
就是说只有备忘录类自己才能操作自身的那些参数(生命力、攻击力、防御力),而管理者类它负责的只能是决定存取哪个备忘录类。
public class RoleStateCaretaker { // 角色状态管理者类
private RoleStateMemento memento; // 档案
public RoleStateMemento getMemento() {
return memento;
}
public void setMemento(RoleStateMemento roleStateMemento) {
this.memento = roleStateMemento;
}
}
(4)、客户端代码
public class Memeto3Demo {
public static void main(String[] args) {
// 大战Boss类
GameRole lixiaoyao = new GameRole(); // 创建角色
lixiaoyao.GetInitState(); // 初始化
lixiaoyao.StateDisplay(); // 当前状态展示
// 保存进度
RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
stateAdmin.setMemento(lixiaoyao.SaveState());
lixiaoyao.Fight(); // 大战
lixiaoyao.StateDisplay(); // 当前状态展示
lixiaoyao.RecoveryState(stateAdmin.getMemento()); // 取档
lixiaoyao.StateDisplay(); // 当前状态展示
}
}
(5)、输出结果
角色当前状态:
体力:100
攻击力:100
防御力:100
……
角色当前状态:
体力:0
攻击力:0
防御力:0
……
角色当前状态:
体力:100
攻击力:100
防御力:100
……
最后的话:
备忘录模式的结构就三部分:
- 1、发起人(Originator)类:也可以理解成是你游戏里的角色,里面有很多参数(数据),有两个特殊方法:存档方法、读档方法。
- 2、备忘录(Memento)类:里面的成员变量就是发起人类里需要存储的内容。
- 3、管理者(Caretaker)类:存储备忘录类,无法操作备忘录类里的数据。
感谢读者能坚持看到这里的,如果可以的话希望帮忙点个小赞,虽然是学习笔记,但还是在线乞讨!!!真的十分感谢!