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시간을 헤매다가 알았다 ㅎㅎ