๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿš€ ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค 6๊ธฐ ์ง€์› ๊ธฐ๋ก

[ํ”„๋ฆฌ์ฝ”์Šค] ํ”„๋ฆฌ์ฝ”์Šค 1์ฃผ์ฐจ ํ›„๊ธฐ (์ˆซ์ž ์•ผ๊ตฌ โšพ๏ธ)

by dev_writer 2023. 10. 31.

ํ•ฉ๊ฒฉ๋„ ๋˜์ง€ ์•Š์•˜์œผ๋ฉด์„œ ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค 6๊ธฐ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒŒ ์กฐ๊ธˆ ์›ƒ๊ธฐ์ง€๋งŒ.. ํ”„๋ฆฌ์ฝ”์Šค๋ฅผ ํ•˜๋ฉด์„œ ์–ด๋–ค ๊ฒƒ๋“ค์„ ๋ฐฐ์› ๊ณ  ๊ณ ๋ฏผํ–ˆ์—ˆ๋Š”์ง€ ๋‚จ๊ธฐ๊ธฐ ์œ„ํ•ด ๋ธ”๋กœ๊ทธ์— ์ ์–ด๋ด…๋‹ˆ๋‹ค.
 
์‚ฌ์‹ค 1์ฃผ์ฐจ๋Š” ์ด๋ฏธ ๋ฒจ๋กœ๊ทธ์— ์ž‘์„ฑํ–ˆ์–ด์„œ, ๊ทธ๋Œ€๋กœ ๋ณต๋ถ™์ด ๋  ๊ฒƒ ๊ฐ™๋„ค์š”.


ํด๋ฆฐ ์ฝ”๋“œ ์ฑ… ๋‚ด์šฉ๋งŒ์ด ๋ฌด์กฐ๊ฑด ์ •๋‹ต์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ๋ง์ž.

์ฝ”๋“œ๋ฅผ ๊ทธ๋ ‡๊ฒŒ ์™„๋ฒฝํžˆ ์ž‘์„ฑํ•˜์ง€ ๋ชปํ•˜๊ธฐ๋„ ํ•ด์„œ ๊ณผ๊ฐํ•œ ๋ง์ผ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ํด๋ฆฐ ์ฝ”๋“œ์—์„œ๋„ ์ด์™€ ๋น„์Šทํ•œ ๋‚ด์šฉ์ด ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ๋„ ์ด ์ฑ…์—์„œ ์ฃผ์žฅํ•˜๋Š” ๊ธฐ๋ฒ• ๋‹ค์ˆ˜๋Š” ๋…ผ์Ÿ์˜ ์—ฌ์ง€๊ฐ€ ์žˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„๋„ ๋ชจ๋“  ๊ธฐ๋ฒ•์— ๋™์˜ํ•˜์ง€ ์•Š์œผ๋ฆฌ๋ผ. ์–ด๋–ค ๊ธฐ๋ฒ•์€ ๊ฒฉ๋ ฌํžˆ ๋ฐ˜๋Œ€ํ•˜๋ฆฌ๋ผ. ๊ทธ๋ž˜๋„ ๊ดœ์ฐฎ๋‹ค. ์šฐ๋ฆฌ ์ƒ๊ฐ์ด ๋ฌด์กฐ๊ฑด ์˜ณ๋‹ค๊ณ  ์ฃผ์žฅํ•  ์˜๋„๋Š” ์—†์œผ๋‹ˆ๊นŒ. ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ํ•œํŽธ์œผ๋กœ ์ด ์ฑ…์€ ์šฐ๋ฆฌ๊ฐ€ ์˜ค๋žซ๋™์•ˆ ๊ณ ๋ฏผํ•˜๊ณ  ์ˆ™๊ณ ํ•œ ๊ตํ›ˆ๊ณผ ๊ธฐ๋ฒ•์„ ๊ถŒ๊ณ ํ•œ๋‹ค. - Clean Code, 17p

