OOP h05
某一款游戏,其主要角色如下:
Tank
坦克
Heavy Tank
重型坦克:初始生命值 200
,攻击力 20
Medium Tank
轻型坦克:初始生命值 100
,攻击力 10
War Factory
兵工厂:初始生命值 100
,无攻击力
Barrack
兵营:可以训练出步枪兵、 RPG 兵、军犬,初始生命值 100
,无攻击力
Rifle Soldier
步枪兵:初始生命值 50
(对战 军犬除外),攻击力 5
(对战军犬可以一次击毙军犬)
Rocket Soldier
火箭兵:初始生命值 50
(对战 军犬除外),攻击力 10
Dog
军犬:初始生命值 50
,攻击力 5
(对战人类时候一口毙命)
此外还要能通过 Soldier.getLivingSoldierCount
/ Soldier.getDeadedSoldierCount
统计现在有多少个活着的和死去的士兵数量
请遵循以上游戏规则,并根据如下测试代码(见文末)设计代码
作业批改的时候,除了如下调用的方法以外,不会增加新的调用方法,但是可能有不同的组合方式。例如让一个 Dog
反复攻击 HeavyTank
,判断 HeavyTank.getHealth()
任何对象有 getHealth()
方法,返回当前生命值,如果已经死亡则返回 <=0
的一个数字
2021.3.19 更新
- 文中定义的
Base
类可更名为 GameObject
抽象类。定义更改(和初始化)生命值的方法,其中参数为攻击力;定义死亡方法,修改生命值后调用(只需在 Soldier
类中重写);在攻击方法中先判断攻击力,再调用更改生命值的方法。
- 定义
Param
类,存放全部常量(生命值和攻击力)。
WarFactory
和 Barrack
继承 Building
类。
- 利用
instanceof
判断参数属于何种类。
0x00 分析
阅读需求,可以发现要实现的是一个“小游戏”,游戏中有许多角色,分为「工厂」和「兵营」两大类。工厂可以建造「坦克」,分为「重型坦克」和「轻型坦克」;兵营可以训练「士兵」和「军犬」,士兵分为「步枪兵」和「火箭兵」。每种角色都有「生命值」和「攻击力」。
综合以上分析,可以发现需要将上述角色抽象为「类」,而且存在明显的继承关系:
- 每种角色都有「生命值」和「攻击力」,即它们可以继承同一个父类,这个父类中有「生命值」和「攻击力」的属性;
- 坦克分为重坦和轻坦,即重坦和轻坦继承自坦克类,同理步枪兵和火箭兵也继承自士兵类。
综合以上分析,可以首先创建 Base
类作为所有类的父类,再依次创建 WarFactory
和 Barrack
、Tank
、Soldier
和 Dog
、HeavyTank
和 MediumTank
、RifleSoldier
和 RPGSoldier
。
0x01 Base
Base
作为下面所有类的父类,具有所有类共有的属性:「生命值」和「攻击力」。
类的初始化时需要指明其生命值和攻击力,所以 Base
的构造方法需要对其生命值和攻击力进行初始化,供子类继承。
阅读 Test
代码:
1 2
| mediumTank1.attack(rifleSoldier1);
|
可以发现需要具有 attack
方法,攻击其他对象。结合其他示例可以发现,需要攻击的对象有 Tank
、Soldier
和 Dog
,所以在 Base
中需要进行重载。
第一次写的时候没能正确理解 attack
方法的攻击范围,错误地认为坦克只能攻击士兵和军犬、士兵只能攻击坦克和军犬……
而且当时由于错误的理解,遂即把 attack
方法写在坦克、士兵和军犬类中,为了不直接修改生命值,又对应写了“被攻击”方法……
改正思路后,attack
方法统一写在基本类中,提高了内聚性,降低了耦合性。
结合说明,任何对象都需要有 getHealth()
方法,返回当前生命值;isDestroy()
方法判断是否被消灭。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.huawei.classroom.student.h05;
public class Base { protected int lifeValue; protected int attackPower;
public Base(int lv, int ap) { this.lifeValue = lv; this.attackPower = ap; }
public void attack(Soldier soldier) { if (soldier.lifeValue > 0) { soldier.lifeValue -= this.attackPower; if (soldier.lifeValue <= 0) Soldier.setDeadedSoldierCount(); } } public void attack(Tank tank) { if (tank.lifeValue > 0) tank.lifeValue -= this.attackPower; } public void attack (Dog dog) { if (dog.lifeValue > 0) dog.lifeValue -= this.attackPower; } public int getHealth() { return this.lifeValue; } public boolean isDestroyed() { 被消灭的标志就是生命值小于等于0 return this.lifeValue <= 0; } }
|
0x02 WarFactory
&Barrack
WarFactory
为工厂类,继承 Base
,且可以建造坦克。
阅读 Test
代码:
1 2
| Tank mediumTank1=(MediumTank)warFactory.building(EnumObjectType.mediumTank);
|
可以发现建造坦克方法 building
的参数为枚举类型 EnumObjectType
,则需要新建 EnumObjectType
:
1 2 3 4 5 6 7 8 9
| package com.huawei.classroom.student.h05;
public enum EnumObjectType { RPGSoldier, dog, rifleSoldier, mediumTank, heavyTank }
|
WarFactory
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.huawei.classroom.student.h05;
public class WarFactory extends Base{ public WarFactory() { super(100, 0); } public Tank building(EnumObjectType built) { if (built == EnumObjectType.mediumTank) return new MediumTank(); else if (built == EnumObjectType.heavyTank) return new HeavyTank(); else return null; } }
|
Barrack
和 WarFactory
类似,可以训练士兵和军犬,同样用到 EnumObjectType
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.huawei.classroom.student.h05;
public class Barrack extends Base{ public Barrack() { super(100, 0); }
public Base traing(EnumObjectType trained) { if (trained == EnumObjectType.rifleSoldier) return new RifleSoldier(); else if (trained == EnumObjectType.RPGSoldier) return new RPGSoldier(); else if (trained == EnumObjectType.dog) return new Dog(); else return null; } }
|
0x03 Tank
&Soldier
&Dog
Tank
类无需进行额外操作,只需继承 Base
的构造方法,供子类继承:
1 2 3 4 5 6 7 8 9 10 11
| package com.huawei.classroom.student.h05;
public class Tank extends Base{ public Tank(int lv, int ap) { super(lv, ap); }
}
|
Soldier
类需要记录生存和死亡的士兵数目,应定义为静态变量,定义 setter 和 getter 便于修改记录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.huawei.classroom.student.h05;
public class Soldier extends Base{ private static int livingSoldierCount; private static int deadSoldierCount;
public Soldier(int lv, int ap) { super(lv, ap); setLivingSoldierCount(); }
public static int getLivingSoldierCount() { return livingSoldierCount; } public static void setLivingSoldierCount() { livingSoldierCount++; } public static int getDeadedSoldierCount() { return deadSoldierCount; } public static void setDeadedSoldierCount() { deadSoldierCount++; livingSoldierCount--; }
}
|
Dog
类继承 Base
的构造方法,初始化特定的生命值和攻击力。注意到特殊要求“对战人类时候一口毙命”,则需要重写(此处和重载无区别)Base
的 attack
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.huawei.classroom.student.h05;
public class Dog extends Base { public Dog() { super(50, 5); }
public void attack(Soldier soldier) { if (soldier.lifeValue > 0) { soldier.lifeValue = 0; Soldier.setDeadedSoldierCount(); } } }
|
0x04 HeavyTank
&MediumTank
只需要继承 Tank
的构造方法,初始化特定的生命值和攻击力:
1 2 3 4 5 6 7 8 9 10 11
| package com.huawei.classroom.student.h05;
public class HeavyTank extends Tank { public HeavyTank() { super(200, 20); }
}
|
1 2 3 4 5 6 7 8 9 10 11
| package com.huawei.classroom.student.h05;
public class MediumTank extends Tank { public MediumTank() { super(100, 10); }
}
|
0x05 RifleSoldier
&RPGSoldier
RifleSoldier
“可以一次击毙军犬”,所以需要重写(重载)attack
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.huawei.classroom.student.h05;
public class RifleSoldier extends Soldier{ public RifleSoldier() { super(50, 5); }
public void attack(Dog dog) { if (dog.lifeValue > 0) dog.lifeValue = 0; } }
|
RPGSoldier
只需初始化特定生命值和攻击力即可:
1 2 3 4 5 6 7 8 9 10 11
| package com.huawei.classroom.student.h05;
public class RPGSoldier extends Soldier{ public RPGSoldier() { super(50, 10); }
}
|
0x06 Test
Test
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| package com.huawei.classroom.student.h05;
public class Test {
public Test() { }
public static void main(String[] args) { Barrack barrack=new Barrack(); if(barrack.getHealth()==100) { System.out.println("ok1"); } RifleSoldier rifleSoldier1=(RifleSoldier)barrack.traing(EnumObjectType.rifleSoldier); if(rifleSoldier1.getHealth()==50) { System.out.println("ok2"); } RPGSoldier rPGSoldier1=(RPGSoldier)barrack.traing(EnumObjectType.RPGSoldier ); if(rPGSoldier1.getHealth()==50) { System.out.println("ok3"); } Dog dog1=(Dog)barrack.traing(EnumObjectType.dog ); if(dog1.getHealth()==50) { System.out.println("ok4"); } WarFactory warFactory=new WarFactory(); if(warFactory.getHealth()==100) { System.out.println("ok5"); } Tank mediumTank1=(MediumTank)warFactory.building(EnumObjectType.mediumTank); if(mediumTank1.getHealth()==100) { System.out.println("ok6"); }
Tank heavyTank1=(HeavyTank)warFactory.building(EnumObjectType.heavyTank ); if(heavyTank1.getHealth()==200) { System.out.println("ok7"); }
heavyTank1.attack(mediumTank1); if (mediumTank1.getHealth() == 80) { System.out.println("ok7.3"); }
mediumTank1.attack(heavyTank1); if (heavyTank1.getHealth() == 190) { System.out.println("ok7.6"); }
mediumTank1.attack(rifleSoldier1); if(rifleSoldier1.getHealth()==40){ System.out.println("ok8"); }
dog1.attack(rifleSoldier1); if(rifleSoldier1.isDestroyed()) { System.out.println("ok9"); }
mediumTank1.attack(dog1); if(dog1.getHealth()==40){ System.out.println("ok10"); } RifleSoldier rifleSoldier2=(RifleSoldier)barrack.traing(EnumObjectType.rifleSoldier); rifleSoldier2.attack(dog1); if(dog1.isDestroyed()) { System.out.println("ok11"); } if(Soldier.getLivingSoldierCount()==2&&Soldier.getDeadedSoldierCount()==1) { System.out.println("ok12"); }
}
}
|