๐Ÿ•น Level 1. ์ž๋™์ฐจ ๊ฒฝ์ฃผ ๋ฏธ์…˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ •๋ฆฌ


์šฐํ…Œ์ฝ” ๋ ˆ๋ฒจ 1 ๋‚œ์ƒ ์ฒ˜์Œ์œผ๋กœ ์ฝ”๋“œ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ›๊ฒŒ ๋˜์—ˆ๋‹ค.
์‚ฌ์‹ค TDD๋„ ์ฒ˜์Œ์ด๊ณ , ๋ˆ„๊ตฐ๊ฐ€ ๋‚ด ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ  ํ”ผ๋“œ๋ฐฑ ๋ฐ›๋Š” ๊ฒƒ ๋˜ํ•œ ์ฒ˜์Œ์ธ๋ฐ
์ด๋Ÿฐ ๊ธฐํšŒ๊ฐ€ ์ •๋ง ๊ฐ์‚ฌํ•˜๋ฉด์„œ๋„ ๋–จ๋ ธ๋˜ ์ฒซ ๋ฆฌ๋ทฐ ์š”์ฒญ์ด์—ˆ๋‹ค ๐Ÿ‘€

์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ›์œผ๋‹ˆ ๋‚ด๊ฐ€ ๋ชจ๋ฅด๋Š” ๋ถ€๋ถ„๊ณผ ๋‚˜์˜ ์‹ค์ˆ˜๋“ค์„ ๋ช…ํ™•ํžˆ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.
๋˜ํ•œ ๋ฆฌ๋ทฐ๋ฅผ ๋ณด๋‹ˆ ๋‚˜์˜ ์ž˜๋ชป๋œ ์ฝ”๋”ฉ ์Šต๊ด€๋„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
๋•๋ถ„์— ๊ณ ๋ฏผํ•˜๊ณ  ๊ณต๋ถ€ํ•ด๋ด์•ผ ํ•  ๊ฒƒ๋“ค์„ ์•Œ ์ˆ˜ ์žˆ์–ด
์ •๋ง๋กœ ๋‚˜์—๊ฒŒ ๋„ˆ๋ฌด๋‚˜๋„ ํฐ ๋„์›€์ด ๋˜์—ˆ๋‹ค ๐Ÿ™‡โ€โ™‚๏ธ

ํ”ผ๋“œ๋ฐฑ ๋ฐ›์€ ๋ถ€๋ถ„์—์„œ ๊ณ„์† ๋จธ๋ฆฟ ์†์— ๋ฐ•๊ธฐ ์œ„ํ•ด
๋ช‡ ๊ฐ€์ง€๋Š” ์ •๋ฆฌํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ด๋ผ ํŒ๋‹จ์ด ๋˜์—ˆ๋‹ค.

๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ํ”ผํ•˜๋ผ

Pattern ๊ฐ์ฒด๋Š” ๋น„์‹ธ๋‹ค

๋‚˜๋Š” ์ง€๊ธˆ๊ป ์˜ฌ๋ฐ”๋ฅธ ์ˆซ์ž์ธ์ง€, ๋ฌธ์ž์ธ์ง€ ๋“ฑ์„ ๊ฒ€์ฆํ•  ๋•Œ
๋Œ€๋ถ€๋ถ„ Pattern ๊ฐ์ฒด๋ฅผ ์ •๋ง ์•„๋ฌด๋ ‡์ง€ ์•Š๊ฒŒ ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์€ ์ €๋ ‡๊ฒŒ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ ์‹œ ํŒจํ„ด ๊ฐ์ฒด๋ฅผ ๋งค๋ฒˆ ์ƒ์„ฑํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด๋ฒˆ์— ์ „๋ฐ˜์ ์ธ ๋ฆฌ๋ทฐ๋“ค์„ ๋ณด๋‹ˆ ์ •๊ทœ์‹์„ ํ™œ์šฉํ•ด ๊ฐ’์„ ๊ฒ€์ฆํ•œ ๋กœ์ง์—๋Š”
โ€œPattern์€ ๋งค์šฐ ๋น„์‹ผ ๊ฐ์ฒด์ด๋‹ค.โ€ ๋ผ๋Š” ๋ฆฌ๋ทฐ๊ฐ€ ๊ผญ ์žˆ์—ˆ๋‹ค.
๋ถ€๋„๋Ÿฝ์ง€๋งŒ ํ”„๋กœ๊ทธ๋žจ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์„ฑ๋Šฅ๊นŒ์ง€ ๊ณ ๋ คํ•˜์ง€๋Š” ๋ชปํ–ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

