Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a15c29b
2025 02 28 by devny - initial commit
dev-ny Feb 28, 2025
bab8e93
2025 03 04 by devny - Early return
dev-ny Mar 4, 2025
cf602eb
2025 03 04 by devny - 3중 depth 를 해소했다.
dev-ny Mar 4, 2025
15cdced
2025 03 04 by devny - 사용하는 곳 가까운 곳에서 변수를 선언하자!
dev-ny Mar 4, 2025
9d183b7
2025 03 05 by devny - 예외처리 추가
dev-ny Mar 5, 2025
c278d55
2025 03 05 by devny - 의도적인/비의도적인 예외처리 추가
dev-ny Mar 5, 2025
4dd97dd
2025 03 05 by devny - 객체를 이용하여 추상화 진행
dev-ny Mar 5, 2025
efe59b3
2025 03 06 by devny - SRP: 단일 책임 원칙1
dev-ny Mar 6, 2025
7a3bd1e
2025 03 06 by devny - SRP: 단일 책임 원칙2
dev-ny Mar 6, 2025
8389e4f
2025 03 06 by devny - SRP: 단일 책임 원칙3
dev-ny Mar 6, 2025
df16043
2025 03 07 by devny - p10 과 같이 기존의 row, col 범위가 늘어났을때 오동작하는 현상 수정(범위 확장)
dev-ny Mar 7, 2025
0c242d5
2025 03 07 by devny - SRP + OCP(단일책임원칙 + 개방-폐쇄원칙)
dev-ny Mar 7, 2025
7e293f2
2025 03 07 by devny - OCP(개방-폐쇄원칙)
dev-ny Mar 10, 2025
076f6eb
2025 03 10 by devny - LSP(리스코프 치환 원칙)
dev-ny Mar 10, 2025
a440421
2025 03 10 by devny - ISP(인터페이스 분리 원칙)_1
dev-ny Mar 10, 2025
8864b74
2025 03 10 by devny - ISP(인터페이스 분리 원칙)_2
dev-ny Mar 10, 2025
6ca6d2f
2025 03 10 by devny - ISP(인터페이스 분리 원칙)_2
dev-ny Mar 10, 2025
3d874f3
2025 03 10 by devny - DIP(의존성 역전 원칙)
dev-ny Mar 10, 2025
45b06d8
2025 03 10 by devny - DIP(의존성 역전 원칙)
dev-ny Mar 10, 2025
6b0305c
2025 03 11 by devny - DIP(의존성 역전 원칙)와 DI(의존성 주입)과 IoC(제어의 역전)에 대한 설명
dev-ny Mar 11, 2025
2c530ea
2025 03 11 by devny - Cell 의 isFlagged, isOpened 를 별도의 객체로 분리
dev-ny Mar 11, 2025
a712f97
2025 03 11 by devny - Index 로 이루어졌던 좌표를 CellPosition 을 통해 VO 로 만듦.
dev-ny Mar 11, 2025
d8f762e
2025 03 12 by devny - 일급 컬렉션 생성(Cells)
dev-ny Mar 11, 2025
93a373b
2025 03 12 by devny - 일급 컬렉션 생성 및 활용(CellPositions)
dev-ny Mar 12, 2025
d981303
2025 03 12 by devny - Enum의 특성과 활용
dev-ny Mar 12, 2025
a32aa69
2025 03 12 by devny - CellSnapShot.java -> CellSnapshot
dev-ny Mar 12, 2025
ac00cf0
2025 03 12 by devny - 다형성으로 반복되는 if 문 처리하기
dev-ny Mar 12, 2025
100d3ed
2025 03 12 by devny - Enum 을 통해 추가 추상화 진행
dev-ny Mar 12, 2025
c4ca94d
2025 03 12 by devny - mine package 생성 및 studycafe 코드 복사
dev-ny Mar 12, 2025
a2cb8eb
2025 03 12 by devny - DAY7_추상화 레벨맞추기
dev-ny Mar 12, 2025
20c2dfd
2025 03 16 by devny - Enum 으로 추상화 진행 완료
dev-ny Mar 15, 2025
3c7e44f
2025 03 16 by devny - 새로운 도메인 정보 도출
dev-ny Mar 15, 2025
efc5ee4
2025 03 16 by devny - 주석 관련
dev-ny Mar 16, 2025
41a18bf
2025 03 16 by devny - 무한 루프 위험성이 있음. 수정.
dev-ny Mar 16, 2025
a1e760c
2025 03 16 by devny - gameStatus 를 GameBoard 로 이관
dev-ny Mar 16, 2025
0d92db4
2025 03 17 by devny - 변수와 메서드의 나열 순서
dev-ny Mar 16, 2025
c7c093d
2025 03 17 by devny - package 명 변경
dev-ny Mar 16, 2025
2466606
2025 03 18 by devny - test commit
dev-ny Mar 18, 2025
d8e96f3
2025 03 18 by devny - SampleTest.java 에 함수 생성
dev-ny Mar 18, 2025
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
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.gradle
build/
build
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
Expand Down Expand Up @@ -34,3 +35,11 @@ out/

