Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions TIL/sample/2026-06-16-상속.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# 2026-06-16 상속

## 오늘 배운 내용

- 복사 붙여넣기의 경우 추가,수정에 시간이 걸리고 소스의 파악이나 관리가 어려워지기 때문에 해결책으로 상속을 활용한다
- 다중 상속은 Java에서 금지

## 기억할 것

- "is-a원칙"이라고 하는 수칙에 따른 상속이 올바른 상속이다
- 개념적으로 is-a관계가 되지 못 함에도 불구하고 상속을 사용한 경우가 잘못 된 상속 이다.
- 잘못된 상속을 하면 클래스를 확장할때 현실 세계와의 모순이 생기고 객체지향의 3대 특징중 1가지 다형성을 이용할수 없게 된다.
- 자식클래스 일 수록 구체화 되고 부모 클래스 일수록 추상적인 것으로 일반화 된다.

## 정리

### 상속의 기초

- extends를 사용하여 기존 클래스를 기초로 하는 새로운 클래스를 정의 할 수 있다
- 부모 클래스의 멤버는 자동적으로 자식 클래스에 상속되므로, 자식 클래스에는 추가 된 부분만 기술 하면 된다
- 부모 클래스에 있는 메소드를, 자식 클래스에서 재작성 할 경우 이것을 오버라이드 한다고 한다
- final 을 붙인 클래스는 상속이 되지 않고, final 이 붙은 메소드는 오버라이드 되지 않는다
- 올바른 상속이란 “자식 클래스 is-a 부모 클래스"
- 상속에는 “추상적, 구체적" 관계에 있다는 것을 정의하는 역할도 있음

### 인스턴스

- 인스턴스는 내부에 부모클래스의 인스턴스를 가지는 다중구조를 가진다
- 보다 외측의 인스턴스에 속하는 메소드가 우선적으로 동작한다
- 외측의 인스턴스에 속하는 메소드는 super 을 사용하여 내측 인스턴스의 멤버에 점근할 수 있다

### 생성자 동작

- 다중구조의 인스턴스가 생성되는데, JVM 는 자동적으로 가장 외측 인스턴스의 생성자를 호출
- 모든 생성자는, “부모 인스턴스의 생성자"를 호출 할 필요가 있다
- 생성자의 선두에 super() 가 없으면, 암묵적인 “super();” 가 추가 됨

## 실습 코드

```java
package com.survivalcoding;

public class PoisonSlime extends Slime {
private int poisonCount = 5;

public PoisonSlime(String suffix) {
super(suffix, 50);
}

@Override
public void attack(Hero hero) {
super.attack(hero);

if (this.poisonCount > 0) {
System.out.println("추가로 독 포자를 살포했다!");
int poisonDamage = hero.getHP() / 5;
hero.setHP(hero.getHP() - poisonDamage);
System.out.println(poisonDamage + "포인트 데미지");
this.poisonCount--;
}
}

public int getPoisonCount() {
return poisonCount;
}
}


```

## 어려웠던 점

- 웬래 코드를 plantUML로 바꾸는 것이 처음이라 오류를 자주냈다

## 해결 방법

- AI의 도움을 받았다

## 내일 더 공부할 것

