| 1. Dependency 2. 프로젝트 설계 3. 프로젝트 구현 |
1. Dependency
구현 시 자주 사용하는 라이브러리 : javafaker, Lombok
(1) javafaker
1) maven 라이브러리 검색 사이트 : https://search.maven.org/
2) Gradle Groovy DSL 부분 코드 복사

3) build.gradle 파일 dependency 설정

(2) Lombok
1) lombok 설치 : https://projectlombok.org/
2) javaFaker와 같은 방법으로 dependency 설정
3) 따로 플러그인을 설치해야 사용 가능
[Files] -> [setting] -> [Plugins] -> [lombok] 플러그인 설치
사용 예시
JavaFaker
자동으로 데이터를 생성해줌
Faker faker = new Faker();
String title = faker.name().fullName();
String title2 = faker.starTrek().character();
String title3 = faker.starTrek().location();
long r = faker.number().randomNumber();
// ...
//randomDigit() : 0~9 까지의 숫자 중에서 출력
Stream.generate(() -> faker.number().randomDigit())
.limit(10)
.forEach(System.out::println);
//1 ~ 9 까지의 숫자를 중복되지 않게 10개 뽑아서 출력
Stream.generate(() -> faker.number().randomDigitNotZero())
.distinct()
.limit(10)
.forEach(System.out::println);
//위의 결과를 객체로 저장
Integer[] nums = Stream.generate(() -> faker.number().randomDigitNotZero())
.distinct()
.limit(10)
.toArray(Integer[]::new);
IntStream nums2 = Stream.generate(() -> faker.number().randomDigitNotZero())
.distinct()
.limit(10)
.mapToInt(i -> Integer.valueOf(i));
Lombok
자주 사용하는 class의 메서드를 제공해주는 애너테이션 제공
/*
@AllArgsConstructor //Lombok : 모든 constructer를 포함하는 생성자를 만들어줘라
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Getter
@Setter
*/
@Data //위에있는 애너테이션은 자주 쓰는 것이기 때문에 Data에서 제공해준다.
public class User {
private int age;
private String name;
}
2. 프로젝트 설계
1) 요구사항 파악하기
- 게임의 룰 이해
프로젝트를 설계할 때 숫자야구 게임을 만드는 것을 예로 든다면, 게임의 규칙을 이해하는 것이 중요하다. 예를 들어, 게임을 실행할 GUI는 어떤것인지, 데이터의 범위는 어떻게 되는지가 게임 규칙의 예시이다.
ex)
- 3자리 숫자 사용 (확장 가능)
- 중복된 숫자를 사용하지 않음
- 1~9 를 사용 (0은 사용하지 않음)
- Console 상에서 동작하는 프로그램을 제작
2) 일을 객체로 나누기/객체를 연관짓기
- 게임이 중심이 되는 BaseBall 객체를 만든다.
- 게임을 실행하려면 첫번째로 랜덤의 숫자를 생성하는 NumberGenerator 객체가 필요하다.
- NumberGenerator가 정답을 만들어내면 loop가 시작되고, 사용자에게 비교값을 입력받기 위한 Input 객체가 있어야 한다.
- 입력값을 받고난 후 1~9에 해당하는 숫자가 입력되어있는지 확인할 parse 메서드가 필요하다.
- 입력값과 정답을 비교할 ballCount 메서드가 필요하고, 비교 후 결과를 출력할 Output 객체가 필요하다.
- BaseBall 입장에서 Input과 Output이 어떤 방식으로 진행되는지 중요하지 않기 때문에 두 객체는 interface로 만들고,
- 게임 룰에 Console에서 실행하는 룰이 있으므로 두객체는 Console에서 구현한다.
- 비교 결과가 3 스트라이크 일 경우에는 정답을 출력하고 아닐 경우에는 다시 비교값을 입력받는다.
이런 흐름에 따라서 생성되는 객체는 BaseBall, NumberGenerator, Input, Output, Console이다.
3) 핵심로직 설계하기

- 게임이 중심이 되는 BaseBall 객체를 만든다.
- 게임을 실행하려면 첫번째로 랜덤의 숫자를 생성하는 NumberGenerator 객체가 필요하다.
- NumberGenerator가 정답을 만들어내면 loop가 시작되고, 사용자에게 비교값을 입력받기 위한 Input 객체가 있어야 한다.
- 입력값을 받고난 후 1~9에 해당하는 숫자가 입력되어있는지 확인할 parse 메서드가 필요하다.
- 입력값과 정답을 비교할 ballCount 메서드가 필요하고, 비교 후 결과를 출력할 Output 객체가 필요하다.
- BaseBall 입장에서 Input과 Output이 어떤 방식으로 진행되는지 중요하지 않기 때문에 두 객체는 interface로 만들고,
- 게임 룰에 Console에서 실행하는 룰이 있으므로 두객체는 Console에서 구현한다.
- 비교 결과가 3 스트라이크 일 경우에는 정답을 출력하고 아닐 경우에는 다시 비교값을 입력받는다.
3. 프로젝트 구현