๊ฐœ๋ฐœ์„ ํ•˜๋ฉฐ ๋Š๋‚€ ์ ์€, ๊ทธ๊ฐ„ ํด๋ฆฐ ์ฝ”๋“œ ๋“ฑ๊ณผ ๊ฐ™์ด ๋งค์šฐ ๊ถŒ์œ„์ ์ธ ์ฑ…๋“ค์€ ์—ฌ๊ธฐ์— ์ ํžŒ ๋‚ด์šฉ์ด ๋ฌด์กฐ๊ฑด ์ •๋‹ต์ด๋ผ๋Š” ์‹์œผ๋กœ ๊ณต๋ถ€ํ–ˆ์—ˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
 
์ง„์ •ํ•œ ์„ฑ์žฅ์„ ํ•˜๋ ค๋ฉด, ๊ผญ ์ด๊ฒŒ ์ •๋‹ต์ธ ๋‚ด์šฉ์ผ์ง€ ๋˜๋‡Œ์–ด๋ณด๋Š” ์‹œ๊ฐ„์ด ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
 
๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ง€๋‚˜์นœ ํ•จ์ˆ˜ ๋ถ„ํ• ์€ ์˜คํžˆ๋ ค ๊ฐ€๋…์„ฑ์„ ํ•ด์นœ๋‹ค.

ํด๋ฆฐ ์ฝ”๋“œ์—์„œ ์ œ๊ณต๋œ ์ฝ”๋“œ ์ค‘ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

private String render(boolean isSuite) throws Exception {
    this.isSuite = isSuite;
    if (isTestPage())
    includeSetupAndTeardownPages();
    return pageData.getHtml();
}

private boolean isTestPage() throws Exception {
    return pageData.hasAttribute("Test");
}

์ด๋ ‡๊ฒŒ ๋ถ„ํ• ํ•œ ์˜๋„๋Š” ์ดํ•ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ ํ•จ์ˆ˜์˜ ์ถ”์ƒํ™” ์ˆ˜์ค€์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.
 
์ด์™€ ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ, ์ด๋ฒˆ ๋ฏธ์…˜์„ ํ•˜๋ฉฐ ์ €๋Š” ์ฒ˜์Œ์— ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

private void play() {
    int computerNumber = RandomNumber.pickNumber();
    while (true) {
        ...
        int strike = countGameStrike();
        ...
    }
    printGameEnd();
    askResumeInput();
}

private int countGameStrike() {
    return umpire.countStrike();
}

private static void printGameEnd() {
    EndView.end();
}

private static void askResumeInput() {
    AskController.askResumeInput();
}

์ด ์ฝ”๋“œ๋Š” ๊ณผ์—ฐ ์ข‹๊ฒŒ ์ž‘์„ฑ๋œ ์ฝ”๋“œ์ผ๊นŒ์š”?
 
์ด ๋ฐฉ์‹์˜ ๋‹จ์ ์œผ๋กœ๋Š” ํด๋ž˜์Šค์— ๋„ˆ๋ฌด ๋งŽ์€ ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•จ์— ๋”ฐ๋ผ ํ•จ์ˆ˜๊ฐ€ ํ•„์š” ์ด์ƒ์œผ๋กœ ๋งŽ์•„์ ธ, ์˜คํžˆ๋ ค ๊ฐ€๋…์„ฑ์ด ๋ถˆํŽธํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
 
๋”ฐ๋ผ์„œ ์ €๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ”๊พธ์—ˆ์Šต๋‹ˆ๋‹ค.

private void play() {
    int computerNumber = RandomNumber.pickNumber();
    while (true) {
        ...
        int strike = umpire.countStrike();
        ...
    }
    EndView.end();
    AskController.askResumeInput();
}