์ดํŽ™ํ‹ฐ๋ธŒ ์ž๋ฐ” item 6 ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ํ”ผํ•˜๋ผ์™€
์ด ๊ธ€์€ ํŽ˜์–ด์˜ ๋ฆฌ๋ทฐ์— ๋‹ฌ๋ฆฐ ๊ธ€์„ ๋‚˜์˜ ์ฐธ๊ณ ์ž๋ฃŒ๋กœ ์‚ผ์•˜๋‹ค.

Pattern ์ธ์Šคํ„ด์Šค๋Š” 1๋ฒˆ ์‚ฌ์šฉ๋˜๊ณ  ๋ฒ„๋ ค์ ธ์„œ
๊ณง๋ฐ”๋กœ GC์˜ ๋Œ€์ƒ์ด ๋˜์–ด ๋น„์‹ผ ๋น„์šฉ์ด ๋“ ๋‹ค.

์ด๋Ÿฐ Pattern ๊ฐ์ฒด๋Š” ํ•œ ๋ฒˆ ์ƒ์„ฑํ•˜๊ณ  ์žฌํ™œ์šฉ ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ 
์•„๋ž˜์™€ ๊ฐ™์ด ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ์‹œ ๋งค๋ฒˆ ์ƒ์„ฑํ•˜๋Š” ํšจ์œจ์„ฑ ๋–จ์–ด์ง€๋Š” ๋ฐฉ๋ฒ•์„ ๋ฒ„๋ฆฌ๊ณ ,
static ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•ด์ฃผ์—ˆ๋‹ค.

public class StringCalculator {
    private static final Pattern PATTERN = Pattern.compile("//(.)\n(.*)");

static์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ  ๋ช…์‹ฌํ•˜๊ธฐ

๋ถ„๋ช…ํžˆ static์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ์— ๋Œ€ํ•ด์„œ ์ž๋ฐ” ๊ธฐ๋ณธ ๊ฐœ๋…์— ์ •๋ฆฌํ–ˆ์—ˆ๋Š”๋ฐ
๊ตฌํ˜„ ์ค‘ ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ static ๋ณ€์ˆ˜์™€ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.
static ์„ ์‚ฌ์šฉํ•  ๋•Œ โ€œ๋ณ€์ˆ˜๊ฐ€ ์„ ์–ธ๋œ ํด๋ž˜์Šค์˜ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๋ณ€์ˆ˜์ธ๊ฐ€?โ€๋ฅผ ์งˆ๋ฌธํ•˜์ง€ ์•Š์•˜๊ณ ,
โ€œ์–ด๋– ํ•œ ์ธ์Šคํ„ด์Šค์—๋„ ์†ํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์— ๋”ฑ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•จโ€์„ ์—ผ๋‘ํ•ด๋‘์ง€ ์•Š์€ ๋ฌธ์ œ์˜€๋‹ค.

โ€œCars์˜ List<Car> cars๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰๋˜๊ณ  ๋ฐ”๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜์–ด
๋ชจ๋“  ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๊ฐ’์ด์–ด์•ผํ•˜๋Š”๊ฐ€?โ€๋ฅผ ์งˆ๋ฌธํ•˜๋ฉด
์ € ์ฝ”๋“œ๋ฅผ ๋œฏ์–ด ๊ณ ์ณ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋‹จ๋ฒˆ์— ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›๊ณ  ๋ณด๋‹ˆ ์ผ๊ธ‰ ์ปฌ๋ ‰์…˜์„ ๋ณธ ์˜๋„๋Œ€๋กœ ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ ๊ฐ™์•„
์ฃผ์‹  jojoldu๋‹˜์˜ ์ผ๊ธ‰ ์ปฌ๋ ‰์…˜ ๊ธ€์„ ๋‹ค์‹œ ์ •๋…ํ•ด๋ณด์•˜๋‹ค.

์–ธ๊ธ‰ํ•œ ๋ถ€๋ถ„์€ ์ผ๊ธ‰ ์ปฌ๋ ‰์…˜์˜ ๊ฐœ๋…์„ ๋„์ž…ํ•˜๊ณ , ์ถ”๊ฐ€์ ์œผ๋กœ ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๋Œ€๋Œ€์ ์ธ ๋ฆฌํŒฉํ† ๋ง์„ ๊ฑฐ์ณค๋‹ค.

public class Cars {
    private final List<Car> cars;

    private Cars(final List<Car> cars) {
        this.cars = new ArrayList<>(cars);
    }

