백엔드 공부진행도/연습 코드
HamburgerPOJO
myeongjaechoi
2025. 3. 2. 21:40
https://github.com/myeongjaeking/HamburgerKioskPOJO
GitHub - myeongjaeking/HamburgerKioskPOJO
Contribute to myeongjaeking/HamburgerKioskPOJO development by creating an account on GitHub.
github.com
package kiosk;
import io.Input;
import manager.Manager;
import member.Member;
import kiosk.validator.KioskErrorMessage;
import root.Root;
public class Kiosk {
private final Manager manager = new Manager();
private final Member member = new Member();
public Kiosk(){
}
public void start(){
while (true){
showKiosk();
String input = Input.nextLine();
if(!selectKiosk(input)){
break;
}
}
}
private void showKiosk(){
Root[] values = Root.values();
for (Root value : values) {
System.out.println(value.getRoot());
}
}
private boolean selectKiosk(String input){
try {
int index = Integer.parseInt(input);
Root selected = Root.values()[index];
if(selected == Root.EXIT){
return false;
}
selected.apply(manager);
}catch (ArrayIndexOutOfBoundsException | NumberFormatException e){
System.out.println(KioskErrorMessage.NOT_FIT_FORMAT_KIOSK.getMessage());
}
return true;
}
}
package root;
import io.Input;
import manager.Manager;
import member.Member;
public enum Root {
EXIT("0. 종료"){
public void apply(Manager manager){
}
},
CREATE_MANGER("1. 관리자 생성"){
public void apply(Manager manager){
String input = Input.nextLine();
try {
String[] name = input.split(",");
manager.create(name[0].trim(),Integer.parseInt(name[1].trim()));
System.out.println(manager.getName()+ manager.getMoney());
}catch (IllegalArgumentException e){
System.out.println("실패");
}
}
},
CONNECT_MANGER("2. 관리자 접속"){
public void apply(Manager manager){
}
},
CREATE_MEMBER("3. 회원 생성"){
public void apply(Manager manager){
}
},
CONNECT_MEMBER("4. 회원 접속"){
public void apply(Manager manager){
}
};
private final String root;
Root(String root){
this.root = root;
}
public String getRoot(){
return root;
}
abstract public void apply(Manager manager);
}
여기서 apply 말고 더 잘 만들 수 있을 거 같은데 고민이 된다. 어떻게 해야 최대한 SOLID와 OOP를 준수하며 코드를 짤 수 있을까..
현재
package root;
import manager.CreateManager;
import manager.Manager;
public enum Root {
EXIT("0. 종료"),
CREATE_MANGER("1. 관리자 생성",new CreateManager()),
CONNECT_MANGER("2. 관리자 접속"),
CREATE_CONSUMER("3. 회원 생성"),
CONNECT_CONSUMER("4. 회원 접속");
private final String root;
private final RootProcess rootProcess;
Root(String root, RootProcess rootProcess){
this.root = root;
this.rootProcess = rootProcess;
}
public String getRoot(){
return root;
}
public void process(Manager manager){
rootProcess.start(manager);
}
}
이렇게 변경하였다. 전 보다는 더 나아졌지만, process 메서드에서 CONSUMER를 추가하면 OCP에 위배된다. 그래서 현재 고민중이다.
package root;
import consumer.Consumer;
import manager.Manager;
public record RootDto(Manager manager, Consumer consumer) {
}
그래서 Dto로 작성하였다.
package kiosk;
import consumer.Consumer;
import io.Input;
import manager.Manager;
import kiosk.validator.KioskErrorMessage;
import root.Root;
import root.RootDto;
public class Kiosk {
private final Manager manager = new Manager();
private final Consumer consumer = new Consumer();
public Kiosk() {
}
public void start() {
while (true) {
showKiosk();
String input = Input.nextLine();
if (!selectKiosk(input)) {
break;
}
}
}
private void showKiosk() {
Root[] values = Root.values();
for (Root value : values) {
System.out.println(value.getRoot());
}
}
private boolean selectKiosk(String input) {
try {
int index = Integer.parseInt(input);
Root selectedRoot = Root.values()[index];
if (selectedRoot == Root.EXIT) {
return false;
}
RootDto rootDto = new RootDto(manager, consumer);
selectedRoot.process(rootDto);
} catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
System.out.println(KioskErrorMessage.NOT_FIT_FORMAT_KIOSK.getMessage());
}
return true;
}
}
DTO를 넘겨줘서 사용자가 입력한 메뉴를 실행하는 로직으로 구성하였다.
package manager.process;
import io.Input;
import manager.Manager;
import manager.validator.ManagerErrorMessage;
import manager.validator.ManagerValidator;
import root.RootDto;
import root.RootProcess;
import util.Separator;
public class CreateManager implements RootProcess {
@Override
public void start(RootDto rootDto) {
System.out.println("관리자의 정보를 입력해 주세요 ex) 관리자1, 10000");
String input = Input.nextLine();
try {
String[] nameAndMoney = input.split(Separator.REST.getSign());
ManagerValidator.createValidation(nameAndMoney);
String name = nameAndMoney[0].trim();
int money = Integer.parseInt(nameAndMoney[1].trim());
Manager manager = rootDto.manager();
manager.create(name, money);
} catch (IllegalArgumentException e) {
System.out.println(ManagerErrorMessage.NOT_FIT_FORMAT_CREATE_MANAGER.getMessage());
}
}
}
이젠 테스트코드로 넘어갔다.
class CreateManagerTest {
Manager manager = new Manager();
private void prepare(String input) {
String[] nameAndMoney = input.split(Separator.REST.getSign());
ManagerValidator.createValidation(nameAndMoney);
String name = nameAndMoney[0].trim();
int money = Integer.parseInt(nameAndMoney[1].trim());
manager.create(name, money);
}
@Test
@DisplayName("정상적인 입력이면 관리자 생성 성공한다.")
void testSuccessCreateManager() {
String input = "관리자,1";
prepare(input);
Assertions.assertEquals(manager.getName(), "관리자");
Assertions.assertEquals(manager.getMoney(), 1);
}
@Test
@DisplayName("공백이 있어도 관리자 생성 성공한다.")
void testBlankInputCreateManager() {
String input = " 관리자, 1";
prepare(input);
Assertions.assertEquals(manager.getName(), "관리자");
Assertions.assertEquals(manager.getMoney(), 1);
}
@Test
@DisplayName("돈이 음수일 때 관리자 생성 시 에러를 발생시킨다.")
void testValidateNegative() {
String input = " 관리자1, -1";
assertThatThrownBy(() -> prepare(input))
.isInstanceOf(NumberFormatException.class)
.hasMessage(ManagerErrorMessage.INVALID_NEGATIVE_NUMBER.getMessage());
}
@Test
@DisplayName("입력 문자열에 구분자가 2개 이상이면 에러를 발생시킨다.")
void testValidateInputLength() {
String input = " 관리자1,1,3";
assertThatThrownBy(() -> prepare(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(ManagerErrorMessage.NOT_FIT_FORMAT_CREATE_MANAGER.getMessage());
}
@Test
@DisplayName("입력 문자열에서 ,기준 뒷 문자열이 숫자로 변환 불가능하면 에러를 발생시킨다.")
void testValidateNumberFormat() {
String input = " 관리자1, a";
assertThatThrownBy(() -> prepare(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(ManagerErrorMessage.NOT_FIT_FORMAT_CREATE_MANAGER.getMessage());
}
@Test
@DisplayName("입력 문자열에서 ,기준 뒷 문자열이 소숫점이 존재하면 에러를 발생시킨다.")
void testHaveDecimalPoint() {
String input = " 관리자1, 10.0";
assertThatThrownBy(() -> prepare(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(ManagerErrorMessage.NOT_FIT_FORMAT_CREATE_MANAGER.getMessage());
}
@Test
@DisplayName("입력 문자열에서 , 이름만 가질 경우 에러를 발생시킨다.")
void testHaveOnlyName() {
String input = " 관리자1";
assertThatThrownBy(() -> prepare(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(ManagerErrorMessage.NOT_FIT_FORMAT_CREATE_MANAGER.getMessage());
}
@Test
@DisplayName("입력 문자열에서 , 돈만 가질 경우 에러를 발생시킨다.")
void testHaveOnlyMoney() {
String input = " 3";
assertThatThrownBy(() -> prepare(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(ManagerErrorMessage.NOT_FIT_FORMAT_CREATE_MANAGER.getMessage());
}
}
모두 잘 통과하는 것을 확인할 수 있었다.
assertThatThrownBy() 쓰려고 build.gradle 생성해줬는데, gradle은 src/main/java 디렉토리 구조로 이루어져야 한다는 것을 2시간을 헤매다가 알았다 ㅎㅎ