### VS Code ###
.vscode/
### Example user template template
### Example user template

# IntelliJ project files
.idea
*.iml
out
gen
15 changes: 15 additions & 0 deletions src/main/java/cleancode/minesweeper/tobe/AnotherGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cleancode.minesweeper.tobe;

import cleancode.minesweeper.tobe.game.GameRunnable;

public class AnotherGame implements GameRunnable {
// @Override
// public void initialize() {
// // ...필요없는데...
// }

@Override
public void run() {
// 게임 진행
}
}
61 changes: 61 additions & 0 deletions src/main/java/cleancode/minesweeper/tobe/GameApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package cleancode.minesweeper.tobe;

import cleancode.minesweeper.tobe.minesweeper.config.GameConfig;
import cleancode.minesweeper.tobe.minesweeper.gamelevel.Beginner;
import cleancode.minesweeper.tobe.minesweeper.io.ConsoleInputHandler;
import cleancode.minesweeper.tobe.minesweeper.io.ConsoleOutputHandler;

public class GameApplication {

public static void main(String[] args) {

GameConfig gameConfig = new GameConfig(
new Beginner(),
new ConsoleInputHandler(),
new ConsoleOutputHandler()
);

Minesweeper minesweeper = new Minesweeper(gameConfig);
minesweeper.initialize();
minesweeper.run();
}


/**
* DIP (Dependency Inversion Principle)
* 스프링의 3대 요소
* IOC, DI
* PSA, AOP
*
* DI = Depedency Injection
*
* IoC = Inversion of Control
*
* DIP 는 고수준 모듈과 저수준 모듈이 직접적으로 의존하는 것이 아닌 추상화에 서로 의존해야 한다.
* DI 는 의존성을 주입한다. 필요한 의존성을 내가 직접 생성하는 것이 아니라 외부에서 주입받겠다. DI 를 생각하면 떠올라야하는 숫자가 있음. 3
* 객체(A)가 있고, 또 다른 객체(B) 하나가 있음. A 객체가 B 객체를 필요로 함. 이 둘이 의존성을 갖고 싶은데, A가 B를 생성해서 사용하는게 아니라 의존성을 주입받고 싶음.
* 생성자나 다른 메소드를 통해 주입받고자 할때 A와 B 는 주입하는 걸 할 수 있음. 그러니 제 3자가 A와 B의 의존성을 맺어줄 수 밖에 없음.
* Spring 에서는 이걸 "Spring Context"="IOC Container" 가 해줌.
* 이 것들(객체의 결정과 주입)이 Runtime 에 실행됨
*
* DI 와 붙어다니는 IoC
* IoC 는 제어의 역전. Spring 에서만 사용되지 않음. 더 큰 개념
* 프로그램의 흐름을 개발자가 아닌 프레임워크가 담당하도록 하는 것.
* 제어의 순방향 = 프로그램은 개발자가 만드는 것 = 내가 만든 프로그램 개발자가 제어 -> 근데 이 제어 흐름이 역전됐다.
* 내가 만든 프로그램이 미리 만들어진 공장같은 프레임워크가 있고 프레임워크 안에 요소로 내 코드가 들어가서 일부분 톱니바퀴의 하나처럼 동작하는 것.
* 프레임워크는 톱니바퀴만 빠져있는 것. 내가 톱니바퀴만 하나 만들어서 내 애플리케이션이야! 하면서 뾱하고 끼우면 됨.
* 이땐 프레임워크가 메인이 되는 것임. 내 코드는 프레임 워크의 일부가 되면서. 제어가 프레임워크 쪽으로 넘어가는 것
* Spring Framework = 코드를 입력했을때 Spring 이 제공하는 여러가지 기능을 사용하면서 규격에 맞춰서 코딩함 -> 이런 것들이 제어의 역전
*
* 객체의 레벨에서 보면 IoC 컨테이너라는 친구가 객체를 직접적으로 생성해주고 생명주기를 관리해 줌.
* MineSweeper 가 아까는 ConsoleInputHandler 를 class 내에서 생성하고 사용함.
* IoC 컨테이너가 하는 일은. "생성과 소멸은 내가 알아서 해줄게" "객체 자체의 생명주기도 내가 알아서 다 해줄게, 의존성 주입도 DI 로 해줄게"
* "너는 쓰기만 해!!!"
*
* 객체레벨에서도 프로그램 제어권이 IoC 컨테이너라고 하는 친구에게 주도권이 있기 때문에 객체의 생성들 즉,
* Spring 에서는 Spring 이 관리하는 객체들을 Bean 이라고 하는데 Spring 에서는 이 Bean 들을 생성하고 Bean 들끼리 의존성을 주입해주고
* 생명주기를 관리하는 일을 IoC 컨테이너가 하게 됨.
*
* @Component, @Service -> 내가 직접 생성 안함. IoC 컨테이너가 생성해줌.
*/
}
122 changes: 122 additions & 0 deletions src/main/java/cleancode/minesweeper/tobe/Minesweeper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package cleancode.minesweeper.tobe;