- 옆의 그림은 최종적으로 완성한 프로젝트의 구조이다.
- engine 패키지 내부는 설계 시 객체로 생성하고자 했던 것들을 포함시킨다.
- 가장 핵심이 되는 BaseBall 클래스에 프로그램을 실행하는데에 꼭 필요한 로직을 설계한다. 가장 먼저 구현하게되는 클래스이다. 다른 객체들에서 필요한 기능들을 미리 명시해 둠으로써 구현해야할 메서드를 미리 생각해 둘 수 있다.
- Runnable 인터페이스를 implement하여 run() 메서드를 통해 프로젝트를 실행하기위한 로직을 구현하고, 이때 필요한 객체를 하나씩 생성한다.
(1) engine 패키지 내부
@Override
public void run() {
Numbers answer = generator.generate(COUNT_OF_NUMBERS);
while(true){
String inputString = input.input("숫자를 맞춰보세요. :");
Optional<Numbers> inputNumbers = parse(inputString);
if(inputNumbers.isEmpty()){
output.inputError();
continue;
}
BallCount bc = ballCount(answer,inputNumbers.get());
output.ballCount(bc);
if(bc.getStrike() == COUNT_OF_NUMBERS){
output.correct();
break;
}
}
}
- 위 코드는 BaseBall 클래스의 run()메서드를 구현한 부분이다.
- 1. 가장 먼저 정답코드 생성을 위한 NumberGenerator 클래스를 만든다.
- 랜덤으로 생성하기 위해서 Faker 라이브러리를 사용한다.
- 하지만 engine 핵심 구현부에서 dependency를 사용하지 않는 편이 좋기 때문에 engine.io 패키지에 NumberGenerator 인터페이스를 생성한 후 이 인터페이스를 implement를 받아 구현부분에서 생성 하도록 한다.
- 1-1) NumberGenerator를 implement한 FakerNumberGenerator 클래스를 생성한다.
- stream() 을 이용해서 count 갯수 만큼의 랜덤한 숫자를 생성한다.
-
public Numbers generate(int count) { return new Numbers( Stream.generate(() -> faker.number().randomDigitNotZero()) .distinct() .limit(count) .toArray(Integer[]::new) ); }
- 2. 생성한 정답 코드를 저장할 Numbers 클래스를 만든다.
- engine.Model 패키지에 클래스를 저장한다.
- Model이 되는 클래스는 따로 모아서 저장
- 3. while문을 통해 무한 루프 시작
- 4. loop 안에서 비교할 값을 입력받고 출력할 Input, Output 클래스를 생성한다.
- engine.io 패키지에 클래스를 저장한다.
- 입출력과 관계있는 클래스는 따로 모아서 저장
- 5. Optional 타입을 응용해서 null값을 가지는 객체를 생성하지 않도록 하고, 입력받은 값을 parse() 메서드를 통해 유효한 값인지 검증한다.
- 6. ballCount() 메서드로 strike와 ball의 갯수를 확인한다.
-
private BallCount ballCount(Numbers answer, Numbers inputNumbers) { AtomicInteger strike = new AtomicInteger(); AtomicInteger ball = new AtomicInteger(); answer.indexdeForEach((a,i) ->{ inputNumbers.indexdeForEach((n,j)->{ if(!a.equals(n)) return; if(i.equals(j)) strike.addAndGet(1); else ball.addAndGet(1); }); }); return new BallCount(strike.get(), ball.get()); } - 이때 주의해야할 점은 int, Integer, AtomicInteger에 대한 내용이다. 나중에 따로 정리하면 좋을 것 같다.
- strike와 ball 갯수를 확인한 값을 저장할 BallCount클래스를 생성한다.
- 마찬가지로 engine.Model 패키지에 클래스를 저장한다.
-
- 7. strike의 갯수가 지정한 처음 지정한 3개라면 correct() 메서드를 실행하여 정답임을 출력한다.
(2) engine 패키지 외부
- 1. 직접 코드를 실행하는 부분인 App을 작성한다. 이부분을 호스트 코드라고 한다.
- engine 패키지 내부와 같은 핵심 부분은 구현코드라고 한다.
-
public class App { public static void main(String[] args) { NumberGenerator generator = new HackFakerNumberGenerator(); Console console = new Console(); new BaseBall(generator, console, console).run(); } } - 위 코드처럼 실행에 필요한 객체들을 선언해주고 바로 실행한다.
- 2. 입출력과 관계있는 Console 클래스를 구현한다.
- Console은 Input, Output 두가지와 관련있기 때문에 두가지 모두 implements 한다.
- 입출력에 필요한 Scanner를 상수로 선언한다.
- 필요한 구현코드를 override 해서 작성한다.
- Console은 Input, Output 두가지와 관련있기 때문에 두가지 모두 implements 한다.
- 3. 이전에 만들어두었던 NumberGenerator를 구현할 FakerNumberGenerator 클래스를 생성하고, 메서드를 구현한다.
이렇게 객체를 나누어가면서 설계한 적은 처음인 것 같다.
그동안은 객체를 생성할 때 class 하나에 필요한 기능을 중복해서 넣어가면서 인터페이스나 상속을 사용하지 않았는데 이런 방식이 굉장히 효율적인 것 같고 이런 흐름으로 프로그램을 설계하는 연습을 좀 더 해서 자연스럽게 나올 수 있었으면 좋겠다.
'TIL' 카테고리의 다른 글
| [TIL] 210810 (0) | 2021.08.10 |
|---|---|
| [TIL] 210809 (0) | 2021.08.10 |
| [TIL] 210805 (0) | 2021.08.06 |
| [TIL] 210804 (0) | 2021.08.05 |
| [TIL] 210803 (0) | 2021.08.05 |