์ •ํ™•ํžˆ ๋งํ•˜๋ฉด, ๋‹ค๋ฅธ ๊ฐ์ฒด์—๊ฒŒ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด ๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•˜๋Š” ๋ถ€๋ถ„์€ ๊ทธ ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์œผ๋กœ ์ถฉ๋ถ„ํžˆ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ตณ์ด ๋ณ„๋„์˜ ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ์ง€ ์•Š์•„๋„ ๋  ๊ฒƒ ๊ฐ™๋‹ค๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ ธ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์„ ํ•จ์— ์žˆ์–ด ๋‹ค๋ฅธ ๊ฐ์ฒด์—๊ฒŒ ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญ์„ ํ•˜๋Š” ๊ฒƒ์€ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
 
๊ทธ๋Ÿฌ๋‚˜, ํ•จ์ˆ˜ํ™”๋ฅผ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์ฝ”๋“œ ํ‘œํ˜„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ทธ๋•Œ๋Š” ํ•จ์ˆ˜ํ™”๋ฅผ ์ง„ํ–‰ํ•ด๋„ ๊ดœ์ฐฎ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๋˜ ๋‹ค๋ฅธ ํด๋ฆฐ ์ฝ”๋“œ์˜ ์˜ˆ์‹œ๊ฐ€ ๊ทธ ์˜ˆ์ž…๋‹ˆ๋‹ค.

// includeSetupAndTeardownPages ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์•„๋ž˜ ๋„ค ๊ฐœ์˜ ์ž‘์—…์ด ๋‹จ์ถ•๋œ๋‹ค
private void includeSetupAndTeardownPages() throws Exception {
    includeSetupPages();
    includePageContent();
    includeTeardownPages();
    updatePageContent();
}

๋˜ ๋‹ค๋ฅธ ์˜ˆ๋กœ๋Š”, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊ฟ€ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

// BEFORE
public static void assertNumberValue(final String input) {
    if (!isInputValidPositiveNumber(input)) {
        throw new IllegalArgumentException();
    }
}

private static boolean isInputInvalidPositiveNumber(final String input) {
    return input.matches("^[1-9]+$");
}

// AFTER
public static void assertNumberValue(final String input) {
    if (!input.matches("^[1-9]+$")) {
        throw new IllegalArgumentException();
    }
}

ํ•˜์ง€๋งŒ ์•„๋ž˜ ๊ฒฝ์šฐ๋Š” ๋ถ„๋ฆฌํ•  ํ•„์š”๊ฐ€ ์žˆ์–ด ๋ณด์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ๋„˜์–ด ํŠน์ • ์ž‘์—…์„ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.

public static int pickNumber() {
    StringBuilder numberBuilder = new StringBuilder();

    while (!isBuilderEnoughPicked(numberBuilder)) {
        saveNewNumber(numberBuilder);
    }
    ...
}

// ๊ฐ’์ด ๊ฐ™์€์ง€ "๋น„๊ต" ํ–‰์œ„๋ฅผ ํ•œ๋‹ค.
private static boolean isBuilderEnoughPicked(final StringBuilder numberBuilder) {
    return numberBuilder.length() == PLAY_NUMBER_DIGIT.getValue();
}
์–ด๋–จ ๋•Œ ํ•จ์ˆ˜ ๋ถ„ํ• ์„ ํ•ด์•ผ ํ• ๊นŒ์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์€ ๊ณ„์†๋˜์–ด์•ผ ํ•  ๋‚ด์šฉ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋Š” ํ•œ ๊ฐ€์ง€๋งŒ์„ ์ž˜ํ•ด์•ผ ํ•˜๋ฉด์„œ๋„, ๋„ˆ๋ฌด ์ง€๋‚˜์นœ ๋ถ„ํ• ๋กœ ์ธํ•ด ๊ฐ€๋…์„ฑ์„ ํ•ด์น˜๋ฉด ์•ˆ ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‘˜ ์‚ฌ์ด์˜ ์ ์ ˆํ•œ ์กฐํ™”๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์„ ๋•Œ๊นŒ์ง€ ์—ฐ๋งˆํ•ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ง€๋‚˜์นœ ํด๋ž˜์Šค ๋ถ„ํ• ์„ ํ•˜์ง€ ๋ง์ž.