import cleancode.minesweeper.tobe.minesweeper.board.GameBoard;
import cleancode.minesweeper.tobe.minesweeper.config.GameConfig;
import cleancode.minesweeper.tobe.minesweeper.exception.GameException;
import cleancode.minesweeper.tobe.game.GameInitializable;
import cleancode.minesweeper.tobe.game.GameRunnable;
import cleancode.minesweeper.tobe.minesweeper.io.BoardIndexConverter;
import cleancode.minesweeper.tobe.minesweeper.io.InputHandler;
import cleancode.minesweeper.tobe.minesweeper.io.OutputHandler;
import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition;
import cleancode.minesweeper.tobe.minesweeper.user.UserAction;

// 모든 지뢰찾기 게임 로직을 여기에 둘 것임
public class Minesweeper implements GameInitializable, GameRunnable {
// 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링
// private static final int BOARD_ROW_SIZE = 8;
// private static final int BOARD_COL_SIZE = 10;
// 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함


private final GameBoard gameBoard;
private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter();
// 입출력에 대한건 여기서!
private final InputHandler inputHandler;
private final OutputHandler outputHandler;

public Minesweeper(GameConfig gameConfig) {
gameBoard = new GameBoard(gameConfig.getGameLevel());
this.inputHandler = gameConfig.getInputHandler();
this.outputHandler = gameConfig.getOutputHandler();
}

@Override
public void initialize() {
gameBoard.initializeGame();
}

@Override
public void run() {
outputHandler.showGameStartCommand();

while (gameBoard.isInProgress()) {
try {
outputHandler.showBoard(gameBoard);

// String cellInput = getCellInputFromUser();
CellPosition cellInput = getCellInputFromUser();
UserAction userAction = getUserActionInputFromUser();

actOnCell(cellInput, userAction);
} catch (GameException e) { // 의도적인 Exception
outputHandler.showExceptionMessage(e);

} catch (Exception e) { // 예상하지 못한 Exception
outputHandler.showSimpleMessage("프로그램에 문제가 생겼습니다.");
// e.printStackTrace(); // 실무에서는 Antipattern 실무에서는 log 시스템에서 log 를 남기고 별도의 조치를 취함
}
}

outputHandler.showBoard(gameBoard); // 마지막 결과값 보여줌

if (gameBoard.isWinStatus()) {
outputHandler.showGameWinningComment();
}
if (gameBoard.isLoseStatus()) {
outputHandler.showGameLosingComment();
}
}

private CellPosition getCellInputFromUser() {
outputHandler.showCommentForSelectingCell();
CellPosition cellPosition = inputHandler.getCellPositionFromUser(); // 이때, index 로서의 기능은 할 수 있게 함.
// 보드 길이에 따른 Position 검증은 GameBoard 에!
// 한군데서 하면 되는데 너무 잘게 쪼개는거 아닌가요? -> 그렇게 느낄 수 있겠지만 책임을 조금 분리!
// 보드가 있는 곳에서 자연스럽게 검증을 해보자!
if (gameBoard.isInvalidCellPosition(cellPosition)) {
throw new GameException("잘못된 좌표를 선택하셨습니다.");
}
return cellPosition;
}

private UserAction getUserActionInputFromUser() {
outputHandler.showCommentFOrUserAction();
return inputHandler.getUserActionFromUser() ;
}

private void actOnCell(CellPosition cellPosition, UserAction userAction) {

if (doesUserChooseToPlantFlag(userAction)) {
gameBoard.flagAt(cellPosition);

return;
}

if (doesUserChooseToOpenCell(userAction)) {
gameBoard.openAt(cellPosition);
}

System.out.println("잘못된 번호를 선택하셨습니다.");
}

private boolean doesUserChooseToPlantFlag(UserAction userAction) {
return userAction == UserAction.FLAG;
}

private boolean doesUserChooseToOpenCell(UserAction userAction) {
return userAction == UserAction.OPEN;
}

// private boolean isAllCellOpened() {
// boolean isAllOpened = true;
// for (int row = 0; row < BOARD_ROW_SIZE; row++) {
// for (int col = 0; col < BOARD_COL_SIZE; col++) {
// if (BOARD[row][col].equals(CLOSED_CELL_SIGN)) { // 네모 박스가 하나라도 있는지 확인
// isAllOpened = false;
// }
// }
// }
// return isAllOpened;
// }
}
Loading