Java小游戏
一、需求
二、思路一
HP当然是怪物的一个属性成员,而武器是角色的一个属性成员,类型可以使字符串,用于描述目前角色所装备的武器。角色类有一个攻击方法,以被攻击怪物为参数,当实施一次攻击时,攻击方法被调用,而这个方法首先判断当前角色装备了什么武器,然后据此对被攻击怪物的HP进行操作,以产生不同效果
package com.homework.game;
public class Monster {
//定义怪物名称
public String monster_name;
//定义怪物血量
public int monster_hp;
//定义构造方法
// public Monster(){
// }
//定义有参数的构造方法,主函数中调用此方法可以传递怪物的名称和初始血量
public Monster(String name,int hp){
//给怪物名称和初始血量赋值
this.monster_name = name;
this.monster_hp = hp;
}
// //设置怪物的名称
// public void setMonsterName(String name){
// this.monster_name = name;
// }
//返回怪物名称
public String getMonsterName(){
return this.monster_name;
}
//设置怪物的血量
public void setMonsterHp(int hp){
this.monster_hp = hp;
}
//返回怪物血量
public int getMonsterHp(){
return this.monster_hp;
}
}
package com.homework.game;
import javax.sound.midi.SoundbankResource;
import java.util.Random;
public class Player {
//创建玩家名称变量
public String player_name;
//创建玩家武器变量
public String player_weapon;
//设置玩家名称方法
public void setPlayerName(String name){
this.player_name = name;
}
//获取玩家名称方法
public String getPlayerName(){
return this.player_name;
}
//设置玩家武器
public void setPlayWeapon(String weapon){
this.player_weapon = weapon;
}
//获取玩家武器
public String getPlayerWeapon(){
return this.player_weapon;
}
//玩家攻击方法
public void attackMethod(Monster monster){
// System.out.println(monster.getMonsterHp());
// System.out.println(this.player_weapon);
if (monster.getMonsterHp() > 0){
if("木剑".equals(this.player_weapon)){
int hp = monster.getMonsterHp();
hp = hp - 20;
monster.setMonsterHp(hp);
if (hp > 0){
System.out.println(this.player_name+"用"+this.player_weapon+"打了20HP,"+
monster.getMonsterName()+"还剩余"+monster.getMonsterHp()+"血量,可继续攻击");
}else {
System.out.println(monster.getMonsterName()+"被击杀!!!");
return;
}
}else if("铁剑".equals(this.player_weapon)){
if (monster.getMonsterHp() > 0){
int hp = monster.getMonsterHp();
hp = hp - 50;
monster.setMonsterHp(hp);
if (hp > 0){
System.out.println(this.player_name+"用"+this.player_weapon+"打了50HP,"+
monster.getMonsterName()+"还剩余"+monster.getMonsterHp()+"血量,可继续攻击");
}else {
System.out.println(monster.getMonsterName()+"被击杀!!!");
return;
}
}else {
System.out.println(monster.getMonsterName()+"死了,不需要攻击了!!!");
return;
}
}else if("魔剑".equals(this.player_weapon)){
if (monster.getMonsterHp() > 0){
int random = ((int)(Math.random()*10))+1;
int less_hp = (random > 5)?200:100;
if (less_hp == 200){
System.out.println("暴击了!!!!");
}
int hp = monster.getMonsterHp();
hp = hp - less_hp;
monster.setMonsterHp(hp);
if (hp > 0){
System.out.println(this.player_name+"用"+this.player_weapon+"打了"+less_hp+"HP"+
monster.getMonsterName()+"还剩余"+monster.getMonsterHp()+"血量,可继续攻击");
}else {
System.out.println(monster.getMonsterName()+"被击杀!!!");
return;
}
}
}else {
System.out.println("没有武器,无法攻击");
}
}else {
System.out.println(monster.getMonsterName()+"死了,无需攻击!!!");
}
}
}
package com.homework.game;
import java.util.Scanner;
public class TestMain {
public static void main(String[] args) {
System.out.println("------------------游戏开始------------------");
//创建怪物对象
Monster monster = new Monster("魔王",500);
//创建玩家对象
Player player = new Player();
//创建两个录入对象
Scanner player_name_scanner = new Scanner(System.in);
Scanner player_weapon_scanner = new Scanner(System.in);
//定义玩家名称变量
System.out.println("请输入玩家名称:");
String player_name = player_name_scanner.next();
while(true){
if(monster.getMonsterHp() > 0){
System.out.println("请输入武器准备(木剑、铁剑、魔剑):");
String player_weapon = player_weapon_scanner.next();
player.setPlayerName(player_name);
player.setPlayWeapon(player_weapon);
player.attackMethod(monster);
}else {
System.out.println("--------------------You Win----------------------");
break;
}
}
}
}
小结:
计的角色类的攻击方法很长,并且方法中有一个冗长的if…else结构,且每个分支的代码的业务逻辑很相似,只是很少的地方不同。
再者,上述设计思路,违反了O【Open】C【Close】P原则。在这个设计中,如果以后我们增加一个新的武器,如倚天剑,每次攻击损失500HP,那么,我们就要打开角色类,修改攻击方法。而我们的代码应该是对修改关闭的,当有新武器加入的时候,应该使用扩展完成,避免修改已有代码。
一般来说,当一个方法里面出现冗长的if…else或switch…case结构,且每个分支代码业务相似时,往往预示这里应该引入多态性来解决问题。而这里,如果把不同武器攻击看成一个策略,那么引入策略模式(Strategy Pattern)是明智的选择。
Tip:OCP原则,即开放关闭原则,指设计应该对扩展开放,对修改关闭。
Tip:策略模式,英文名Strategy Pattern,指定义算法族,分别封装起来,让他们之间可以相互替换,此模式使得算法的变化独立于客户。
三、思路二
因上述原因,我们重新设计,得到以下思路
package com.homework.game2;
//定义一个攻击接口,以怪物作为参数,各类武器实现这个攻击接口
public interface Attack {
public void attcak(Monsters monsters);
}
package com.homework.game2;
//武器----木剑
public class WoodenSword implements Attack {
@Override
public void attcak(Monsters monsters) {
//通知怪物失去对应血量
monsters.bloodLoss(20,"木剑");
}
}
package com.homework.game2;
//武器----铁剑类
public class IronSword implements Attack{
@Override
public void attcak(Monsters monsters) {
//通知怪物掉血50
monsters.bloodLoss(50,"铁剑");
}
}
package com.homework.game2;
import javax.sound.midi.SoundbankResource;
//武器-----魔剑类
public class MagicSword implements Attack{
@Override
public void attcak(Monsters monsters) {
int random = ((int)(Math.random()*10))+1;
if(random > 5){
System.out.println("魔剑暴击了,击杀200Hp!!!");
monsters.bloodLoss(200,"魔剑");
}else {
monsters.bloodLoss(100,"魔剑");
}
}
}
package com.homework.game2;
//怪物类
public class Monsters {
//定义怪物的名称
public String monsters_name;
//定义怪物的血量
public int monsters_hp;
// //构造方法,用来创建对象
// public Monsters(){}
// //定义怪物的构造方法,在主方法中传入参数,初始化怪物属性
public Monsters(String name,int hp){
this.monsters_name = name;
this.monsters_hp = hp;
}
// //获取怪物名称方法
// public String getMonstersName(){
// return this.monsters_name;
// }
// //设置怪物血量
// public void setMonstersHp(int hp){
// this.monsters_hp = hp;
// }
// //获取怪物血量
// public int getMonsterHp(){
// return this.monsters_hp;
// }
//怪物掉血的方法,参数 1.掉血量;2.使用的武器
Players players =new Players("王也");
public void bloodLoss(int hp,String weapon_name){
if(this.monsters_hp > 0){
this.monsters_hp = this.monsters_hp - hp;
if(this.monsters_hp > 0){
System.out.println(players.getPlayersName()+"用"+weapon_name+"攻击了"+this.monsters_name+hp+
"Hp,剩余"+this.monsters_hp+"Hp,可以继续攻击");
}else {
System.out.println(this.monsters_name+"死了,无需攻击");
}
}else {
System.out.println(this.monsters_name+"死了,无需攻击");
return;
}
}
}
package com.homework.game2;
//玩家类
public class Players {
//定义玩家名称
public String players_name;
//定义玩家武器
public Attack players_weapon;
//设置玩家构造方法,初始化玩家名称
public Players(String name){
this.players_name = name;
}
// //设置玩家名称方法
// public void setPlayersName(String name){
// this.players_name = name;
// }
//获取玩家名称方法
public String getPlayersName(){
return this.players_name;
}
//接口回调 Attack weapon 接口回调变量
//设置玩家武器方法,传入接口参数,将变量赋值给this.players_weapon
public void setPlayersWeapon(Attack weapon){
this.players_weapon = weapon;
}
// //获取玩家武器方法
// public Attack getPlayersWeapon(){
// return this.players_weapon;
// }
//玩家攻击方法
public void player_AttcakMethod(Monsters monsters){
this.players_weapon.attcak(monsters);
}
}
package com.homework.game2;
public class TestMain {
public static void main(String[] args) {
//创建怪物对象
Monsters monsters = new Monsters("大魔王",200);
//创建玩家对象
Players players = new Players("王也");
players.setPlayersWeapon(new WoodenSword());
players.player_AttcakMethod(monsters);
players.setPlayersWeapon(new IronSword());
players.player_AttcakMethod(monsters);
players.setPlayersWeapon(new MagicSword());
players.player_AttcakMethod(monsters);
}
}
改进后的代码有如下优点:
第一,虽然类的数量增加了,但是每个类中方法的代码都非常短,没有了以前攻击方法那种很长的方法,也没有了冗长的if…else,代码结构变得很清晰。
第二,类的职责更明确了。在第一个设计中,角色不但负责攻击,还负责给怪物减少HP和判断怪物是否已死。这明显不应该是角色的职责,改进后的代码将这两个职责移入怪物内,使得职责明确,提高了类的内聚性。
第三,引入Strategy[策略]模式后,不但消除了重复性代码,更重要的是,使得设计符合了OCP。如果以后要加一个新武器,只要新建一个类,实现武器接口,当角色需要装备这个新武器时,客户代码只要实例化一个新武器类,并赋给角色的武器成员就可以了,已有的角色和怪物代码都不用改动。这样就实现了对扩展开发,对修改关闭