diff --git a/TIL/2026-06-16-inheritance.md b/TIL/2026-06-16-inheritance.md new file mode 100644 index 0000000..cd49964 --- /dev/null +++ b/TIL/2026-06-16-inheritance.md @@ -0,0 +1,103 @@ +# 2026-06-16 상속 + +## 과제 피드백 + +- `assertDoesNotThrow(() -> mainTestWand.setPower(setRandomDoubleInRange), "정상 값 입력시 예외발생하는 오류 발생");` 와 같은 + 랜덤값을 이용한 테스트는 지양하는것이 좋음. + - 말 그대로 랜덤값이다보니, 성공 실패가 갈릴 수 있음 + - 경계값 테스트로 전환하여 진행할 것 +- `@BeforeEach` 를 이용하여 매 테스트 실행 전에 준비작업 (`Wand testWand = new Wand();` 와 같은) 을 자동화 할 수 있음 +- 코드 리뷰나 협업할 때 편하도록 한 눈에 들어올 수 있게 코드 길이를 조절할 것 + +--- + +## 상속 + +- 이전에 만든 클래스와 비슷한 다른 클래스를 만들 필요가 있을 때 사용 + +### 상속의 기초 + +- extends (클래스명)을 클래스 뒤에 붙여, 기존 (클래스명)을 기초로 하는 새로운 클래스 정의 가능 +- 부모클래스의 멤버는 자동적으로 자식클래스에 상속 + - 추가되는 부분만 기술하면 됨 +- 부모클래스에 있는 메서드를 자식클래스에서 작성하는 경우를 오버라이드한다고 함 +- final을 붙인 클래스는 상속 / 오버라이드 할 수 없다. +- is-a 를 충족해야 올바른 상속 + - 예시 + 1. 차는 탈 것의 한 종류다. (car is a vehicle) + 2. IPhone은 Phone의 한 종류다. +- 상속에는 추상적 / 구체적 관계에 있다는 것을 정의하는 역할도 가짐 + +#### 오버라이드 + +- 부모 클래스에서 받은 메서드를 자식클래스에 맞게 수정하는 것 +- Slime에서의 attack()은 hero의 체력을 10 깍는 함수지만
PoisonSlime의 attack()은 체력을 10 깎음과 동시에 독데미지로 현재 체력의 1/5 만큼 추가로 깍음 + - 코드 예시 + ```java + // Slime 에서의 attack() + public void attack(Hero hero) { + if (hero == null) { + throw new IllegalArgumentException("공격하는 대상이 있어야합니다."); + } + + System.out.println("슬라임 " + this.suffix + "이/가 공격했다"); + System.out.println("대상 " + hero.getName() + "에게 " + slimeAttackPoint + "포인트 데미지!"); + + hero.setHP(hero.getHP() - slimeAttackPoint); + } + + // PoisonSlime 에서의 attack() + @Override + public void attack(Hero hero) { + super.attack(hero); + if (poisonCount > 0) { + System.out.println("포이즌슬라임" + this.getSuffix() + "의 능력! 추가로, 독 포자를 살포했다!"); + System.out.println("대상 " + hero.getName() + "에게 " + hero.getHP() / 5 + "포인트 독 데미지!"); + hero.setHP(hero.getHP() * 4 / 5); + poisonCount -= 1; + } else { + System.out.println("포이즌슬라임" + this.getSuffix() + "은 독을 모두 소모했다."); + } + } + ``` + +### 인스턴스 + +- 내부에 부모클래스의 인스턴스를 가지는 다중구조를 가짐 +- 보다 바깥의 인스턴스에 속하는 자식클래스의 오버라이드한 메서드부터 동작 + - 오버라이드 한 메서드 -> 자식 클래스의 메서드 -> 부모클래스의 메서드 순으로 검색하여 실행 + - 단, 생성자의 경우에는 부모 먼저 실행하고, 자식의 생성자가 실행함 + - 부모 클래스인 Hero에 있는 생성자가 자식클래스인 SuperHero에 있는 생성자보다 먼저 동작 +- 외측의 인스턴스에 속하는 메서드는 super를 사용하여 내측 인스턴스의 멤버에 접근 가능 + +### 생성자 동작 + +- 다중 구조의 인스턴스가 생성되는데, JVM은 자동으로 가장 외측 인스턴스의 생성자를 호출함. +- 모든 생성자는 부모 인스턴스의 생성자를 호출 할 필요가 있음 +- 생성자 선두에 `super()`가 없어도 암묵적으로 `super();` 가 추가됨 + +## UML + +- 아래와 같이 PlantUML을 이용하여 구성도를 그릴 수 있음 +- 정보를 주고받을 때 용이함 + +```plantuml +@startuml + +scale 2 + +class SuperHero extends Hero { + +void setFlying(boolean Flying) + +void boolean isFlying() +} + +class Hero +{ + - int hp + + + void attack() +} + +@enduml +``` + diff --git a/game/game.puml b/game/game.puml new file mode 100644 index 0000000..6696fd5 --- /dev/null +++ b/game/game.puml @@ -0,0 +1,134 @@ +@startuml + +scale 2 + +class SuperHero extends Hero { + - boolean isFlying + + + SuperHero(String name, int hp) + + SuperHero(String name) + + SuperHero() + + + void setFlying(boolean Flying) + + boolean isFlying() + + void run() +} + +class Hero +{ + - int hp + - int maxHP + - String name + + + String getName() + + int getHP() + + int getMaxHP() + + void setName(String name) + + void setHP(int hp) + + void setMaxHP(int maxHP) + + + void attack() + + void run() + + void slip() +} + + +class Wizard +{ + - String name + - int hp + - int mp + - Wand wand + + + int defaultWizardMP = 100 + + + {Constructor} Wizard(String name, int hp, int mp, Wand wand) + + {Constructor} Wizard(String name, int hp, int mp) + + {Constructor} Wizard(String name, int hp) + + {Constructor} Wizard(String name) + + + String getName() + + void setName(String name) + + int getHP() + + void setHP(int hp) + + int getMP() + + void setMP(int mp) + + Wand getWand() + + void setWand(Wand wand) + + + void heal(Hero hero) +} + +class GreatWizard extends Wizard +{ + + int defaultGreatWizardMP = 150 + + int recoverPoint = 25 + + + {Constructor} GreatWizard(String name, int hp, int mp, Wand wand) + + {Constructor} GreatWizard(String name, int hp, int mp) + + {Constructor} GreatWizard(String name, int hp) + + {Constructor} GreatWizard(String name) + + + heal(Hero hero) + + superHeal(Hero hero) +} + +class Slime +{ + ~ int slimeAttackPoint + - String suffix + - int hp + - int maxHP + + + {Constructor} Slime(String suffix, int hp) + + + String getSuffix() + + int getHP() + + int getMaxHP() + + + void setSuffix(String suffix) + + void setHP(int hp) + + void setMaxHP(int MaxHP) +} + +class PoisonSlime extends Slime +{ + - int poisonCount = 5 + + + {Constructor} PoisonSlime(String suffix, int hp) + + + int getPoisonCount() + + void attack(Hero hero) +} + +class Cleric +{ + ~ int maxHP + ~ int maxMP + ~ int selfAidMPCost = 5 + ~ int randBoundary = 3 + ~ Random rand + + - String name + - int hp + - int mp + + + {Constructor} Cleric(String name, int hp, int mp) + + {Constructor} Cleric(String name, int hp) + + {Constructor} Cleric(String name) + + + int getSelfAidMPCost() + + String getName() + + int getHP() + + int getMP() + + + void setName(String name) + + void setHP(int hp) + + void setMP(int mp) + + + void selfAid() + + int pray(int second) + +} + +@enduml \ No newline at end of file diff --git a/game/src/main/java/com/survivalcoding/Cleric.java b/game/src/main/java/com/survivalcoding/Cleric.java index b4e3a68..53718aa 100644 --- a/game/src/main/java/com/survivalcoding/Cleric.java +++ b/game/src/main/java/com/survivalcoding/Cleric.java @@ -9,19 +9,14 @@ public class Cleric { final int randBoundary = 3; final Random rand = new Random(); - String name; - int hp; - int mp; + private String name; + private int hp; + private int mp; Cleric(String name, int hp, int mp) { - if (hp <= 0 || hp > 50) { - throw new IllegalArgumentException("HP는 최소 1, 최대 50이어야 합니다."); - } else if (mp < 0 || mp > 10) { - throw new IllegalArgumentException("MP는 최대 10이여야 합니다. 음수는 입력할 수 없습니다."); - } - this.name = name; - this.hp = hp; - this.mp = mp; + this.setName(name); + this.setHP(hp); + this.setMP(mp); } Cleric(String name, int hp) { @@ -32,17 +27,45 @@ public class Cleric { this(name, maxHP, maxMP); } - Cleric() { - throw new IllegalArgumentException("스탯을 입력하지 않은 클레릭은 생성될 수 없습니다."); + + public int getSelfAidMPCost() { + return selfAidMPCost; + } + + public String getName() { + return name; + } + + public void setName(String name) { + if (name == null) { + throw new IllegalArgumentException("이름은 null값 일 수 없습니다."); + } + this.name = name; + } + + public int getHP() { + return hp; + } + + public void setHP(int hp) { + this.hp = Math.clamp(hp, 0, maxHP); + } + + public int getMP() { + return mp; + } + + public void setMP(int mp) { + this.mp = Math.clamp(mp, 0, maxMP); } void selfAid() { // 5 미만일 때 실패하는 로직 구현 - if (mp < selfAidMPCost) { + if (this.getMP() < selfAidMPCost) { System.out.println("MP가 부족합니다."); return; } - mp -= selfAidMPCost; + this.setMP(this.getMP() - selfAidMPCost); hp = maxHP; } diff --git a/game/src/main/java/com/survivalcoding/GreatWizard.java b/game/src/main/java/com/survivalcoding/GreatWizard.java new file mode 100644 index 0000000..4272ff2 --- /dev/null +++ b/game/src/main/java/com/survivalcoding/GreatWizard.java @@ -0,0 +1,65 @@ +/* +연습문제 5. +GreatWizard 클래스 요구사항: + Wizard 클래스를 상속받음 +필드: + mp의 초기값은 150 +메서드: + void heal(Hero hero) 재정의 : hp를 25 회복시키고 자신의 mp를 5 소모 + void superHeal(Hero hero) : hp를 최대로 회복시키고 자신의 mp를 50 소모 + mp가 부족하면 "마나가 부족합니다" 출력 + 힐을 성공하면 "슈퍼 힐을 시전했습니다. 대상 HP: XX" 출력 + */ + +package com.survivalcoding; + +public class GreatWizard extends Wizard { + public static final int defaultGreatWizardMP = 150; + public static int recoverPoint = 25; + public static final int healMPCost = 5; + public static final int superHealMPCost = 50; + + + public GreatWizard(String name, int hp, int mp, Wand wand) { + super(name, hp, mp, wand); + } + + public GreatWizard(String name, int hp, int mp) { + super(name, hp, mp); + } + + public GreatWizard(String name, int hp) { + super(name, hp, defaultGreatWizardMP); + } + + public GreatWizard(String name) { + super(name, Wizard.defaultWizardHP, defaultGreatWizardMP); + } + + @Override + public void heal(Hero hero) { + if (hero == null) { + throw new IllegalArgumentException("Hero는 null 값이 올 수 없습니다."); + } + if (this.getMP() >= healMPCost) { + hero.setHP(hero.getHP() + recoverPoint); + System.out.println("힐을 시전했습니다. " + hero.getName() + " HP : " + hero.getHP()); + this.setMP(this.getMP() - healMPCost); + } else { + System.out.println("마나가 부족합니다."); + } + } + + public void superHeal(Hero hero) { + if (hero == null) { + throw new IllegalArgumentException("Hero는 null 값이 올 수 없습니다."); + } + if (this.getMP() >= superHealMPCost) { + this.setMP(this.getMP() - superHealMPCost); + hero.setHP(hero.getMaxHP()); + System.out.println("슈퍼 힐을 시전했습니다. " + hero.getName() + " HP : " + hero.getHP()); + } else { + System.out.println("마나가 부족합니다."); + } + } +} diff --git a/game/src/main/java/com/survivalcoding/Hero.java b/game/src/main/java/com/survivalcoding/Hero.java index c1848fd..28cd0a2 100644 --- a/game/src/main/java/com/survivalcoding/Hero.java +++ b/game/src/main/java/com/survivalcoding/Hero.java @@ -4,9 +4,26 @@ public class Hero { static int money = 100; + static String defaultHeroName = "홍길동"; + static int defaultHeroHP = 100; private String name; private int hp; + private int maxHP = 100; + + + public Hero(String name, int hp) { + setName(name); + setHP(hp); + } + + public Hero(String name) { + this(name, defaultHeroHP); + } + + public Hero() { + this(defaultHeroName, defaultHeroHP); + } static void setRandomMoney() { money = new Random().nextInt(1000); @@ -32,29 +49,35 @@ public void setName(String name) { this.name = name; } - public int getHp() { + public int getHP() { return hp; } public void setHP(int hp) { - this.hp = hp; + this.hp = Math.clamp(hp, 0, maxHP); } - Hero() { - this.name = "홍길동"; - hp = 100; + public int getMaxHP() { + return maxHP; } - Hero(String name) { - this.name = name; - hp = 100; + public void setMaxHP(int maxHP) { + this.maxHP = Math.max(maxHP, 0); } - // 나는 공격하면 hp 가 1씩 빠진다 - void attack() { + + public void attack() { hp -= 1; // sout System.out.println("공격했다"); } + public void run() { + System.out.println("도망쳤다"); + } + + public final void slip() { + hp -= 5; + System.out.println("미끄러졌다!"); + } } \ No newline at end of file diff --git a/game/src/main/java/com/survivalcoding/Person.java b/game/src/main/java/com/survivalcoding/Person.java index 47ae0c7..2b6dca8 100644 --- a/game/src/main/java/com/survivalcoding/Person.java +++ b/game/src/main/java/com/survivalcoding/Person.java @@ -1,14 +1,3 @@ -/* -연습문제 2. -Person 클래스를 작성하시오. - -이름과 태어난 해를 생성자로 받는다 (name, birthYear) -이름과 태어난 해는 한번 정해지면 수정이 불가능하다. -getAge() 메서드를 통해 나이를 제공하지만, 임의로 수정은 불가능하다. -나이 계산은 올해년도에서 birthYear 년도를 뺀 값을 리턴한다 -현재 시간과 날짜를 구하는 방법은 검색 해 볼 것 - */ - package com.survivalcoding; import java.time.LocalDate; diff --git a/game/src/main/java/com/survivalcoding/PoisonSlime.java b/game/src/main/java/com/survivalcoding/PoisonSlime.java new file mode 100644 index 0000000..b536902 --- /dev/null +++ b/game/src/main/java/com/survivalcoding/PoisonSlime.java @@ -0,0 +1,43 @@ +/* +연습문제 3 + +독 슬라임(PoisonSlime) 은, 슬라임 (Slime) 중에서도 특히 “독 공격" 이 되는 것 +PoisonSlime 는 아래의 코드로 인스턴스화 되는 클래스임 +PoisonSlime poisonSlime = new PoisonSlime(“A”); +PoisonSlime 독 공격 가능 횟수를 저장하는 poisonCount(초기값 5)를 가진다. 아무나 수정 금지 +PoisonSlime attack() 메소드가 호출되면 다음 내용의 공격을 한다 +우선, “보통 슬라임과 같은 공격"을 한다 +poisonCount가 0이 아니면 다음을 추가로 수행한다 +화면에 “추가로, 독 포자를 살포했다!” 를 표시 +독 데미지는 용사의 HP / 5 이며 소수점 이하는 버린다. 독 데미지만큼 용사의 HP를 감소시키고 “~포인트 데미지"라고 표시한다 +poisonCount 를 1 감소 시킨다 + + */ + +package com.survivalcoding; + +public class PoisonSlime extends Slime { + private int poisonCount = 5; + + public PoisonSlime(String suffix, int hp) { + super(suffix, hp); + } + + @Override + public void attack(Hero hero) { + super.attack(hero); + if (poisonCount > 0) { + System.out.println("포이즌슬라임" + this.getSuffix() + "의 능력! 추가로, 독 포자를 살포했다!"); + System.out.println("대상 " + hero.getName() + "에게 " + hero.getHP() / 5 + "포인트 독 데미지!"); + hero.setHP(hero.getHP() * 4 / 5); + poisonCount -= 1; + } else { + System.out.println("포이즌슬라임" + this.getSuffix() + "은 독을 모두 소모했다."); + } + } + + public int getPoisonCount() { + return poisonCount; + } + +} diff --git a/game/src/main/java/com/survivalcoding/Slime.java b/game/src/main/java/com/survivalcoding/Slime.java new file mode 100644 index 0000000..02cf01d --- /dev/null +++ b/game/src/main/java/com/survivalcoding/Slime.java @@ -0,0 +1,50 @@ +package com.survivalcoding; + +public class Slime { + static final int slimeAttackPoint = 10; // 슬라임 공격력 + private final String suffix; + private int hp; + private int maxHP; + + public Slime(String suffix, int hp) { + this.suffix = suffix; + setMaxHP(hp); + setHP(hp); + } + + + public String getSuffix() { + return suffix; + } + + public int getHP() { + return hp; + } + + public void setHP(int hp) { + // 체력이 음수가 될 경우 0 으로, maxHP를 초과할 경우 maxHP로 보정 + this.hp = Math.clamp(hp, 0, maxHP); + } + + public int getMaxHP() { + return maxHP; + } + + public void setMaxHP(int maxHP) { + this.maxHP = Math.max(maxHP, 0); + if (this.getHP() < this.maxHP) { + this.setHP(this.maxHP); + } + } + + public void attack(Hero hero) { + if (hero == null) { + throw new IllegalArgumentException("공격하는 대상이 있어야합니다."); + } + + System.out.println("슬라임 " + this.suffix + "이/가 공격했다"); + System.out.println("대상 " + hero.getName() + "에게 " + slimeAttackPoint + "포인트 데미지!"); + + hero.setHP(hero.getHP() - slimeAttackPoint); + } +} diff --git a/game/src/main/java/com/survivalcoding/SuperHero.java b/game/src/main/java/com/survivalcoding/SuperHero.java new file mode 100644 index 0000000..c9163c0 --- /dev/null +++ b/game/src/main/java/com/survivalcoding/SuperHero.java @@ -0,0 +1,35 @@ +package com.survivalcoding; + +public class SuperHero extends Hero { + private boolean isFlying; + + public SuperHero(String name, int hp) { + super(name, hp); + } + + public SuperHero(String name) { + super(name); + } + + public SuperHero() { + super(); + } + + public boolean isFlying() { + return isFlying; + } + + public void setFlying(boolean flying) { + isFlying = flying; + } + + @Override + public void run() { + System.out.println("매우 빠르게 달렸다."); + } + + @Override + public void attack() { + super.attack(); + } +} diff --git a/game/src/main/java/com/survivalcoding/Wand.java b/game/src/main/java/com/survivalcoding/Wand.java index a3384cc..04a61f8 100644 --- a/game/src/main/java/com/survivalcoding/Wand.java +++ b/game/src/main/java/com/survivalcoding/Wand.java @@ -1,14 +1,3 @@ -/* -연습문제 1. -Wand 클래스와 Wizard 클래스의 각 setter 메소드에 대해, 아래의 4가지 규칙에 따라 인수의 타당성 검사를 추가하시오. -부정한 값이 설정 될 경우에는 “throw new IllegalArgumentException(“메세지");” 를 작성하여 프로그램을 중단 시킵니다. -마법사나 지팡이의 이름은 null 일 수 없고, 반드시 3문자 이상이어야 한다 -지팡이의 마력은 0.5 이상 100.0 이하여야 한다. -마법사의 지팡이는 null 일 수 없다. -마법사의 MP는 0 이상이어야 한다. -HP가 음수가 되는 상황에서는 대신 0을 설정 되도록 한다. - */ - package com.survivalcoding; public class Wand { diff --git a/game/src/main/java/com/survivalcoding/Wizard.java b/game/src/main/java/com/survivalcoding/Wizard.java index 550398a..b17cee1 100644 --- a/game/src/main/java/com/survivalcoding/Wizard.java +++ b/game/src/main/java/com/survivalcoding/Wizard.java @@ -1,12 +1,11 @@ /* -연습문제 1. -Wand 클래스와 Wizard 클래스의 각 setter 메소드에 대해, 아래의 4가지 규칙에 따라 인수의 타당성 검사를 추가하시오. -부정한 값이 설정 될 경우에는 “throw new IllegalArgumentException(“메세지");” 를 작성하여 프로그램을 중단 시킵니다. -마법사나 지팡이의 이름은 null 일 수 없고, 반드시 3문자 이상이어야 한다 -지팡이의 마력은 0.5 이상 100.0 이하여야 한다. -마법사의 지팡이는 null 일 수 없다. -마법사의 MP는 0 이상이어야 한다. -HP가 음수가 되는 상황에서는 대신 0을 설정 되도록 한다. +연습문제 4. +필드: + int mp (초기값 100) +메서드: + void heal(Hero hero) : hp를 20 회복시키고 자신의 mp를 10 소모 + mp가 부족하면 "마나가 부족합니다" 출력 + 힐을 성공하면 "힐을 시전했습니다. 대상 HP: XX" 출력 */ package com.survivalcoding; @@ -15,9 +14,12 @@ public class Wizard { static final int nameMinLength = 3; static final String tooShortName = "12"; static final int maxHP = 50; - static final int maxMP = 30; + static final int maxMP = 500; static final String defaultWandName = "기본지팡이"; static final double defaultWandPower = 1.0; + static public final int defaultWizardMP = 100; + static public final int defaultWizardHP = 30; + private Wand wand = new Wand(defaultWandName, defaultWandPower); static int basePoint = 10; // heal 기술 기본값 @@ -37,11 +39,11 @@ public Wizard(String name, int hp, int mp) { } public Wizard(String name, int hp) { - this(name, hp, maxMP, new Wand(defaultWandName, defaultWandPower)); + this(name, hp, defaultWizardMP, new Wand(defaultWandName, defaultWandPower)); } public Wizard(String name) { - this(name, maxHP, maxMP, new Wand(defaultWandName, defaultWandPower)); + this(name, defaultWizardHP, defaultWizardMP, new Wand(defaultWandName, defaultWandPower)); } public String getName() { @@ -92,8 +94,14 @@ public void heal(Hero hero) { if (hero == null) { throw new IllegalArgumentException("Hero는 null 값이 올 수 없습니다."); } - int recoverPoint = (int) (basePoint * this.wand.getPower()); - hero.setHP(hero.getHp() + recoverPoint); + if (this.getMP() >= 10) { + int recoverPoint = (int) (basePoint * this.wand.getPower()); + hero.setHP(hero.getHP() + recoverPoint); + System.out.println("힐을 시전했습니다. " + hero.getName() + "HP : " + hero.getHP()); + this.setMP(this.getMP() - 10); + } else { + System.out.println("마나가 부족합니다."); + } } public void fireball(Hero hero) { diff --git a/game/src/test/java/com/survivalcoding/ClericTest.java b/game/src/test/java/com/survivalcoding/ClericTest.java index acbeb8c..c951227 100644 --- a/game/src/test/java/com/survivalcoding/ClericTest.java +++ b/game/src/test/java/com/survivalcoding/ClericTest.java @@ -28,11 +28,7 @@ void clericCreation() { assertStats("set1 스탯 확인(전부 입력)", set1, testName, testHP, testMP); assertStats("set2 스탯 확인(이름, HP 입력)", set2, testName, testHP, Cleric.maxMP); assertStats("set3 스탯 확인(이름 입력)", set3, testName, Cleric.maxHP, Cleric.maxMP); - - // 생성 안되는지 검증 - assertThrows(IllegalArgumentException.class, () -> { - Cleric noNameCase = new Cleric(); - }); + // 최대, 최소 값을 지키지 않는 경우 생성이 안되는지 검증 assertAll("최대값, 최소값을 지키지 않았을 경우 확인", @@ -54,9 +50,9 @@ void clericCreation() { // 스탯 확인을 위한 임시 함수 private void assertStats(String headDescription, Cleric cleric, String exceptedName, int exceptedHP, int exceptedMP) { assertAll(headDescription, - () -> assertEquals(exceptedName, cleric.name, "이름 틀림"), - () -> assertEquals(exceptedHP, cleric.hp, "HP 틀림"), - () -> assertEquals(exceptedMP, cleric.mp, "MP 불일치") + () -> assertEquals(exceptedName, cleric.getName(), "이름 틀림"), + () -> assertEquals(exceptedHP, cleric.getHP(), "HP 틀림"), + () -> assertEquals(exceptedMP, cleric.getMP(), "MP 불일치") ); } @@ -70,27 +66,27 @@ void selfAid() { // given 준비 Cleric test1 = new Cleric("testCleric1"); - test1.hp = initSelfAidHP; - test1.mp = initSelfAidMP; + test1.setHP(initSelfAidHP); + test1.setMP(initSelfAidMP); // when 실행 test1.selfAid(); // then 검증 - assertEquals(initSelfAidMP - test1.selfAidMPCost, test1.mp); - assertEquals(Cleric.maxHP, test1.hp); + assertEquals(initSelfAidMP - test1.getSelfAidMPCost(), test1.getMP()); + assertEquals(Cleric.maxHP, test1.getHP()); // mp가 부족할 경우 테스트 // given 준비 - test1.mp = mpEdgeCase; - test1.hp = initSelfAidHP; + test1.setMP(mpEdgeCase); + test1.setHP(initSelfAidHP); // when 실행 test1.selfAid(); // then 검증 - assertNotEquals(Cleric.maxHP, test1.hp); - assertEquals(mpEdgeCase, test1.mp); + assertNotEquals(Cleric.maxHP, test1.getHP()); + assertEquals(mpEdgeCase, test1.getMP()); } @Test @@ -107,7 +103,7 @@ void pray() { int tmp; // 관측용 변수 생성 // 초기값 - test2.mp = initPrayMP; + test2.setMP(initPrayMP); // when 실행 tmp = test2.pray(randTestSecond); @@ -116,11 +112,11 @@ void pray() { // 피드백 이후 assert 관련 함수들을 찾아서 랜덤값 작동 확인 방법 개선 // pray(int second)는 second + (0 ~ 2) 이기 때문에, 회복량은 (현재MP ~ 현재MP + 2) 가 되어야 함. assertTrue(tmp >= 0 && tmp <= 2, "랜덤함수 테스트실패"); - assertTrue(test2.mp >= initPrayMP && test2.mp <= (initPrayMP + 2), "실제 마나 회복 테스트실패"); + assertTrue(test2.getMP() >= initPrayMP && test2.getMP() <= (initPrayMP + 2), "실제 마나 회복 테스트실패"); // 초기값 9로 maxMP를 넘기는 상황을 연축 - test2.mp = overflowInitPrayMP; - final int correctAnswer = Cleric.maxMP - test2.mp; + test2.setMP(overflowInitPrayMP); + final int correctAnswer = Cleric.maxMP - test2.getMP(); // when 실행 // 무조건 maxMP를 넘기므로 1이 출력되도록 실행 diff --git a/game/src/test/java/com/survivalcoding/GreatWizardTest.java b/game/src/test/java/com/survivalcoding/GreatWizardTest.java new file mode 100644 index 0000000..380f93b --- /dev/null +++ b/game/src/test/java/com/survivalcoding/GreatWizardTest.java @@ -0,0 +1,112 @@ +package com.survivalcoding; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class GreatWizardTest { + final String testName = "Test"; + final int testHP = Wizard.defaultWizardHP; + final int testMP = GreatWizard.defaultGreatWizardMP; + final String testWandName = Wizard.defaultWandName; + final double testWandPower = Wizard.defaultWandPower; + final int excessiveRecoveryAmount = 10; + final int healMPCost = GreatWizard.healMPCost; + final int superHealTestHP = 1; + final int superHealMPCost = GreatWizard.superHealMPCost; + + + GreatWizard testGreatWizard; + Hero testHero; + + @BeforeEach + void setUp() { + testGreatWizard = new GreatWizard(testName); + testHero = new Hero(testName); + } + + @Test + @DisplayName("테스트용 GreatWizard 정상 생성 여부 확인") + void createGreatWizardTest() { + // Given + Wand testWand = new Wand(testWandName, testWandPower); + GreatWizard createTestGW = new GreatWizard(testName, testHP, testMP, testWand); + + // when then + assertAll( + "getter / setter 이상작동함", + () -> assertEquals(testName, createTestGW.getName()), + () -> assertEquals(testHP, createTestGW.getHP()), + () -> assertEquals(testMP, createTestGW.getMP()), + () -> assertEquals(testWand, createTestGW.getWand()) + ); + } + + @Test + @DisplayName("heal 정상 작동 여부 확인") + void heal() { + // Given + // testGreatWizard는 setUp에서 준비됨 + testHero.setHP(testHero.getMaxHP() - GreatWizard.recoverPoint); + int currentMP = testGreatWizard.getMP(); + + // when + testGreatWizard.heal(testHero); + + // then + assertEquals(testHero.getMaxHP(), testHero.getHP(), "회복이 제대로 안됨"); + assertEquals(currentMP - healMPCost, testGreatWizard.getMP(), "마나 소모가 안됨"); + } + + @Test + @DisplayName("heal 회복량 초과시 최대 체력까지 회복되는가") + void overHeal() { + // Given + // testGreatWizard는 setUp에서 준비됨 + testHero.setHP(testHero.getMaxHP() - GreatWizard.recoverPoint + excessiveRecoveryAmount); + + // when + testGreatWizard.heal(testHero); + + // then + assertEquals(testHero.getMaxHP(), testHero.getHP()); + } + + @Test + @DisplayName("heal 대상이 null값일 때 예외가 발생하는가") + void nullHeal() { + // given + // testGreatWizard는 setUp에서 준비됨 + + // when then + assertThrows(IllegalArgumentException.class, () -> testGreatWizard.heal(null)); + } + + @Test + @DisplayName("superHeal로 testHero의 체력이 최대체력까지 회복되는가") + void superHeal() { + // given + // testGreatWizard는 setUp에서 준비됨 + testHero.setHP(superHealTestHP); + int currentMP = testGreatWizard.getMP(); + + // when + testGreatWizard.superHeal(testHero); + + // then + assertEquals(testHero.getMaxHP(), testHero.getHP()); + assertEquals(currentMP - superHealMPCost, testGreatWizard.getMP(), "마나 소모가 안됨"); + } + + @Test + @DisplayName("superHeal 대상이 null값일 때 예외가 발생하는가") + void nullSuperHeal() { + // Given + // testGreatWizard는 setUp에서 준비됨 + + // when then + assertThrows(IllegalArgumentException.class, () -> testGreatWizard.superHeal(null)); + } +} \ No newline at end of file diff --git a/game/src/test/java/com/survivalcoding/HeroTest.java b/game/src/test/java/com/survivalcoding/HeroTest.java index 58db39b..126e2e4 100644 --- a/game/src/test/java/com/survivalcoding/HeroTest.java +++ b/game/src/test/java/com/survivalcoding/HeroTest.java @@ -19,13 +19,13 @@ void attack() { hero.attack(); // then 검증 - assertEquals(49, hero.getHp()); + assertEquals(49, hero.getHP()); // when 실행 hero.attack(); // then 검증 - assertEquals(48, hero.getHp()); + assertEquals(48, hero.getHP()); } @Test diff --git a/game/src/test/java/com/survivalcoding/PoisonSlimeTest.java b/game/src/test/java/com/survivalcoding/PoisonSlimeTest.java new file mode 100644 index 0000000..4d24c84 --- /dev/null +++ b/game/src/test/java/com/survivalcoding/PoisonSlimeTest.java @@ -0,0 +1,62 @@ +package com.survivalcoding; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PoisonSlimeTest { + final String testSuffix = "Test"; + final int testSlimeHP = 10; + final int defaultHeroHP = 100; + + Hero testHero; + + @BeforeEach + void setUp() { + testHero = new Hero(Hero.defaultHeroName, defaultHeroHP); + } + + @Test + @DisplayName("PoisonSlime 공격기능 테스트") + void attackDamageTest() { + // Given + // 슬라임의 공격력은 10, PoisonSlime 특성으로 현재 체력의 1/5 만큼 추가 감소해야함 + // 즉 testHero의 체력은 (기존 체력 - 10) * 4 / 5 가 되어야 하기에 Given에서 answerTestHeroHP를 준비 + int answerTestHeroHP = (testHero.getHP() - 10) * 4 / 5; + PoisonSlime testPoisonSlime = new PoisonSlime(testSuffix, testSlimeHP); + + // when + testPoisonSlime.attack(testHero); + + // then + assertEquals(answerTestHeroHP, testHero.getHP(), "독 데미지를 정상적으로 입히지 않음"); + } + + @Test + @DisplayName("PoisonSlime의 독 공격 횟수가 모두 소진된 후 확인") + void poisonCountTest() { + // Given + int answerTestHeroHP = (testHero.getHP() - 10) * 4 / 5; + PoisonSlime testPoisonSlime = new PoisonSlime(testSuffix, testSlimeHP); + int repeat = testPoisonSlime.getPoisonCount(); + + // when then + // testHero가 중간에 죽으면 안되므로 체력을 회차별로 defaultHeroHP 로 초기화 + for (int i = 0; i < repeat; i++) { + testHero.setHP(defaultHeroHP); + testPoisonSlime.attack(testHero); + assertEquals(answerTestHeroHP, testHero.getHP(), "데미지 계산에서 오류가 발생"); + } + + testHero.setHP(defaultHeroHP); + testPoisonSlime.attack(testHero); + + assertEquals( + defaultHeroHP - PoisonSlime.slimeAttackPoint, + testHero.getHP(), + "poisonCount를 전부 소모했지만 독데미지를 입힘" + ); + } +} \ No newline at end of file diff --git a/game/src/test/java/com/survivalcoding/SlimeTest.java b/game/src/test/java/com/survivalcoding/SlimeTest.java new file mode 100644 index 0000000..809b984 --- /dev/null +++ b/game/src/test/java/com/survivalcoding/SlimeTest.java @@ -0,0 +1,76 @@ +package com.survivalcoding; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SlimeTest { + final String testSuffix = "Test"; + final int testSlimeHP = 10; + final int defaultHeroHP = 100; + final int negativeTest = -1; + + @Test + @DisplayName("Get 정상 작동 테스트") + void getTest() { + // Given + Slime testSlime = new Slime(testSuffix, testSlimeHP); + String getSuffixTest; + int getHPTest; + + // When + getHPTest = testSlime.getHP(); + getSuffixTest = testSlime.getSuffix(); + + // then + assertEquals(testSlimeHP, getHPTest); + assertEquals(testSuffix, getSuffixTest); + } + + @Test + @DisplayName("setHP시 음수나 최대 체력을 넘기지 않는지") + void setHP() { + // Given + Slime testSlime = new Slime(testSuffix, testSlimeHP); + + // when then + assertAll( + "setHP 검증 실패", + () -> { + testSlime.setHP(testSlime.getMaxHP() + 1); + assertEquals( + testSlime.getMaxHP(), + testSlime.getHP(), + "최대 체력을 넘기는 HP 값이 적용됨" + ); + }, + () -> { + testSlime.setHP(negativeTest); + assertEquals( + 0, + testSlime.getHP(), + "체력이 음수가 되었지만 HP 가 0으로 보정되지 않음"); + } + ); + } + + @Test + @DisplayName("attack 정상 작동 테스트") + void attack() { + // Given + Hero testHero = new Hero(Hero.defaultHeroName, defaultHeroHP); + Slime testSlime = new Slime(testSuffix, testSlimeHP); + + // when + testSlime.attack(testHero); + + // then + assertEquals( + defaultHeroHP - Slime.slimeAttackPoint, + testHero.getHP(), + "슬라임 공격 로직이 정상작동 하지 않음"); + + } +} \ No newline at end of file diff --git a/game/src/test/java/com/survivalcoding/WandTest.java b/game/src/test/java/com/survivalcoding/WandTest.java index 86831e5..4987450 100644 --- a/game/src/test/java/com/survivalcoding/WandTest.java +++ b/game/src/test/java/com/survivalcoding/WandTest.java @@ -36,8 +36,10 @@ void setName() { // then assertEquals(changeName, mainTestWand.getName()); - assertAll("이름이 null값이거나, 3문자 이하일 경우 테스트", () -> assertThrows(IllegalArgumentException.class, () -> mainTestWand.setName(null)), - () -> assertThrows(IllegalArgumentException.class, () -> mainTestWand.setName(tooShortName))); + assertAll("이름이 null값이거나, 3문자 이하일 경우 테스트", + () -> assertThrows(IllegalArgumentException.class, () -> mainTestWand.setName(null)), + () -> assertThrows(IllegalArgumentException.class, () -> mainTestWand.setName(tooShortName)) + ); } @Test diff --git a/image/ClassDiagram.png b/image/ClassDiagram.png new file mode 100644 index 0000000..90018ab Binary files /dev/null and b/image/ClassDiagram.png differ