일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- explode()
- 정규식
- php
- myshortcut
- 부트캠프후기
- 오류
- Excel
- node.js
- 함수
- oracle
- 현대이지웰java풀스택개발자아카데미6월
- ES6
- react
- DOM
- 노션
- Java
- 멀티캠퍼스it부트캠프
- formula
- JDBC
- jQuery
- dao
- OOP
- strpos()
- 객체지향
- 배열
- error
- DTO
- SQL
- MySQL
- JavaScript
- Today
- Total
코딩짜는 일상
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 6주차 - JAVA 객체지향으로 코드 리팩토링 해보기 본문
📚 서론
이번주는 말씀드렸던대로 연습문제로 제출했던 코드를 객체지향으로 리팩토링해보려고 합니다!
제출 당시에는 빠르게 완성하는 것에 몰두하느라 익숙한 절차지향 스타일로 만들었고
모든 함수를 static void로 해서 전역함수처럼 썼거든요... ㅎㅎㅎ
우선 연습문제 내용부터 살펴볼까요?
- 패키지와 클래스를 아래와 같이 생성하고 필요하다면 추가할 것.
- game_project
- game
- GaBaBo.java
- Guess.java
- info
- AppInfo.java
- start
- AppStart.java
- game
- game_project
- 애플리케이션 실행 시 메뉴 4개가 보이도록 반드시 추가하고 세부내용은 자유롭게 수정할 수 있음.
숫자를 입력해 사용자가 실행할 메뉴를 고를 수 있게 하고 해당 메뉴의 내용을 실행.- 애플리케이션 정보 : 애플리케이션 내용을 출력 후 메뉴로 복귀
- 가위바위보 게임 : 1.가위 2.바위 3.보 를 선택하면 렌덤하게 고른 컴퓨터와 승패를 판단
- 숫자 맞히기 게임 : 1~5 사이 렌덤한 숫자를 입력후 맞췄는지 판단
- 종료
- 에러가 발생할 수 있는 부분은 예외처리 할 것
- 기능별로 클래스를 분리하고 상속이 가능하다면 상속할 것
- 인터페이스를 활용해 메소드를 표준화 할 것
내용이 더 있긴한데 일단 지금 다루려는 객체지향형 리팩토링도 내용이 많아서 요기까지만 하겠습니다...
저는 당시 3번까지밖에 완성하지 못했습니다.
다 만든 후 클래스 분리와 상속을 하려고 했는데 시간이 모잘랐어요...🥹
🎷 Before: 전역함수만으로 작성된 절차지향형 코드
// GaBaBo.java (핵심만)
package game_project.game;
import java.util.Random;
import game_project.start.AppStart; // 전역함수 사용을 위해 임포트
public class GaBaBo {
public static void WinOrLose() { // 승패 확인용 함수
// # 사용자 값 받기
int WLinput = 0;
do {
// 결과 출력용 함수를 별도로 만들어 편의성을 노림
AppStart.msg1("1. 가위 / 2. 바위 / 3. 보");
AppStart.msg2("무얼 내시겠습니까? ");
WLinput = AppStart.getNumFromUser(3, 1);
} while(WLinput == -2);
// ... 승패 로직 ...
}
public static void GababoStart() { // 가위바위보 로직 시작
AppStart.msg1("*********************************************\n"
+ "\t\t가위바위보 게임");
String chk;
do {
WinOrLose(); // 입력값을 확인하여 승패를 출력
chk = AppStart.checkContinue(); // 계속 할지 말지 체크
} while(chk.equals("Y"));
AppStart.msg1("메뉴로 돌아갑니다\n"
+ "=============================================");
}
}
// AppStart.java (핵심만)
package game_project.start;
// ... import 생략 ...
import game_project.info.*;
import game_project.game.*; // 메뉴 선택 시 게임 실행을 위해 직접 임포트
public class AppStart {
// ... 생략 ...
public static void main(String[] args) {
int answer = 0;
do {
showMenu(); // 앱 메뉴 보여주기
answer = getMenuNum(4); // 이동할 앱 메뉴번호를 사용자에게 받기
if(answer == 1) { // 선택한 메뉴에 따라 클래스 실행
AppInfo.printInfo();
} else if(answer == 2) {
GaBaBo.GababoStart();
} else if(answer == 3) {
Guess.GuessStart();
} // ... 생략 ...
} while(answer != 4);
// ... 생략 ...
}
}
저는 처음엔 메인인 AppStart.java에 대부분의 함수를 만들고 다른 클래스에서 끌어썼습니다.
- 메뉴를 출력해서 보여주는 함수
- 숫자만 입력 받는 함수(예외처리)
- y 또는 n을 입력받는 함수(예외처리)
- 사용자에게 계속할지 말지 여부를 묻는 함수
- 사용자에게 입력할 메뉴를 묻는 함수
그리고 각각의 게임 클래스에선 게임 내용에 따라 승패를 확인하는 내용을 함수로 추가해 주었습니다.
🚨 문제점
- 정적 메서드 남발
거의 대부분이 static void 로 이루어져 있습니다.
연습문제에서 요구하는 인터페이스 활용과 상속이 빠져있어서 객체지향이라고 볼 수 없습니다. - AppStart.java의 복잡한 공용함수
AppStart에는 공용함수가 main과 함께 있어서 복잡하고
다른 클래스는 공용함수를 쓰기위해 AppStart를 필수로 인용하게 됩니다. - Game들의 낮은 확장성
게임들은 기본적으로
컴퓨터에게 렌덤한 숫자를 받고
사용자에게 숫자를 입력받고
승패여부를 확인하는 3가지 단계로 이뤄집니다.
비슷한 구성임에도 이것을 각각의 클래스에 별도의 함수로 만들었기에 신규 게임 추가가 다소 번거로워집니다.
🎺 After: 객체지향 핵심 개념을 적용한 코드
전체 코드를 쓰기 보단 지난주 작성한 객체지향 핵심 개념을 기준으로
부분 수정한 코드를 보여드리는 편이 나을 것 같아서 작성했습니다.
1️⃣ 추상화(Abstracition)
추상화의 핵심은 핵심 기능만 보여주어 무엇을 할 수 있는가에 집중하게 하는 것입니다.
애플리케이션에 수록된 가위바위보, 숫자 맞추기 게임은 둘 다 처음엔 게임 룰을 사용자에게 보여주고
사용자가 입력한 숫자와 컴퓨터가 렌덤하게 고른 숫자를 비교해 승패를 확인하는 기능들이 주입니다.
이것을 토대로 공통된 부분을 인터페이스(Interface)로 만들어 핵심 기능만 보여주겠습니다.
package game_project.game;
public interface NumbersGame {
// 게임 실행
public void play();
// 게임 타이틀 출력
public void title();
}
이제 모든 게임들이 play() 메서드를 통해 실행되도록 오버라이드 하면
AppStart 입장에선 다른 복잡한 작업은 생각할 것 없이 메뉴에 맞는 객체를 가져와 play() 메서드만 실행하면 됩니다.
이로써 메뉴에 맞는 객체를 실행한다는 기능에만 집중하게 되었습니다!
2️⃣ 상속(Inheritance) & 캡슐화 (Encapsulation)
상속은 부모 클래스의 특성을 자식 클래스가 물려받아 사용하는 것이 핵심이며
GaBaBo, Guess 게임들이 NumbersGame 인터페이스를 상속하도록 할 것입니다.
그리고 캡슐화의 핵심은 어떻게 접근할 수 있게 하는가 입니다.
사용자가 게임의 로직이 어떻게 이뤄지는지 알게 된다면 승부를 조작할 수도 있겠죠?
그리고 공용 함수의 내용이 어떻게 구성되어있는지 알게 되면 악용하기도 쉬울 것입니다.
이때 필요한 것이 캡슐화이며 private를 써서 이 둘을 클래스 내에서만 접근할 수 있게 제한합니다.
특히 여기서 생성자를 통해 외부에서 AppFunction을 전달받는 이유가 있는데, 바로 코드의 결합도를 낮추기 위해서입니다.
AppFunction에는 Scanner 클래스도 사용하게 되는데 이는 사용이 완료되면 종료하여 메모리를 반환해주는 작업이 필요합니다.
그런데 GaBaBo에서 직접 AppFunction 객체를 만들어서 쓰게되면 사용 후 종료하는 것까지 신경써야겠죠..?
하지만 AppFunction을 외부에서 건네받으면 GaBaBo는 다른건 신경쓸 필요 없이 신나게 사용만 하면 되고
AppFuncrion을 건내준 AppStart에서만 종료를 신경써주면 됩니다!
import game_project.util.AppFunction; // 공용함수 임포트
public class GaBaBo implements NumbersGame {
private AppFunction af;
public GaBaBo(AppFunction af) { // 생성자를 통해 공용함수 받기
this.af = af;
}
@Override
public void play() {
String chk;
title();
do {
gameRound(); // 게임 내용 실행
chk = af.checkContinue(); // 계속 할지 말지 체크
} while(chk.equals("Y"));
}
@Override
public void title() { // 타이틀 출력
af.msg1("*********************************************\n"
+ "\t\t가위바위보 게임");
}
private void gameRound() {
// (생략) 룰 설명하고 사용자에게 값 받기
// (생략) 받은 값과 컴퓨터 렌던값 비교하여 승패 확인
// (생략) 승패 결과 출력
}
}
3️⃣ 다형성(Polymorphism)
다형성은 때에 따라 다르게 사용할 수 있게 하는 것이 핵심입니다.
GaBaBo와 Guess는 NumbersGame을 상속하도록 하였기에
부모인 NumbersGame를 타입으로 하는 변수games를 만들고
메뉴에 따라 new연산자로 자식 클래스 객체를 생성해 넣어줄 것입니다.
그러면 동일하게 games.play(); 를 이용해 각각 다른 게임을 실행할 수 있게 됩니다.
// AppStart.java
// ... 임포트 생략 ...
public static void main(String[] args) {
// ... 초기화 및 appIO 설정 ...
NumbersGame games = null; // NumbersGame 타입의 변수 선언
do {
// (생략) 메뉴 출력
answer = af.getMenuNum(4); // 이동할 메뉴번호 사용자에게 받기
if (answer == 1) { // 1. 애플리케이션 정보
AppInfo.printInfo();
} else if (answer == 2) { // 2. 가위바위보 게임
games = new GaBaBo(af); // GaBaBo 객체 생성 후 games 변수에 할당
games.play();
} else if (answer == 3) { // 3. 숫자 맞히기 게임
games = new Guess(af); // Guess 객체 생성 후 games 변수에 할당
games.play();
} else if (answer == 4) { // 4. 종료
// ... 종료 메시지 ...
break;
} else {
// ... 예외 ...
}
} while (true);
af.closeScanner();
}
🔥 결론
객체지향적으로 코딩을 하면서 확실히 각 파일마다 필요한 부분별로 그룹화도 잘 되고
중요한 핵심 부분은 private로 숨겨주면서 보안적인 면도 든든해진다는 느낌을 받았습니다.
확실히 코드가 조금 더 읽기 편해지네요!
사실 처음엔 interface에 private void gameRound를 선언해주려고 했는데
이게 불가능하다는 것도 리팩토링 하면서 알게 되었습니다;
역시 어렵다고 익숙한거만 쓰기 보단 원래 쓰던 방식도
객체지향으로 바꿔보는 쪽이 확실히 공부가 잘 되긴 하네요!
'TIL' 카테고리의 다른 글
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 7주차 - JDBC를 이용한 JAVA의 데이터베이스 접근과 DTO, DAO를 도입하기 위한 구조도 (2) | 2025.08.26 |
---|---|
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 30 & 31 & 32 & 33일차 - Oracle, Java (1) | 2025.08.26 |
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 5주차 - JAVA 객체지향 프로그래밍 (5) | 2025.08.12 |
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 27 & 28 & 29일차 - Oracle (3) | 2025.08.12 |
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 4주차 - 깃허브 프로필 꾸미기 (4) | 2025.08.05 |