    public static Cars makeFromCarNames(final List<Name> carNames) {
        List<Car> cars = carNames.stream()
                .map(Car::new)
                .collect(Collectors.toList());
        validateNonDuplicatedNames(cars);
        return new Cars(cars);
    }

๋‚ด๊ฐ€ โ€œstatic์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ  ๋ช…์‹ฌํ•˜๊ธฐโ€๋ฅผ โ€œ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ํ”ผํ•˜๋ผโ€์˜ ํ•˜์œ„์— ๋„ฃ์€ ์ด์œ ๋Š”
static ๋ฉ”์„œ๋“œ๋‚˜, static ์ƒ์ˆ˜๋งŒ ๊ฐ–๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค๋Š” ๊ตณ์ด ์ƒˆ๋กœ ์ธ์Šคํ„ด์Šค๋กœ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
์ฆ‰static์€ ์ƒํƒœ ๊ฐ’์„ ๊ฐ€์ง€์ง€ ์•Š์•„ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
๋•Œ๋ฌธ์— ์ƒ์„ฑ์ž๋ฅผ private ์ ‘๊ทผ์ž๋กœ ๋ง‰์•„ ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ง‰์•„์•ผ ํ•œ๋‹ค.

โž• ๋ฆฌ๋ทฐ์–ด๋‹˜๊ป˜์„œ ์ถ”๊ฐ€์ ์œผ๋กœ static๊ณผ ์ผ๊ธ‰ ์ปฌ๋ ‰์…˜์— ์ฐธ๊ณ ํ• ๋งŒํ•œ ๋„ˆ๋ฌด๋‚˜๋„ ์œ ์ตํ•œ ๊ธ€์„ ์ฃผ์…จ๋‹ค.
(์ถ”๊ฐ€๋กœ ๊นจ๋‹ฌ์€ ๊ฒƒ์„ ์งค๋ง‰ํ•˜๊ฒŒ ๋‚จ๊ธฐ์ž๋ฉด
static์€ ๋‹คํ˜•์„ฑ์„ ์œ„๋ฐ˜ํ•˜๋‹ˆ ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ๋„ ๋ฉ€์–ด์ง€๊ฒŒ ๋œ๋‹ค)
๋ฆฌ๋ทฐ์–ด๋‹˜์˜ ๋ชจ๋“  ์ฝ”๋ฉ˜ํŠธ๊ฐ€ ๋‚ด๊ฒŒ ํ”ผ๊ฐ€๋˜๊ณ  ์‚ด์ด๋œ๋‹ค ๐Ÿ˜ญ๐Ÿ™‡โ€โ™‚๏ธ

Optional์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ

์ฃผ์‹  ๊ธ€์„ ์ฝ์–ด๋ณด๊ณ  ๋ฐ˜ํ™˜๊ฐ’์ด โ€˜์—†์Œโ€™์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์ด ์ฃผ๋ชฉ์ ์ธ
Optional์˜ ์˜๋„์— ๋งž์ง€ ์•Š์€ get() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ ๋ฌธ์ œ์ ์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.
์Šค์Šค๋กœ๋„ Optional์— ๋Œ€ํ•ด ์ •๋ฆฌ ํ–ˆ์—ˆ๋Š”๋ฐ ๊ทธ๋ƒฅ ๋‹จ์ˆœํžˆ ๊ฐœ๋…์„ ์ •๋ฆฌํ•˜๊ธฐ์— ๊ธ‰๊ธ‰ํ–ˆ๋‚˜ ์‹ถ๊ธฐ๋„ ํ–ˆ๋‹ค.

๋ง์”€ํ•ด์ฃผ์‹  ๋Œ€๋กœ Car์— Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ
compareTo()๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•˜์—ฌ map()์—์„œ ์ตœ๋Œ“๊ฐ’์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๋‹ค.

public class Car implements Comparable<Car> {
    private static final String REGEX_ALPHA = "^[a-zA-z]*$";
    private static final String REGEX_KOREAN = "[๊ฐ€-ํžฃ]*$";
    private static final int NAME_LENGTH_LIMIT = 5;
    private static final int MOVE_PIVOT = 4;

    private String name;
    private int position;

    public Car(final String name) {
        validateName(name);
        this.name = name;
    }