- 상속이랑 plantUML에 대해 좀더 공부해야겠다
105 changes: 105 additions & 0 deletions game/game.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
@startuml
package com.survivalcoding {

class Cleric {
{static} +MAXHP : int = 50
{static} +MAXMP : int = 10
-SelfAidCost : int = 5 {readOnly}
~name : String
~HP : int = 50
~MP : int = 10
+Cleric(name: String)
+Cleric(name: String, hp: int)
+Cleric(name: String, hp: int, mp: int)
~selfAid() : void
~pray(time: int) : int
}

class Person {
-name : String {readOnly}
-birthYear : int {readOnly}
+Person(name: String, brithYear: int)
+Person(name: String)
+getName() : String
+getBirthYear() : int
+getAge() : int
}

class Wand {
-name : String
-power : double
+getName() : String
+setName(name: String) : void
+getPower() : double
+setPower(power: double) : void
}

class Hero {
-name : String
-HP : int
-MaxHP : int = 100
+Hero(name: String, HP: int)
+getHP() : int
+setHP(HP: int) : void
+getName() : String
+setName(name: String) : void
+getMaxHP() : int
+setMaxHP(maxHP: int) : void
~attack() : void
}

class Wizard {
-wand : Wand
-hp : int
-mp : int
-name : String
~heal(hero: Hero) : void
+getWand() : Wand
+setWand(wand: Wand) : void
+getHp() : int
+setHp(hp: int) : void
+getMp() : int
+setMp(mp: int) : void
+getName() : String
+setName(name: String) : void
}
Comment on lines +51 to +65

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wizard와 Person 간 상속 관계 누락.

PlantUML 다이어그램에서 Person 클래스는 정의되어 있지만, Wizard가 Person을 상속하는 관계(extends)가 표현되지 않았습니다. 리뷰 스택 컨텍스트에 따르면 Wizard는 Person을 상속해야 합니다.

다음을 추가하세요:

Wizard <|-- Person : 상속 (extends)

또한 Cleric도 Person을 상속하는 경우, 같은 방식으로 표현하세요.

Also applies to: 18-26

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@game/game.puml` around lines 51 - 65, Add missing inheritance relationships
between the character classes and the Person base class in the PlantUML diagram.
In the Wizard class definition (lines 51-65), add a PlantUML inheritance
relationship to show that Wizard extends Person using the appropriate PlantUML
syntax. Additionally, apply the same inheritance relationship fix to the Cleric
class at lines 18-26 if it also should inherit from Person. Use the standard
PlantUML notation to define these parent-child relationships so that the diagram
properly reflects that both Wizard and Cleric are subclasses of Person.


class GreatWizard {
+GreatWizard()
~heal(hero: Hero) : void
+superHeal(hero: Hero) : void
}

class Slime {
-suffix : String {readOnly}
-hp : int
+Slime(suffix: String, hp: int)
+getSuffix() : String
+getHp() : int
+setHp(hp: int) : void
~attack(hero: Hero) : void
}

class PoisonSlime {
-poisonCount : int = 5
+PoisonSlime(suffix: String)
+attack(hero: Hero) : void
+getPoisonCount() : int
}

}

' ---- 클래스 간의 관계 표현 영역 ----

' 1. 마법사 및 대마법사 관련 관계
Wizard "1" --> "1" Wand : 가집니다 (has)
Wizard ..> Hero : heal()에서 참조 (uses)
Wizard <|-- GreatWizard : 상속 (extends)
GreatWizard ..> Hero : heal(), superHeal()에서 참조 (uses)

' 2. 슬라임 관련 관계
Slime <|-- PoisonSlime : 상속 (extends)
Slime ..> Hero : attack()에서 참조 (uses)
PoisonSlime ..> Hero : attack()에서 참조 (uses)

@enduml
9 changes: 9 additions & 0 deletions game/src/main/java/com/survivalcoding/Exercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ public class Exercise {
2.list(학생의 점수가 곂칠수도 있기에)
3.map(도시의 수가 많으니 키값을 검색해서 빠르게 인구수를 찾을수있기에)
*/
/*
26_06_16 연습문제 1
2,3,5
연습문제2
(1) 아이폰 -> Phone -> 기계
(2) BMW -> Car -> 고철
(3) 종이 한자 사전 -> Dictionary -> 폐지
*/
Comment on lines +17 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

연습문제 주석: 문맥 명확화 필요.

라인 17-24의 주석이 명확하지 않습니다:

  • "26_06_16 연습문제 1"에서 제시된 선택지(2,3,5)가 앞의 연습문제 1 답(set, list, map)과 어떤 관계인지 불명확합니다.
  • "연습문제 2"의 is-a 매핑에서 "BMW → Car → 고철"은 일반적인 OOP 상속 계층(Car → Vehicle → Object)과 다릅니다. 이것이 폐기물 처리 분류를 의도한 것이라면, 주석을 더 명확히 하세요.

예: // 폐기물 처리 관점: BMW is-a Car is-a 고철

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@game/src/main/java/com/survivalcoding/Exercise.java` around lines 17 - 24,
Clarify the comments in the Exercise.java file (lines 17-24) by explicitly
explaining the context of each exercise. For "26_06_16 연습문제 1", add a note
clarifying which of the choices (2,3,5) corresponds to which data structure
(set, list, map) from the exercise answers. For "연습문제 2", rewrite the is-a
mappings to make it explicit that these represent waste disposal classification
hierarchies rather than standard OOP inheritance, for example by prefixing with
a comment like "폐기물 처리 관점:" to clarify that "BMW → Car → 고철" is a disposal
category mapping, not a typical object-oriented inheritance hierarchy.


}
31 changes: 31 additions & 0 deletions game/src/main/java/com/survivalcoding/GreatWizard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.survivalcoding;

public class GreatWizard extends Wizard {

public GreatWizard() {
super();
this.setMp(150);
}

@Override
void heal(Hero hero) {
if (this.getMp() < 5) {
System.out.println("마나가 부족합니다.");
return;
}

this.setMp(this.getMp() - 5);
hero.setHP(hero.getHP() + 25);
System.out.println("힐을 시전 했습니다. 대상 HP : " + hero.getHP());
}

public void superHeal(Hero hero) {
if (this.getMp() < 50) {
System.out.println("마나가 부족합니다.");
return;
}
this.setMp(this.getMp() - 50);
hero.setHP(hero.getMaxHP());
System.out.println("슈퍼 힐을 시전했습니다. 대상 HP: " + hero.getHP());
}
}
26 changes: 24 additions & 2 deletions game/src/main/java/com/survivalcoding/Hero.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.survivalcoding;

