2025. 1. 14. 22:15ㆍ개발 회고/TIL
😊오늘 배운 내용
프로세스와 쓰레드, 스타크래프트 프로젝트, 결합도 세션, 키오스크 프로젝트 (level 3)
[어떤 문제가 있었는지 + 어떻게 해결하였는지]
1. 스타크래프트 프로젝트 상속문제
클래스 생성과 상속, 다형성을 공부하기 위해 간단한 스타크래프트 프로젝트를 만들어보았다.
Unit 클래스
public class Unit {
//필드
private String name;
private int hp;
private int speed;
private int damage;
private int position;
private String description;
//생성자
public Unit(String name, int hp, int speed, int damage, int position, String description) {
this.name = name;
this.hp = hp;
this.speed = speed;
this.damage = damage;
this.position = position;
this.description = description;
}
//메서드
//현재 위치 출력 메서드
public void printPosition() {
System.out.println(this.name + "의 현재 위치는 현재 " + this.position + "입니다." );
}
//이동 메서드
public void move(String direction) {
if ("right".equals(direction)) {
this.position += 1;
System.out.println(this.name + "이(가) 오른쪽으로 이동했습니다.");
} else if ("left".equals(direction)) {
this.position -= 1;
System.out.println(this.name + "이(가) 왼쪽으로 이동했습니다.");
}
}
//위치 지정 메서드
public void positionSetter(int settingPosition) {
this.position = settingPosition;
}
//공격 메서드
public void attack(Unit target) {
//타겟의 hp가 0이하면 사망했다는 문구가 나옴
if (target.hp <= 0) {
System.out.println(target.name + "이가 사망했습니다. 공격이 불가능한 상태입니다.");
} else {
//타겟의 hp를 감소시킴
System.out.println(this.name + "이가 " + target.name + "이를 공격합니다.");
target.hp -= this.damage;
if (target.hp <= 0) {
target.hp = 0;
}
System.out.println(target.name + "의 남은 체력: " + target.hp);
}
}
//유닛 정보 출력 메서드
public void printInfo() {
System.out.println(this.name + "| 체력: " +this.hp + "| 공격력: " + this.damage + "| 설명: " + this.description);
}
//Getter
public String getName() {
return name;
}
public int getHp() {
return hp;
}
public int getSpeed() {
return speed;
}
public int getDamage() {
return damage;
}
public int getPosition() {
return position;
}
public String getDescription() {
return description;
}
//Setter
public void setHp(int hp) {
this.hp = hp;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public void setDamage(int damage) {
this.damage = damage;
}
}
Terran 클래스
public class Terran extends Unit {
//필드
private final String description = "Terran Unit"; //description 고정
//생성자
public Terran(String name, int hp, int speed, int damage, int position) {
super(name, hp, speed, damage, position, "Terran Unit");
}
//Terran의 move와 attack은 Unit과 같기 때문에 따로 오버라이딩 하지 않았음
}
Protoss 클래스
public class Protoss extends Unit{
//필드
private final String description = "Protoss Unit"; //description 고정
//생성자
public Protoss(String name, int hp, int speed, int damage, int position) {
super(name, hp, speed, damage, position, "Protoss Unit");
}
@Override
//공격 메서드
public void attack(Unit target) {
//타겟의 hp가 0이하면 사망했다는 문구가 나옴
if (target.getHp() <= 0) {
System.out.println(target.getName() + "이가 사망했습니다. 공격이 불가능한 상태입니다.");
} else {
System.out.println(this.getName() + "이가 " + target.getName() + "이를 공격합니다.");
//만약 공격하는 target이 Zerg 타입이라면 데미지가 2배
int targetHP = target.getHp();
if (target instanceof Zerg) {
targetHP -= this.getDamage() * 2;
} else {
targetHP -= this.getDamage();
}
target.setHp(targetHP);
//만약 target의 hp가 0이하라면 무조건 0으로 출력하도록 설정
if (target.getHp() <= 0) {
target.setHp(0);
}
System.out.println(target.getName() + "의 남은 체력: " + target.getHp());
}
}
}
zerg 클래스
public class Zerg extends Unit {
//생성자
public Zerg(String name, int hp, int speed, int damage, int position) {
super(name, hp, speed, damage, position, "Zerg Unit");
}
@Override
//Zerg의 move메서드는 +2,-2로 동작함으로 오버라이딩 하였음
public void move(String direction) {
if ("right".equals(direction)) {
int nowPosition = this.getPosition();
nowPosition += 2;
this.positionSetter(nowPosition);
System.out.println(this.getName() + "이(가) 오른쪽으로 이동했습니다.");
} else if ("left".equals(direction)) {
int nowPosition = this.getPosition();
nowPosition -= 2;
this.positionSetter(nowPosition);
System.out.println(this.getName() + "이(가) 왼쪽으로 이동했습니다.");
}
}
}
Main 클래스
import java.util.Random;
public class Main {
public static void main(String[] args) {
//객체 생성
Terran marine = new Terran("Marine", 20, 10, 6, 0);
Protoss zealot = new Protoss("Zealot", 30, 10, 8, 0);
Zerg zergling = new Zerg("Zergling", 15, 10, 5, 0);
//각 객체의 정보 출력
marine.printInfo();
zealot.printInfo();
zergling.printInfo();
System.out.println("---------------------------------------------------------------------------------------------------");
//각 객체의 위치 출력
marine.printPosition();
zealot.printPosition();
zergling.printPosition();
System.out.println("---------------------------------------------------------------------------------------------------");
//랜덤값 받아오기
Random random = new Random();
int randomInt = random.nextInt(20) + 1;
String[] directions = {"right", "left"};
//유닛 이동
//삼항 연산자로 구현하는 것도 해보기! (nextBoolean 이용)
marine.positionSetter(5);
marine.printPosition();
for(int i = 0; i < randomInt; i++) {
String randomDirection = directions[random.nextInt(directions.length)];
marine.move(randomDirection);
}
System.out.println("---------------------------------------------------------------------------------------------------");
marine.printPosition();
System.out.println("---------------------------------------------------------------------------------------------------");
//공격
zealot.attack(zergling);
zealot.attack(zergling);
zealot.attack(zergling);
zealot.attack(zergling);
}
}
상속을 적용시키기 전까지는 문제가 없었다. 하지만 상속을 구현하려고 Unit 클래스를 부모클래스로 정하고 하위 Terran, Protoss, Zerg 클래스가 Unit을 extends하여 상속을 구현하려고 하니 문제가 하나 발생했다.
나는 필드 중에서 description 부분을 각 자식 클래스 별로 "Terran Unit", "Protoss Unit", "Zerg Unit"으로 초기화한 상태로 객체를 만들고 싶었다.
따라서 나머지 필드는 부모클래스의 필드를 상속받고 description 필드는 자식별로 다르게 초기화하고 싶었던 것이다.
그래서 코드를 다음과 같이 작성하였었다.
자식 생성자에는 필드에서 이미 값이 초기화 되어있으니 매개변수에 description은 빼야지~라고 생각했다. 그러나 자식클래스로 객체를 초기화할때 먼저 super()를 이용해서 부모클래스의 필드로 초기화를 먼저 한 후 나머지 값을 자식클래스의
필드로 초기화한다.
그랬더니 위와같은 오류가 났고 나는 부모생성자로 초기화되기 전에 Zerg 클래스의 description값으로 할당할 수 없다는 뜻으로 저는 이해했다. 아 그럼 부모 생성자를 description 없는 생성자를 하나 더 만들어서(오버로딩) description을 빼고 부모생성자로 초기화한 후 자식클래스 생성자에서 description 필드를 초기화하면 되겠다라고 생각했다. 하지만 결과가 null값이 할당되었고 이 방법은 잘못되었다고 판단했다. 그래서 튜터님께 질문하였고 깨달았다!
생성자를 통해 객체가 생성될 때 객체가 생성되며 필드가 생성되기 전에 필드를 집어넣어주려 해서 생기는 오류였다. 그래서 static으로 메모리에 처음부터 적재시켜주던가(비추), 아니면 상수값으로 정확히 이 값을 넘겨줄 것이다라는 걸 명시해주어야한다.
따라서 부모클래스 생성자 오버로딩은 하지 않고 그냥 원래 그대로 있던 생성자를 사용하되 description 매개변수가 들어가는 자리에 상수값으로 값을 넘겨주었다.
//생성자
public Zerg(String name, int hp, int speed, int damage, int position) {
super(name, hp, speed, damage, position, "Zerg Unit");
}
이렇게 바꿔주었더니 정상작동하였다. 😊
2. 키오스크프로젝트 가변인자
키오스크 프로젝트 level 3를 하던 중이었다. 이제 Main 클래스에서 수행하던 실행흐름을 Kiosk 클래스로 옮겨서 수행해야했다.
키오스크 클래스에서 MenuItem 타입의 객체들을 관리하는 리스트가 필드로 존재해야 한다. 또한 Main클래스에서 키오스크 객체를 생성할 때 값을 받아와서 리스트필드에 값을 할당해야 했다.
필드를 다음과 같이 설정해주고
//필드
private List<MenuItem> menuItems = new ArrayList<>();
이제 키오스크 클래스의 생성자를 만들어야했다.
그런데 메인클래스에서 menuItems 리스트에 메뉴를 추가할때 매번 키오스크 객체를 새로 생성해서(총 메뉴가 4개) 해야할 건 아니다 싶었다.
그래서 생성자에서 매개변수를 여러개 받아와서 한꺼번에 리스트에 값을 add 하고 싶었다. 그러기 위해서는 가변인자로 설정해주어야 한다!
//생성자
public Kiosk(MenuItem ... menuitem) {
for (MenuItem a : menuitem) {
this.menuItems.add(a);
}
}
타입과 변수이름 사이에 ...을 넣어서 여러개의 매개변수가 들어올 수 있도록 하고 for문을 이용해서 들어온 매개변수를 순회하며 리스트에 값을 add 해줬다.
그 후 메인 클래스에서
Kiosk kiosk = new Kiosk(
new MenuItem("random value", 0, "random value"),
new MenuItem("SmokeShack", 8.9, "베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거"),
new MenuItem("Cheeseburger", 6.9, "포테이토 번과 비프패티, 치즈가 토핑된 치즈버거"),
new MenuItem("Hamburger", 5.4, "비프패티를 기반으로 야채가 들어간 기본버거")
);
위와 같이 new 키워드를 이용해 MenuItem 타입의 객체들을 여러개 생성한 후 여러개 전달하였다.
3. 프로세스와 쓰레드
아직 5주차 강의 5-3까지 밖에 듣지 못하여서 프로세스와 쓰레드에 대해서 정확히 모르지만 프로세스는 운영체제로부터 할당받는 실행단위이고 쓰레드는 실행 흐름이다. 자바의 메인쓰레드는 메인메서드에서부터 시작하고 다중쓰레드를 만들 수 있다.
[오늘 회고]
오늘은 중간에 갑자기 세션이 2개가 끼어서 키오스크 프로젝트 level4를 구현하지 못하였다. 내일은 level4를 꼭 구현해야겠다. 그리고 프로세스와 쓰레드를 배우며 생각났는데 학교 때 배운 운영체제를 블로그에 차근차근 정리해보아야겠다. 또 이번 키오스크 프로젝트는 깃에 올릴때 커밋컨벤션을 지켜서 올리도록 해봐야겠다.
내일 할 일
- 키오스크 프로젝트 level5까지 구현 + level 3 유효하지 않은 입력 예외처리(throw 이용)
- 자바 문법 강의 완강
- TIL작성 (자바문법 4주차 내용부터 배운 내용 정리)
'개발 회고 > TIL' 카테고리의 다른 글
[1/16] TIL - 키오스크 프로그램 구현 + 장바구니 기능, static은 왜 쓰면 안될까, 다른 달팽이들은 신경쓰지 말자 (0) | 2025.01.16 |
---|---|
[1/15] TIL - 키오스크 프로그램 구현, 엉덩이 아프다 (1) | 2025.01.15 |
[1/13] TIL - 콘솔 출력 형식 지정, 계산기 코드 개선, 앞으로의 공부 다짐 😊 (0) | 2025.01.13 |
[1/7] TIL - 다형성, 상속, 추상클래스, 인터페이스, 형변환의 필요성 정리 (0) | 2025.01.07 |
[1/6] TIL - 클래스변수 문제 해결, 객체와 클래스, 생성자, 오버로딩, 접근제어자, getter와 setter, this, super, 상속, 오버라이딩 (0) | 2025.01.06 |