ํด๋ฆฐ ์ฝ”๋“œ๋ฅผ ์ž˜๋ชป ํ•ด์„ํ•ด์„œ์ธ์ง€, ์—ฌ๋Ÿฌ converter๋“ค์„ ๋งŒ๋“ค๊ณค ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ˜• ๋ณ€ํ™˜๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์กฐ์ฐจ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์œผ๋กœ ๋ฐ”๋ผ๋ดค์—ˆ์Šต๋‹ˆ๋‹ค.

public class StringInputConverter {

    public static String[] toArray(final String input) {
        return input.split("");
    }
}

public class IntegerInputConverter {

    public static toString(final int number) {
        return String.valueOf(number);
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋‹ค ๋ณด๋‹ˆ, ์•„๋ž˜์™€ ๊ฐ™์€ ๊ดด์ƒํ•œ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

public class BallRule implements GameRule {

    @Override
    public int calculate(final int hitter, final int pitcher) {
        String[] origin = StringInputConverter.toArray(IntegerInputConverter.toString(hitter));
        String[] test = StringInputConverter.toArray(IntegerInputConverter.toString(pitcher));
        
        boolean[] match = recordMatchedPositions(origin, test);
        ...
    }
}

๋„๋Œ€์ฒด ์ด๊ฒƒ์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด๋†“์€ ๊ฒŒ ์–ด๋–ค ์ด์ ์ด ์žˆ๋Š” ๊ฒƒ์ผ๊นŒ์š”? ์ง€๊ธˆ ๋‹ค์‹œ ๋ณด๋ฉด ์˜คํžˆ๋ ค ์“ธ๋ฐ์—†์ด ์˜์กด์„ฑ์ด ๋Š˜์–ด๋‚œ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
 
๋”ฐ์ง€๊ณ  ๋ณด๋ฉด String์„ String [] ๋ฐฐ์—ด๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ String ๊ฐ์ฒด์—๊ฒŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋ฉฐ, int๋ฅผ String์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ ๋˜ํ•œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์ด๋Ÿฌํ•œ ํด๋ž˜์Šค๋Š” ์ž‘์„ฑํ•˜์ง€ ์•Š๊ธฐ๋กœ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

public class BallRule implements GameRule {

    @Override
    public int calculate(final int hitter, final int pitcher) {
        // ํ‘œํ˜„์ด ์กฐ๊ธˆ ๋น„ํšจ์œจ์ ์ผ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. 
        // ๋งํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์€ ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๋กœ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค์„ ์ ˆ์•ฝํ•˜์ž๋Š” ๊ฒƒ์ด๋‹ˆ ์ฝ”๋“œ ํšจ์œจ์€ ์ฐจ์น˜ํ•ด์ฃผ์„ธ์š”.
        String hitterValue = String.valueOf(hitter);
        String pitcherValue = String.valueOf(pitcher);

        String[] hitterNumbers = hitterValue.split("");
        String[] pitcherNumbers = pitcherValue.split("");
        
        boolean[] match = recordMatchedPositions(hitterNumbers, pitcherNumbers);
        ...
    }
}

๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋ฆ„์„ ์“ฐ์ž.

์ด๋ฆ„ ์ง“๊ธฐ๋Š” ์ง„์งœ ์–ด๋ ค์šด ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
 
์ฒ˜์Œ์—๋Š” ์ˆซ์ž ์•ผ๊ตฌ์™€ ๊ด€๋ จํ•˜์—ฌ ์•ผ๊ตฌ๋‹ค ๋ณด๋‹ˆ, ์‹ฌํŒ ๊ฐ์ฒด ์ด๋ฆ„์„ ์•ผ๊ตฌ + ์‹ฌํŒ์„ ๋‚˜ํƒ€๋‚ด๋Š” Umpire๋ฅผ ์ผ์—ˆ์Šต๋‹ˆ๋‹ค.
 
๊ทธ๋™์•ˆ Referee๋Š” ๋“ค์–ด๋ดค์–ด๋„, Umpire์— ๋Œ€ํ•ด์„œ๋Š” ์ฒ˜์Œ ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ์—๋„ ์‚ฌ์ „์  ์˜๋ฏธ์— ๋” ์ง‘์ค‘ํ•˜๊ธฐ ์œ„ํ•ด Umpire๋กœ ์‚ฌ์šฉํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.
 
๊ทธ๋Ÿฌ๋‹ค ํด๋ฆฐ ์ฝ”๋“œ์— ์žˆ๋Š” ๋‹ค์Œ ๋‚ด์šฉ์„ ๋– ์˜ฌ๋ ธ์Šต๋‹ˆ๋‹ค.

ํ•ด๋ฒ• ์˜์—ญ์—์„œ ๊ฐ€์ ธ์˜จ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋ผ
๋ชจ๋“  ์ด๋ฆ„์„ ๋ฌธ์ œ ์˜์—ญ (๋„๋ฉ”์ธ)์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ์ •์ฑ…์€ ํ˜„๋ช…ํ•˜์ง€ ๋ชปํ•˜๋‹ค. ๊ฐ™์€ ๊ฐœ๋…์„ ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์ดํ•ดํ•˜๋˜ ๋™๋ฃŒ๋“ค์ด ๋งค๋ฒˆ ๊ณ ๊ฐ์—๊ฒŒ ์˜๋ฏธ๋ฅผ ๋ฌผ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด์— ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๋ฉด ํด๋ฆฐ ์ฝ”๋“œ์— ์žˆ๋Š” ๋‚ด์šฉ์ฒ˜๋Ÿผ ๊ฐ™์€ ๊ฐœ๋…์„ ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์ดํ•ดํ•˜์ง€ ์•Š๋„๋ก ๋” ๋ณดํŽธ์ ์ธ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ๊ฒฐ๋ก  ๋‚ด๋ ธ์Šต๋‹ˆ๋‹ค. ํ–ฅํ›„ ์‹ค๋ฌด ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๊ณผ์˜ ํ˜‘์—…์€ ํ•„์ˆ˜์ ์œผ๋กœ ์ด๋ฃจ์–ด์งˆ ํ…๋ฐ, ๊ทธ๋•Œ ๋‹ค๋ฅธ ๋ถ„์ด ์ฝ์„ ๋•Œ ์˜คํ•ด๊ฐ€ ์—†๋„๋ก ํ•ด์•ผ ํ•  ํ…Œ๋‹ˆ๊นŒ์š”.


์›์‹œ๊ฐ’ ํฌ์žฅ ๊ฐ์ฒด์˜ ๊ฒ€์ฆ ๋กœ์ง์€ ์›์‹œ๊ฐ’ ํฌ์žฅ ๊ฐ์ฒด์—๊ฒŒ ๋งก๊ธฐ์ž.

์ด์ „์— ๋ฏธ์…˜์„ ์—ฐ์Šต ์‚ผ์•„ ๋ฏธ๋ฆฌ ํ•ด ๋ณด๋ฉฐ ์›์‹œ๊ฐ’ ํฌ์žฅ์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์—ˆ์Šต๋‹ˆ๋‹ค.
 
์›์‹œ๊ฐ’ ํฌ์žฅ์˜ ์žฅ์  ์ค‘ ํ•˜๋‚˜๋Š” ์ผ๋ฐ˜์ ์ธ ์›์‹œ๊ฐ’์œผ๋กœ ์ผ์„ ๋•Œ๋ณด๋‹ค ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด๋‹น ๊ฐ์ฒด์—์„œ ํ•ด ์ฃผ์–ด, ์‚ฌ์šฉ๋˜๋Š” ํด๋ž˜์Šค (ex: Name๊ณผ User๊ฐ€ ์žˆ๋‹ค๋ฉด User)์—์„œ๋Š” ์›์‹œ๊ฐ’์— ๋Œ€ํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
 
๊ทธ๋Ÿฐ๋ฐ ์ €๋Š” ์ฑ…์ž„์„ ๋ถ„ํ• ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์— ์ง‘์ฐฉํ•ด์„œ, ์ฒ˜์Œ์—๋Š” Validator๋ฅผ ๋งŒ๋“ค๊ณค ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

public class NumberValidator {

    public static void assertInputNumberWithLength(final String input, final int length) {
        assertNumberValue(input);
        aasertDigitLength(input, length);
        assertEachNumberUnique(input);
    }
    
    private static void assertNumberValue(final String input) {
        ...
    }
    ...
}

์‹ค์ œ ์‚ฌ์šฉ์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค. ์ด ๋•Œ๋Š” ์›์‹œ๊ฐ’์„ ํฌ์žฅํ•˜์ง€ ์•Š์•˜์œผ๋ฉฐ (๋งŒ๋“ค์—ˆ์–ด๋„ ๊ฒ€์ฆ์„ Validator์—๊ฒŒ ๋งก๊ฒผ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.), ์ง์ ‘์ ์œผ๋กœ ์ž…๋ ฅ์„ ๋ฐ›์•˜์„ ๋•Œ ๋งค๋ฒˆ ๊ฒ€์ฆ์ด ์ผ์–ด๋‚˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

public class ConsoleInputView implements InputView {

    @Override
    public int readPlayNumber() {
        String number = Console.readLine();
        NumberValidator.assertInputNumberWithLength(number, PLAY_NUMBER_DIGIT.getValue());
        return Integer.parseInt(number);
    }
}

ํ•˜์ง€๋งŒ PlayNumber๋ฅผ ์›์‹œ๊ฐ’ ํฌ์žฅ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ณ , ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ PlayNumber์—์„œ ์ง์ ‘ ํ•˜๋„๋ก ๋ฐ”๊ฟจ์Šต๋‹ˆ๋‹ค.

public class PlayNumber {

    private final int number;
    
    private PlayNumber(final String number) {
        validateNumber(number);
        this.number = Integer.parseInt(number);
    }
}

NumberValidator๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ง์ ‘ ์›์‹œ๊ฐ’ ํฌ์žฅ ๋ฐ ํ•ด๋‹น ๊ฐ์ฒด์—์„œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋„๋ก ํ•˜๋ฉด, ์‘์ง‘๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ„๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
 
์ด๋Š” ์˜ค๋ธŒ์ ํŠธ (Object) ์ฑ…์—๋„ ์žˆ๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๋ฐ€์ ‘ํ•˜๊ฒŒ ์—ฐ๊ด€๋œ ์ž‘์—…๋งŒ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์—ฐ๊ด€์„ฑ ์—†๋Š” ์ž‘์—…์€ ๋‹ค๋ฅธ ๊ฐ์ฒด์—๊ฒŒ ์œ„์ž„ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌ์ผœ ์‘์ง‘๋„ (cohension)๊ฐ€ ๋†’๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์Šค์Šค๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž์œจ์ ์ธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๋ฉด ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ์„๋ฟ๋”๋Ÿฌ ์‘์ง‘๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค.

๊ฐ์ฒด์˜ ์‘์ง‘๋„๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ์ฒด ์Šค์Šค๋กœ ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฑ…์ž„์ ธ์•ผ ํ•œ๋‹ค. - ์˜ค๋ธŒ์ ํŠธ (Object), 26p

๋งŒ์•ฝ ๊ตณ์ด NumberValidator๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด PlayNumber ๊ฐ์ฒด ์ž…์žฅ์—์„œ๋Š” ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ์— ๊ด€ํ•ด ๋‹ค๋ฅธ ๊ฐ์ฒด๊ฐ€ ๊ด€์—ฌํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
 
PlayNumber์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ํ•˜๋ฉด, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ๋” ๋ช…๋ฃŒํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

@Test
void ํ”Œ๋ ˆ์ด_์ˆซ์ž๋Š”_์„ธ์ž๋ฆฌ์—ฌ์•ผ๋งŒ_ํ•œ๋‹ค() {
    Assertions.assertThrows(IllegalArgumentException.class, () -> {
        PlayNumber playNumber = PlayNumber.from("1234");
    });
}

๋งŒ์•ฝ NumberValidator๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด PlayNumber์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ NumberValidator์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ์˜€๊ฒ ์ฃ ?


๊ณต์šฉ ์ƒ์ˆ˜๋Š” ์ „์šฉ ํด๋ž˜์Šค์— ๋‹ด๊ธฐ๋ณด๋‹ค๋Š” Enum์œผ๋กœ ๊ด€๋ฆฌํ•˜์ž.

๊ฐ์ฒด์— private ํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ์ƒ์ˆ˜๋Š” ์ œ์™ธํ•˜๊ณ , ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ƒ์ˆ˜๋“ค์€ ์ƒ์ˆ˜ ์ „์šฉ ํด๋ž˜์Šค (ex: Constants ๋“ฑ)์— ๋‹ด๊ธฐ๋ณด๋‹ค๋Š” Enum์„ ์“ฐ๋Š” ๊ฒƒ์ด ๋” ์ข‹์Œ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ดํŽ™ํ‹ฐ๋ธŒ ์ž๋ฐ”์—๋„ ์žˆ๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.
 
Enum์„ ์‚ฌ์šฉํ•จ์— ๋”ฐ๋ผ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์žฅ์  ๋ฐ ์ด์ „์˜ ๋‹จ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์ •์ˆ˜ ์ƒ์ˆ˜๋Š” ๋ฌธ์ž์—ด๋กœ ์ถœ๋ ฅํ•ด๋„ ์˜๋ฏธ๊ฐ€ ์•„๋‹Œ ๋‹จ์ง€ ์ˆซ์ž๋กœ๋งŒ ๋ณด์ž…๋‹ˆ๋‹ค.
  • ๊ฐ™์€ ์ •์ˆ˜ ์—ด๊ฑฐ ๊ทธ๋ฃน์— ์†ํ•œ ๋ชจ๋“  ์ƒ์ˆ˜๋ฅผ ์ˆœํšŒํ•  ์ˆ˜๋„ ์—†์Šต๋‹ˆ๋‹ค. ๊ฐœ์ˆ˜ ๋˜ํ•œ ํŒŒ์•…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ์—ด๊ฑฐ ํƒ€์ž…์œผ๋กœ ํ•˜๋ฉด ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค. ํŠน์ • ์—ด๊ฑฐ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์˜์กดํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๊ทธ์™€ ๋‹ค๋ฅธ ์—ด๊ฑฐ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ›์•˜์„ ์‹œ ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.
  • ์—ด๊ฑฐ ํƒ€์ž…์œผ๋กœ ํ•˜๋ฉด ์ž„์˜์˜ ๋ฉ”์„œ๋“œ๋‚˜ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ž„์˜์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด์ „์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ–ˆ์—ˆ์ง€๋งŒ,

public class Constants {

    public static final int PLAY_NUMBER_DIGIT = 3;
    public static final int RESTART = 1;
    public static final int END = 2;
}

์ดํ›„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊พธ์—ˆ์Šต๋‹ˆ๋‹ค.

public enum Constant {

    PLAY_NUMBER_DIGIT(3),
    PLAY_WANT(1),
    END_WANT(2);

    private final int value;

    Constant(final int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }
}

Enum์— ๋Œ€ํ•ด์„œ๋Š” ์šฐ์•„ํ•œ ํ˜•์ œ๋“ค ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ ๋“ฑ์„ ๋ณด๋ฉด์„œ ๋” ์ตํ˜€๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


์ปค๋ฐ‹ ์ปจ๋ฒค์…˜ ์ค€์ˆ˜

์ปค๋ฐ‹ ์ปจ๋ฒค์…˜์„ ์ •๋ฆฌํ•˜๋ฉด์„œ ๊ทธ๋™์•ˆ๊ณผ ๋‹ค๋ฅด๊ฒŒ ์˜์‹์ ์œผ๋กœ ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜์„ ์ง€ํ‚ค๋ ค๋Š” ๋…ธ๋ ฅ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฌด์กฐ๊ฑด์ ์œผ๋กœ ๋”ฐ๋ฅด๊ธฐ๋ณด๋‹ค๋Š”, ์ œ ์ƒ๊ฐ์— ๋งž์ถฐ ์ผ๋ถ€ ์ˆ˜์ •ํ•œ ๋ถ€๋ถ„์ด ์žˆ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. (์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฝํž ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ ๋“ฑ)
 
๋ฌผ๋ก  ํŒ€ ์ปจ๋ฒค์…˜์ด ์žˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ์ง€ํ‚ค๋Š” ๊ฒŒ ์ตœ์šฐ์„ ์ด์ง€๋งŒ, ์ผ๋‹จ์€ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ๋ณดํŽธ์ ์œผ๋กœ ์•Œ๊ณ  ์žˆ๋Š” ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜์ด ์Šต๊ด€์— ๋ฐฐ์ด๊ฒŒ ๋œ ๊ฒƒ ๊ฐ™์•„ ์ข‹์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.


๊ฒฐ๋ก 

๊ณตํ†ต์ ์œผ๋กœ ๋Š๋‚€ ๊ฒƒ์€, ์‹ค์ œ ์ง€์›์„ ํ•˜๊ณ  ๋ฏธ์…˜์„ ์Šค์Šค๋กœ ํ•˜๋‹ˆ ์ œ ์ฃผ๊ด€์— ๋งž๊ฒŒ ์ด ์ง€์‹์ด ์™œ ๊ทธ๋Ÿฐ์ง€ ํƒ๊ตฌํ•ด ๋ณด๋Š” ํ›ˆ๋ จ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
 
1์ฃผ ์ฐจ๋งŒ ํ•ด๋„ ์ด๋ ‡๊ฒŒ ๋ฐฐ์šฐ๊ณ  ๋Š๋‚€ ๊ฒŒ ๋งŽ์•˜๋Š”๋ฐ, ๋‚˜๋จธ์ง€ ์ฃผ์ฐจ๋“ค์—๋Š” ์–ด๋–ค ๊ฒƒ๋“ค์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Š”์ง€ ๋“ฑ์— ๋Œ€ํ•ด์„œ๋„ ๊ธฐ๋Œ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
 
ํ•œํŽธ์œผ๋กœ๋Š” ์ƒˆ๋กœ์šด ๋ฏธ์…˜์ด ์ฃผ์–ด์กŒ์„ ๋•Œ ๋˜ ๋˜‘๊ฐ™์€ ์‹ค์ˆ˜๋ฅผ ํ•˜๊ฒŒ ๋˜๋Š” ๊ฑด ์•„๋‹Œ์ง€ ๊ฑฑ์ •์ด ๋“ค๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿด ๋•Œ๋งˆ๋‹ค ์ด๋•Œ ๋Š๋ผ๊ณ  ๋ฐฐ์šด ์ ๋“ค์„ ๋ณต๊ธฐํ•ด ๋ณด๊ณ , ๊ฐ™์€ ์‹ค์ˆ˜๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋„๋ก ๋” ์ ๊ฒ€ํ•ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.
 
ํ–ฅํ›„ ์žˆ์„ ๊ณตํ†ต ํ”ผ๋“œ๋ฐฑ์—์„œ๋„ ์ œ๊ฐ€ ์–ด๋–ค ์ ์„ ๋†“์ณค์—ˆ๋Š”์ง€ ์ ๊ฒ€ํ•ด๋ณด๊ธฐ๋„ ํ•  ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค.


์ตœ์ข… ์ œ์ถœ

์ด๊ณณ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.