public class Hero {
String name;
int HP;
private String name;
private int HP;
private int MaxHP = 100;

public Hero(String name, int HP) {
this.name = name;
this.HP = HP;
}
Comment on lines +8 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Hero의 HP/MaxHP 불변식이 보장되지 않습니다.

Line 8-11, Line 17-19, Line 38-40에서 값 검증 없이 대입되어 HP < 0, MaxHP <= 0, HP > MaxHP 상태가 가능합니다. Slime.attackGreatWizard.superHeal이 이 계약에 의존하므로 Hero에서 경계를 강제하는 게 안전합니다.

제안 수정안
 public class Hero {
     private String name;
     private int HP;
     private int MaxHP = 100;

     public Hero(String name, int HP) {
-        this.name = name;
-        this.HP = HP;
+        this.name = name;
+        setHP(HP);
     }
@@
     public void setHP(int HP) {
-        this.HP = HP;
+        if (HP < 0) {
+            this.HP = 0;
+            return;
+        }
+        this.HP = Math.min(HP, this.MaxHP);
     }
@@
     public void setMaxHP(int maxHP) {
-        MaxHP = maxHP;
+        if (maxHP <= 0) {
+            throw new IllegalArgumentException("MaxHP는 1 이상이어야 합니다.");
+        }
+        MaxHP = maxHP;
+        if (this.HP > MaxHP) {
+            this.HP = MaxHP;
+        }
     }
 }

Also applies to: 17-19, 38-40

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@game/src/main/java/com/survivalcoding/Hero.java` around lines 8 - 11, The
Hero class does not validate HP and MaxHP values, allowing invalid states such
as HP < 0, MaxHP <= 0, or HP > MaxHP. Add validation constraints in all three
locations where these fields are assigned. In the constructor (Hero method at
lines 8-11), validate that HP is non-negative and that MaxHP is positive. At the
second assignment location (lines 17-19), validate the invariant for whichever
field is being set. At the third assignment location (lines 38-40), apply the
same validation rules for the field being modified. Ensure that whenever HP or
MaxHP is set, the resulting state satisfies: HP >= 0, MaxHP > 0, and HP <=
MaxHP.


public int getHP() {
return HP;
Expand All @@ -16,4 +22,20 @@ void attack() {
//sout
System.out.println("공격했다");
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getMaxHP() {
return MaxHP;
}

public void setMaxHP(int maxHP) {
MaxHP = maxHP;
}
}
26 changes: 26 additions & 0 deletions game/src/main/java/com/survivalcoding/PoisonSlime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.survivalcoding;

public class PoisonSlime extends Slime {
private int poisonCount = 5;

public PoisonSlime(String suffix) {
super(suffix, 50);
}

@Override
public void attack(Hero hero) {
super.attack(hero);

if (this.poisonCount > 0) {
System.out.println("추가로 독 포자를 살포했다!");
int poisonDamage = hero.getHP() / 5;
hero.setHP(hero.getHP() - poisonDamage);
System.out.println(poisonDamage + "포인트 데미지");
this.poisonCount--;
}
}

public int getPoisonCount() {
return poisonCount;
}
}
30 changes: 30 additions & 0 deletions game/src/main/java/com/survivalcoding/Slime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.survivalcoding;

public class Slime {
private final String suffix;
private int hp;

public Slime(String suffix, int hp) {
this.suffix = suffix;
this.hp = hp;
}

public String getSuffix() {
return suffix;
}

public int getHp() {
return hp;
}

public void setHp(int hp) {
this.hp = hp;
}

void attack(Hero hero) {
System.out.println("슬라임 " + suffix + "이/가 공격했다");
System.out.println("10의 데미지");

hero.setHP(hero.getHP() - 10);
}
}
13 changes: 9 additions & 4 deletions game/src/main/java/com/survivalcoding/Wizard.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
public class Wizard {
private Wand wand;
private int hp;
private int mp;
private int mp = 100;
private String name;

void heal(Hero hero) {
int basePoint = 10;
int recovPoint = (int) (basePoint * this.wand.getPower());
hero.setHP(hero.getHP() + recovPoint);
if (this.mp < 10) {
System.out.println("마나가 부족합니다.");
return;
}

this.mp -= 10;
hero.setHP(hero.getHP() + 20);
System.out.println("힐을 시전 했습니다. 대상 HP : " + hero.getHP());
}

public Wand getWand() {
Expand Down
36 changes: 36 additions & 0 deletions game/src/test/java/com/survivalcoding/GreatWizardTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.survivalcoding;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class GreatWizardTest {

@Test
void heal() {
//준비
String name = "홍홍홍";
int hp = 10;
Hero hero = new Hero(name, hp);
GreatWizard greatWizard = new GreatWizard();
//실행
greatWizard.heal(hero);
//검증
assertEquals(hp + 25, hero.getHP());
assertEquals(145, greatWizard.getMp());
}

@Test
void superHeal() {
//준비
String name = "홍홍홍";
int hp = 10;
Hero hero = new Hero(name, hp);
GreatWizard greatWizard = new GreatWizard();
//실행
greatWizard.superHeal(hero);
//검증
assertEquals(hero.getMaxHP(), hero.getHP());
assertEquals(100, greatWizard.getMp());
}
}
Loading