    @Override
    public int compareTo(final Car anotherCar) {
        return Integer.compare(this.getPosition(), anotherCar.getPosition());
    }

์ˆ˜์ •ํ•œ ๋ฉ”์†Œ๋“œ

public Car getMaxPositionCar() {
        return cars.stream()
                .max(Car::compareTo)
                .orElseThrow(IllegalStateException::new);
    }

๊ฐ์ฒด์ง€ํ–ฅ์—์„œ get์„ ์ง€์–‘ํ•˜์ž

์ด ๋ถ€๋ถ„์€ ๋ฆฌ๋ทฐ์–ด๋‹˜๊ณผ DM์œผ๋กœ ๋‚˜๋ˆˆ ์ด์•ผ๊ธฐ์—์„œ ๋ช…ํ™•ํžˆ ๊นจ๋‹ซ๊ฒŒ ๋œ ๋ถ€๋ถ„์ธ๋ฐ,
get ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ˆœ๊ฐ„ ๊ฐ์ฒด๊ฐ€ ์–ด๋–ค ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ์™ธ๋ถ€์— ์—ฌ์‹คํžˆ ๋“œ๋Ÿฌ๋‚˜๊ฒŒ ๋œ๋‹ค.
๋ฐ์ดํ„ฐ๊ฐ€ private ์ด์–ด๋„ public get์„ ์‚ฌ์šฉํ•˜๋ฉด ์™ธ๋ถ€์— ์•Œ๋ ค์ง€๋Š” ๊ฒƒ์ด ๋œ๋‹ค.
์œ„ ํ”ผ๋“œ๋ฐฑ ์ฝ”๋“œ์—์„œ๋„ getPosition()์ด position์ด๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ์ด ๋“œ๋Ÿฌ๋‚˜๋‹ˆ
์ž๋™์ฐจ์˜ ์ƒํƒœ๊ฐ€ maxPosition ์ธ์ง€๋ฅผ car ๊ฐ์ฒด์—์„œ ํ™•์ธํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

public boolean isMaxPosition(final Car maxPositionCar) {
        return this.position == maxPositionCar.getPosition();
}

๋˜ ๋ฐ์ผ๋ฆฌ ๋ฏธํŒ… ๋™๊ธฐ๋“ค๊ณผ ์ด์•ผ๊ธฐ๋ฅผ ํ•˜๋‹ค ๊นจ๋‹ซ๊ฒŒ ๋œ ๊ฒƒ์€
๋‚˜๋Š” ์ง€๊ธˆ tryCount๋ผ๋Š” ๊ฐ์ฒด์—์„œ ๊ฐ’์„ ๊บผ๋‚ด for ๋ฌธ์„ ๋Œ๋ ธ๋Š”๋ฐ,
์ด๋Š” while์„ ์‚ฌ์šฉํ•˜์—ฌ tryCount์—์„œ ๊ทธ ์ƒํƒœ๋ฅผ ํŒ๋ณ„ํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.
์ˆ˜์ • ์ „

private void raceByTryCount() {
    for (int i = 0; i < tryCount.getCount(); i++) {
        race();
        RacingCarView.printProgressResult(cars.cars());
    }
}

์ˆ˜์ • ํ›„

private void raceByTryCount() {
    while (tryCount.isRemainCount()) {
        cars.race();
        RacingCarView.printProgressResult(cars.cars());
        tryCount.deductCount();
    }
}

HashSet์œผ๋กœ Wrapper class ์ค‘๋ณต ์ œ๊ฑฐ

Car ํด๋ž˜์Šค์— equals์™€ hashCode๋ฅผ ์ด๋ฏธ Override ํ•ด๋†“์•˜๋Š”๋ฐ ์›ƒ๊ธฐ๊ฒŒ๋„ Name์„ ๊บผ๋‚ด์™€์„œ HashSet์œผ๋กœ ์ค‘๋ณต์„ ์ œ๊ฑฐํ–ˆ๋‹ค. (๋ง๊ทธ๋Œ€๋กœ ์ด๋ฆ„์˜ ์ค‘๋ณต ์ œ๊ฑฐ๋ฅผ ํ•œ๊ฑฐ๋‹ค ใ…Ž)
HashSet์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋จผ์ € ํ•ด๋‹น ๊ฐ์ฒด์˜ hashCode()์™€ equals()๋ฅผ ์‹คํ–‰ํ•ด๋ณธ๋‹ค.
๊ด€๋ จ ๋‚ด์šฉ์€ ๋‚ด๊ฐ€ ์ด์ „์— ์ผ๋˜ ๊ธ€์ด ์žˆ๊ธธ๋ž˜ ๊ทธ ๋ถ€๋ถ„์— ์ถ”๊ฐ€์ ์œผ๋กœ ๋” ์ž‘์„ฑํ–ˆ๋‹ค.

์ด๋ฒˆ ํ”ผ๋“œ๋ฐฑ์œผ๋กœ ๋Š๋‚€ ์ 

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