๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๊ฐœ๋ฐœ/๋Ÿฐ์„ธ๊ถŒ

๋„๋ฉ”์ธ ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง ๊ด€๋ จ ๊ณ ๋ฏผ

๊ธ€์„ ์ฝ๊ธฐ ์ „

ํ˜„์žฌ ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค์—์„œ ์ง„ํ–‰ํ•˜๊ณ ์žˆ๋Š” '๋Ÿฐ์„ธ๊ถŒ' ํ”„๋กœ์ ํŠธ์—์„œ ์ƒ์„ฑ ๋กœ์ง์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ• ์ง€ ๊ณ ๋ฏผํ•˜๋Š” ๊ณผ์ •์„ ๋‹ด์€ ๊ธ€์ด๋‹ค.

๊ธ€์„ ์ฝ๊ธฐ ์ „์— ์ดํ•ด๋ฅผ ๋•๊ธฐ์œ„ํ•ด ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•˜๊ฒŒ ์†Œ๊ฐœํ•˜์ž๋ฉด, ํ˜„์žฌ ๋Ÿฐ์„ธ๊ถŒ์€ ์ฃผ๋ณ€ ๋Ÿฌ๋‹ ์ฝ”์Šค๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์ด๋ฉฐ ์ด๋Ÿฌํ•œ ๋Ÿฌ๋‹ ์ฝ”์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋„๋ฉ”์ธ ๊ฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด Course, Segment, GeoLine, Coordinate๊ฐ€ ์žˆ๋‹ค.

`Course`๋Š” ๋Ÿฌ๋‹ ์ฝ”์Šค๋ฅผ ์˜๋ฏธํ•˜๊ณ , `List<Segment>`๋ฅผ ๊ฐ€์ง„๋‹ค. ๊ทธ๋ฆฌ๊ณ  `Segment`๋Š” ๊ฒฝ์‚ฌ๋„์— ๊ด€๋ จํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋“ค์ด๊ณ , ์ด๋“ค์€ ๋‹ค์‹œ  `List<GeoLine>`์„ ๊ฐ–๋Š”๋‹ค. `GeoLine`์€ ์ง€ํ‘œ๋ฉด์˜ ์–ด๋А ํ•œ ์„ ์„ ๊ฐœ๋…์ ์œผ๋กœ ํ‘œํ˜„ํ•œ ๊ฒƒ์ด๋ฉฐ, 2๊ฐœ์˜ `Coordinate`๋ฅผ ๊ฐ€์ง„๋‹ค.

 


์ƒ์„ฑ ๊ด€๋ จ ์ฑ…์ž„์„ ์–ด๋–ป๊ฒŒ ํ• ๊นŒ?

public Course(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    this.id = null;
    this.name = new CourseName(name);
    this.roadType = roadType;
    List<Coordinate> coordinates = CoordinateBuilder.fromRawCoordinates(rawCoordinates)
            .removeSimilar()
            .smooth()
            .build();
    List<GeoLine> geoLines = GeoLineBuilder.fromCoordinates(coordinates)
            .build();
    this.segments = SegmentBuilder.fromGeoLines(geoLines)
            .mergeSameElevationDirection()
            .mergeSameInclineType()
            .build();
    this.length = calculateLength(segments);
    this.inclineSummary = InclineSummary.of(segments);
    this.difficulty = Difficulty.fromLengthAndRoadType(length(), roadType);
}


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

ํ•˜์ง€๋งŒ, ํ˜„์žฌ ์ƒ์„ฑ ์ฑ…์ž„์ด ๋„ˆ๋ฌด ํ˜ผ์žฌํ•ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ฆ.

๊ทธ๋ฆฌ๊ณ  Course์—์„œ๋Š” ์‚ฌ์‹ค Segment์˜ ์กด์žฌ๋งŒ ์•Œ๋ฉด ๋˜๋Š”๋ฐ Coordinate, GeoLine์˜ ์กด์žฌ๋„ ์•Œ์•„์•ผํ•œ๋‹ค. ์‹ฌ์ง€์–ด ์ด๋“ค์„ ์ƒ์„ฑํ•˜๋Š”๊ฒŒ CoordinateBuilder, GeoLineBuilder์ž„์„ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค. SegmentBuilder, CoordinateBiuilder์˜ ๊ฒฝ์šฐ์—๋Š” ๋ฉ”์„œ๋“œ์˜ ํ˜ธ์ถœ ์ˆœ์„œ๋„ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

Course ์ƒ์„ฑ์ž์—์„œ ๊ฐ–๋Š” ์ฑ…์ž„์ด ๋„ˆ๋ฌด ๊ฑฐ๋Œ€ํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

 


Builder ํŒจํ„ด์— ๋Œ€ํ•œ ์ƒ๊ฐ
Builder ํŒจํ„ด์˜ ๋ชฉ์  ์ž์ฒด๋Š” ๊ฐ์ฒด์˜ ๋ณต์žกํ•œ ์ƒ์„ฑ ๊ณผ์ • ๋ฐ ๋งŽ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ๋งคํ•‘ํ•˜๋Š” ๊ณผ์ •๋“ค์„ ๋‹จ์ˆœํ™” ํ•˜๊ธฐ ์œ„ํ•œ ํŒจํ„ด์ž„์„ ์•ˆ๋‹ค.

์šฐ๋ฆฌ ๋„๋ฉ”์ธ ๋กœ์ง์ƒ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐฉ์‹์ด ๋ณต์žกํ•˜๋‹ค. โœ… ์ด๊ฑด ์ธ์ •

๊ทผ๋ฐ Builder ํŒจํ„ด์˜ ๊ถ๊ทน์ ์ธ ๋ชฉํ‘œ๋Š” ‘์„ ํƒ์ ์ธ’ ์ƒ์„ฑ(์œ ์—ฐํ•œ ์กฐํ•ฉ)์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ์‹œ๋กœ ์‹์‚ฌ ์ผ์ •ํ‘œ๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ํ•˜๋ฉด ๋ˆ„๊ตฐ๊ฐ€๋Š” ์ ์‹ฌ ์‹์‚ฌ, ์ €๋… ์‹์‚ฌ๋งŒ ์ฃผ์ž…ํ•˜์—ฌ ์ƒ์„ฑํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ๊ณ , ๋ˆ„๊ตฐ๊ฐ€๋Š” ์•„์นจ, ์ ์‹ฌ, ์ €๋… ์‹์‚ฌ ๋ชจ๋‘ ๋„ฃ์–ด ์ƒ์„ฑํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฐ ๊ด€์ ์—์„œ ์šฐ๋ฆฌ ๋„๋ฉ”์ธ์€ Builder ํŒจํ„ด์˜ ‘์„ ํƒ์ ์ธ’ ์ƒ์„ฑ์ด ํ•„์š”ํ•œ๊ฐ€? ์ด ๋ถ€๋ถ„์€ โŒ

ํ•ญ์ƒ ๊ฐ™์€ ์ˆœ์„œ์™€ ํ˜•์‹์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์Œ. ๊ทธ๋Ÿผ Builder ํŒจํ„ด์˜ ๋„ค์ด๋ฐ์„ ๋”ฐ๋ผ๊ฐˆ ํ•„์š”๊ฐ€ ์žˆ๋‚˜?

๊ธฐ์กด ์ƒ์„ฑ์ž ๋กœ์ง์„ ๋‹จ์ˆœํ™” ํ•˜๊ณ  ์ด์— ๋Œ€ํ•œ ์ƒ์„ฑ ์ฑ…์ž„์€ Factory, Creator ๋“ฑ.. (๋„ค์ด๋ฐ์€ ์ž์œ )๋กœ ๋„˜๊ฒจ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜์ž!

 


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


ํŒจํ„ด์— ๋„ˆ๋ฌด ๋งค๋ชฐ๋˜์ง€ ๋ง์ž
๊ฒฐ๊ตญ ๋‚ด๊ฐ€ ํ•˜๊ณ  ์‹ถ์€๊ฑด ์ƒ์„ฑ์ž ๋กœ์ง ๋‹จ์ˆœํ™” + ์ƒ์„ฑ ์ฑ…์ž„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ

 


๊ฐœ์„ ํ•˜๊ฒŒ ๋œ ๊ฒƒ

public Course(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    this.id = null;
    this.name = new CourseName(name);
    this.roadType = roadType;
    List<Coordinate> coordinates = CoordinateBuilder.fromRawCoordinates(rawCoordinates)
            .removeSimilar()
            .smooth()
            .build();
    List<GeoLine> geoLines = GeoLineBuilder.fromCoordinates(coordinates)
            .build();
    this.segments = SegmentBuilder.fromGeoLines(geoLines)
            .mergeSameElevationDirection()
            .mergeSameInclineType()
            .build();
    this.length = calculateLength(segments);
    this.inclineSummary = InclineSummary.of(segments);
    this.difficulty = Difficulty.fromLengthAndRoadType(length(), roadType);
}

 

๊ธฐ์กด์—๋Š” Course๊ฐ€ segments๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ coordinates๋„ ๋งŒ๋“ค๊ณ , geoLines๋„ ๋งŒ๋“ค๊ณ  ์žˆ์—ˆ์Œ. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๊ฐ์ฒด๋“ค์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์ƒ์„ฑ ์ˆœ์„œ๊นŒ์ง€ ๊ธฐ์–ตํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ, Course๋Š” ์ด๋Ÿฌํ•œ ์‚ฌ์‹ค์„ ์•Œ๊ณ  ์žˆ์„ ํ•„์š”๊ฐ€ ์—†๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ด๋Ÿฌํ•œ ์ƒ์„ฑ ๋กœ์ง๋“ค์„ ๋” ์ถ”์ƒํ™” ํ•˜๋Š”๊ฒŒ ๋งž์ง€ ์•Š์„๊นŒ?

 

public Course(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    this.id = null;
    this.name = new CourseName(name);
    this.roadType = roadType;
    **this.segments = SegmentFactory.create(rawCoordinates);**
    this.length = calculateLength(segments);
    this.inclineSummary = InclineSummary.of(segments);
    this.difficulty = Difficulty.fromLengthAndRoadType(length(), roadType);
}

public class SegmentFactory {

    public static List<Segment> create(List<Coordinate> rawCoordinates) {
        List<Segment> rawSegments = GeoLineFactory.create(rawCoordinates).stream()
                .map(GeoLine::toSegment)
                .toList();

        List<Segment> mergedSegments = mergeSameElevationDirection(rawSegments);

        return mergeSameInclineType(mergedSegments);
    }

    private static List<Segment> mergeSameElevationDirection(List<Segment> segments) {
        // ...
    }

    private static List<Segment> mergeSameInclineType(List<Segment> segments) {
        // ...
    }
}

public class GeoLineFactory {

    public static List<GeoLine> create(List<Coordinate> rawCoordinates) {
        List<Coordinate> coordinates = CoordinateFactory.create(rawCoordinates);

        // ...

        return lines;
    }
}

public class CoordinateFactory {

    public static List<Coordinate> create(List<Coordinate> rawCoordinates) {
        List<Coordinate> nonSimilarCoordinates = removeSimilar(rawCoordinates);
        List<Coordinate> coordinates = smooth(nonSimilarCoordinates);

        validateValidCoordinateSize(coordinates);
        return coordinates;
    }

    private static void validateValidCoordinateSize(List<Coordinate> rawCoordinates) {
        if (rawCoordinates.size() < 2) {
            throw INVALID_COORDINATE_COUNT.create(rawCoordinates.size());
        }
    }

    private static List<Coordinate> removeSimilar(List<Coordinate> rawCoordinates) {
        // ...
    }

    private static List<Coordinate> smooth(List<Coordinate> rawCoordinates) {
        // ...
    }
}


๊ทธ๋ž˜์„œ Builder ๋„ค์ด๋ฐ์„ ์ œ๊ฑฐํ•˜๊ณ  Factory๋กœ ๋ณ€๊ฒฝํ–ˆ์œผ๋ฉฐ ๊ฐ ์ƒ์„ฑ ๋กœ์ง๋“ค์„ ์ถ”์ƒํ™” ํ–ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ Course์—์„œ๋Š” ๋” ์ด์ƒ Coordinate, GeoLine, Segment์˜ ์ƒ์„ฑ ๋ฐฉ์‹์„ ๊ถ๊ธˆํ•ด ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ Factory ๊ฐ์ฒด๋“ค์ด Course → Segment → GeoLine → Coordinate๋กœ ๋งŒ๋“ค์–ด์ง„ ๋„๋ฉ”์ธ ๊ตฌ์กฐ์™€ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ์‰ฌ์›Œ์กŒ๋‹ค.

์•„์‰ฌ์šด ์ ์€ Factory๋ผ๋Š” ๋„ค์ด๋ฐ์ธ๋ฐ, ์ด๋ฅผ ํŒ€ ๋‚ด์—์„œ ํ•ฉ์˜ํ•œ๋‹ค๋ฉด ํ•ด๋‹น ๋„ค์ด๋ฐ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์•„๋‹ˆ๋ผ๋ฉด ๋‹ค๋ฅธ ๋„ค์ด๋ฐ์„ ๊ณ ๋ฏผํ•ด๋ณด๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

 


์ƒ์„ฑ ๋กœ์ง์— ๋Œ€ํ•œ ๊ณ ๋ฏผ

 


ํ˜„์žฌ ๋„๋ฉ”์ธ์€ `Course` → `Segment` → `GeoLine` → `Coordinate`์˜ ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค์–ด์ ธ์žˆ๋‹ค.

`GeoLine`, `Coordinate`๋“ค์€ `Course`์™€ ์„ฑ์งˆ์ด ๋‹ค๋ฅด๋‹ค. ์ขŒํ‘œ๋Š” ๋ถ€๋“œ๋Ÿฌ์šธ ํ•„์š”๊ฐ€ ์—†๊ณ , ์ขŒํ‘œ๋“ค ๊ฐ„์˜ ๊ฑฐ๋ฆฌ๊ฐ€ ๋จผ ๊ฒƒ๋„ ์ƒ๊ด€์—†๋‹ค. ์„ธ๊ทธ๋จผํŠธ๋“ค์€ ๊ฒฝํ–ฅ์„ฑ์ด ๊ฐ™๋‹ค๊ณ  ํ•ฉ์ณ์งˆ ํ•„์š”๋„ ์—†๋‹ค. ‘์ฝ”์Šค’๋ผ๋Š” ๋ฒ”์œ„ ์•ˆ์—์„œ ์ดํ•ดํ•  ๋•Œ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์€ ๋งž๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ์ฝ”์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์ขŒํ‘œ๋“ค๋งŒ์ด ๋ถ€๋“œ๋Ÿฌ์›Œ์•ผ ํ•˜๊ณ , ๊ฑฐ๋ฆฌ๊ฐ€ ์งง์•„์•ผ ํ•˜๊ณ , ๊ฒฝํ–ฅ์„ฑ์€ ํ•ฉ์ณ์ ธ์•ผ ํ•œ๋‹ค. ๋งž๋Š” ๋ง์ด๋‹ค.

ํ•˜์ง€๋งŒ, ์กฐ๊ธˆ ์˜์•„ํ•œ ์ ์€ `GeoLine`, `Coordinate` ๋Š” ๋ชจ๋‘ ‘์ฝ”์Šค’๋ผ๋Š” ๋ฒ”์œ„ ๋‚ด์—์„œ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋“ค์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ํŒ€์›๋“ค ๊ฐ„์— ๊ทธ๋Ÿฌํ•œ ์ ์„ ๊ณต์œ ํ•˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์—์„œ ์ƒ์„ฑ ์‹œ ์ขŒํ‘œ๋“ค์ด ๋ถ€๋“œ๋Ÿฌ์›Œ์ง€๊ณ , ๊ฑฐ๋ฆฌ๊ฐ€ ๋จผ ๊ฒƒ์„ ๋ณด์ •ํ•˜๋Š” ๋กœ์ง์„ `GeoLine`, `Coordinate` ์ชฝ์œผ๋กœ ์˜ฎ๊ฒจ๋„ ๋ฌธ์ œ์—†์ง€ ์•Š์„๊นŒ? ์˜คํžˆ๋ ค `Course` ์— ๋„ˆ๋ฌด ๋งŽ์€ ๋กœ์ง๋“ค์„ ๋‹ด๊ณ  ์žˆ๋Š”๊ฑด ์•„๋‹๊นŒ? `Course`์—์„œ๋งŒ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„๋‹ค๊ณค ํ•˜์ง€๋งŒ, ์ด ๋„๋ฉ”์ธ์„ ๋งŒ๋“ค๋•Œ `Segment`, `GeoLine`, `Coordinate`๋Š” ๋ชจ๋‘ ๊ฐ™์€ ๋งฅ๋ฝ์—์„œ ๋‚˜์˜จ ๊ฐ์ฒด๋“ค์ด๋‹ค.

์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ํ•˜๋‚˜์˜ ๊ฐˆ๋ฆผ๊ธธ์ด ๋‚˜์˜ค๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

 


- `Course` ์™ธ์˜ ํ•˜์œ„ ๊ฐ์ฒด๋“ค์„ ์ˆœ์ˆ˜ํ•˜๊ฒŒ ‘์ง€๋ฆฌ์  ์„ ’, ‘์ง€๋ฆฌ์  ์ ’์˜ ์—ญํ• ๋กœ ๋ณผ ๊ฒƒ์ธ๊ฐ€?

๋งŒ์•ฝ ์ˆœ์ˆ˜ํ•œ ์ง€๋ฆฌ์  ์„ , ์ ์˜ ์—ญํ• ๋กœ ๋ณธ๋‹ค๋ฉด ์ƒ์„ฑ ๋กœ์ง๋“ค์€ `Course`์— ๋ถ„๋ฆฌ๋˜๋Š” ๊ฒƒ์ด ๋งž๋‹ค. ํ•˜์ง€๋งŒ, ๊ทธ๋Ÿฐ๊ฒŒ ์•„๋‹ˆ๋ผ `GeoLine`, `Coordinate`, `Segment`๋„ ์–ด๋А์ •๋„ ๋„๋ฉ”์ธ ๋กœ์ง์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ์กด์žฌ๋“ค๋กœ ๋ณธ๋‹ค๋ฉด ์ƒ์„ฑ ๋กœ์ง์„ ๊ฐ์ž๊ฐ€ ๋ถ€๋‹ดํ•˜๋Š” ๊ฒƒ์ด ๋งž๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

์ด๋Ÿฐ ๋งฅ๋ฝ์„ ๊ณ ๋ คํ–ˆ์„ ๋•Œ ๋‚˜๋Š” ๋ณดํ†ต ํ•˜๋‚˜์˜ ๊ธฐ์ค€์œผ๋กœ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ๋ฐ”๋กœ `์žฌ์‚ฌ์šฉ์„ฑ`์ด๋‹ค. ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์— ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑด๋„ˆ๋›ฐ๊ณ , ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ `Course`๊ฐ€ ์•„๋‹Œ ๊ฐ์ฒด๋“ค์„ ์žฌ์‚ฌ์šฉํ•˜๋Š” ์ผ์ด ์žˆ๋‚˜? ํ˜น์€ ์žˆ์„๊นŒ? ์ง€๊ธˆ ๋‹น์žฅ์ด๋ผ๋ฉด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์—†๋‹ค. `Course` ์™ธ์— ํ•˜์œ„ ๊ฐ์ฒด๋“ค์€ ์‚ฌ์šฉ๋˜๋Š” ์ผ์ด ์—†๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด, ์ถ”ํ›„์— ์žฌ์‚ฌ์šฉ๋  ์ผ์ด ์žˆ์„๊นŒ? ๊ฝค ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ํ•˜๋‚˜ ๋“ค๋ฉด ํ˜„์žฌ๋Š” `Course` ๋‹จ์œ„๋กœ ๋Ÿฌ๋‹ ์ฝ”์Šค๋ฅผ ์ธ์‹ํ•˜๊ณ  ์žˆ์ง€๋งŒ ๋‚˜์ค‘์— `Segment` ๋‹จ์œ„๋กœ ๋Ÿฌ๋‹ ์ฝ”์Šค๋ฅผ ์ธ์‹ํ•˜๊ณ  ๊ฐ `Segment`๋งˆ๋‹ค์˜ ์‚ฌ์šฉ์ž๋ณ„ ๊ธฐ๋ก์„ ๊ธฐ๋กํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ์‹ค์ œ๋กœ Strava์—์„œ๋Š” ํ•ด๋‹น ๋ฐฉ์‹์œผ๋กœ ๋Ÿฌ๋‹ ์ฝ”์Šค๋ฅผ ์ธ์‹ํ•˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๊ฒฝํ–ฅ์„ฑ์„ ํ•ฉ์น˜๊ณ , ์ขŒํ‘œ๋ฅผ ๋ณด์ •ํ•˜๋Š” ์ด๋Ÿฐ ๋กœ์ง๋“ค์€ ๋ชจ๋‘ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ๋“ค์–ด์ง„ ๋กœ์ง๋“ค์ด๋‹ค. ๊ทธ๋ž˜์„œ `Segment` ๋‹จ์œ„๋กœ ๋Ÿฌ๋‹์ฝ”์Šค๋ฅผ ์ธ์‹ํ•ด๋„ ์—ฌ์ „ํžˆ ์‚ฌ์šฉ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์€ ๋กœ์ง๋“ค์ด๋‹ค.

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

 

public Course(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    // ...
    List<Coordinate> coordinates = CoordinateBuilder.fromRawCoordinates(rawCoordinates)
            .removeSimilar()
            .smooth()
            .build();
    List<GeoLine> geoLines = GeoLineBuilder.fromCoordinates(coordinates)
            .build();
    this.segments = SegmentBuilder.fromGeoLines(geoLines)
            .mergeSameElevationDirection()
            .mergeSameInclineType()
            .build();
    // ...
}


ํ˜„์žฌ ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์ง€์ ์ด๋‹ค. ์ข€ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฌธ์ œ๋ฅผ ์ •์˜ํ•˜์ž๋ฉด ‘์ƒ์„ฑ ์ฑ…์ž„ ์ค‘ ์ƒ์„ฑ ๋ฐฉ์‹์„ Course์— ๋‘˜ ๊ฒƒ์ด๋ƒ ์•„๋‹ˆ๋ฉด ์ถ”์ƒํ™” ํ•  ๊ฒƒ์ด๋ƒ’์ด๋‹ค. ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ ์ƒ๋‹นํžˆ ๋งŽ์€ ๊ณ ๋ฏผ์„ ํ•ด๋ดค๋Š”๋ฐ, ๊ฒฐ๋ก ์€ Course์— ๋‘๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๊ฒŒ ๋œ ๊ฐ€์žฅ ํฐ ์ด์œ ๋Š” ์ฝ”๋“œ์˜ ํŒŒํŽธํ™” ๋•Œ๋ฌธ์ด๋‹ค.

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

๋‘ ๋ฒˆ์งธ๋กœ๋Š” ์ •๋ณด ์ „๋ฌธ๊ฐ€์˜ ๊ด€์ ์ด๋‹ค. Course๋Š” Segment, GeoLine, Coordinate๊ฐ€ ์ƒ์„ฑ๋˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์ฆ‰, Course๋Š” ๊ฐ ํ•˜์œ„ ๊ฐ์ฒด๋“ค์˜ ์ •๋ณด ์ „๋ฌธ๊ฐ€์ด๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ ํ•˜์œ„ ๊ฐ์ฒด๋“ค์˜ ์ƒ์„ฑ ์ฑ…์ž„์„ ๊ฐ–๋Š” ๊ฒƒ์ด ํ๋ฆ„์ƒ ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.

๊ทธ๋ž˜์„œ ์ƒ๊ฐ์„ ๋ฐ”๊ฟจ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ์ƒ์„ฑ์ž๊ฐ€ ๋น„๋Œ€ํ•œ ๊ฒƒ์€ ๋งž๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ƒ์„ฑ์ž๊ฐ€ ๊ถ๊ธˆํ•ด์„œ ๋“ค์–ด์™”๋Š”๋ฐ, ์•„์ง์€ ๊ถ๊ธˆํ•˜์ง€ ์•Š์€ ์ขŒํ‘œ ์ฒ˜๋ฆฌ ๊ณผ์ •์„ ๋ด์•ผํ•œ๋‹ค๋ฉด ์กฐ๊ธˆ ํ”ผ๊ณคํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

// 1๋ฒˆ
public Course(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    this.id = null;
    this.name = new CourseName(name);
    this.roadType = roadType;
    this.segments = refineCoordinates(rawCoordinates);
    this.length = calculateLength(segments);
    this.inclineSummary = InclineSummary.of(segments);
    this.difficulty = Difficulty.fromLengthAndRoadType(length(), roadType);
}

private List<Segment> refineCoordinates(List<Coordinate> rawCoordinates) {
    List<Coordinate> coordinates = CoordinateBuilder.fromRawCoordinates(rawCoordinates)
            .removeSimilar()
            .smooth()
            .build();
    List<GeoLine> geoLines = GeoLineBuilder.fromCoordinates(coordinates)
            .build();
    reutrn SegmentBuilder.fromGeoLines(geoLines)
            .mergeSameElevationDirection()
            .mergeSameInclineType()
            .build();
}
```

```java
// 2๋ฒˆ
public static create(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    List<Coordinate> coordinates = CoordinateBuilder.fromRawCoordinates(rawCoordinates)
            .removeSimilar()
            .smooth()
            .build();
    List<GeoLine> geoLines = GeoLineBuilder.fromCoordinates(coordinates)
            .build();
    List<Segment> segments = SegmentBuilder.fromGeoLines(geoLines)
            .mergeSameElevationDirection()
            .mergeSameInclineType()
            .build();

    return new Course(name, roadType, segments);
}

private Course(String name, RoadType roadType, List<Segment> segments) {
    this.id = null;
    this.name = new CourseName(name);
    this.roadType = roadType;
    this.segments = segments;
    this.length = calculateLength(segments);
    this.inclineSummary = InclineSummary.of(segments);
    this.difficulty = Difficulty.fromLengthAndRoadType(length(), roadType);
}


ํŠนํžˆ 1๋ฒˆ ์ฝ”๋“œ๊ฐ€ ํ˜„์žฌ ๊ธฐ์ค€์œผ๋กœ ์ตœ์„ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ํŒ€์—์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด ๋ชจ๋‘ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ƒ์„ฑ์ž ์†์—์„œ ๋งฅ๋ฝ์˜ ๋ณ€ํ™”๋ฅผ ์ฃผ๊ธฐ๋„ ํ•˜๊ณ , ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ Segment๊ฐ€ ์ƒ์„ฑ๋˜๋Š”์ง€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด `refineCoordinates`๋งŒ ํ™•์ธํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

 

๊ฒฐ๋ก 

์ด๋ฒˆ ๊ธฐํšŒ์— ํ”„๋กœ์ ํŠธ ๋„๋ฉ”์ธ์— ๋Œ€ํ•ด์„œ ๋‹ค์‹œ ํ•œ๋ฒˆ ๊นŠ๊ฒŒ ๊ณ ๋ฏผํ•˜๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ๋‹ค.

 

๊ธฐ์กด์—๋Š” Course์—์„œ ์˜๋ฏธ๋ฅผ ๊ฐ–๋”๋ผ๋„ ๊ฐ™์€ ๋งฅ๋ฝ์—์„œ ๋‚˜์˜จ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ผ๋ฉด ํ•ด๋‹น ๋งฅ๋ฝ์„ ๊ฐ€์ง„์ฑ„ ๊ฐ์ž์˜ ์ฑ…์ž„์„ ๊ฐ€์ง€๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ž์นซ ๊ณผ๋„ํ•˜๊ฒŒ ์ฑ…์ž„์„ ๋‚˜๋ˆ„๊ฒŒ ๋˜๋ฉด ์˜คํžˆ๋ ค ์œ ์ง€๋ณด์ˆ˜ ํ•˜๊ธฐ ํž˜๋“  ์ฝ”๋“œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค. ๋ญ”๊ฐ€ ์ข€ ์ถ”์ƒ์ ์ธ ๊ฑฐ ๊ฐ™๋‹ค.

 

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

 

public Course(String name, RoadType roadType, List<Coordinate> rawCoordinates) {
    this.id = null;
    this.name = new CourseName(name);
    this.roadType = roadType;
    this.segments = refineCoordinates(rawCoordinates);
    this.length = calculateLength(segments);
    this.inclineSummary = InclineSummary.of(segments);
    this.difficulty = Difficulty.fromLengthAndRoadType(length(), roadType);
}

private List<Segment> refineCoordinates(List<Coordinate> rawCoordinates) {
    List<Coordinate> coordinates = CoordinateBuilder.fromRawCoordinates(rawCoordinates)
            .removeSimilar()
            .smooth()
            .build();
    List<GeoLine> geoLines = GeoLineBuilder.fromCoordinates(coordinates)
            .build();
    reutrn SegmentBuilder.fromGeoLines(geoLines)
            .mergeSameElevationDirection()
            .mergeSameInclineType()
            .build();
}

 

Builder๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „ ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ์— Segment, Coordinate, GeoLine๋“ค์ด ๊ฐ์ž ์ด๋Ÿฌํ•œ ์ฑ…์ž„์„ ๊ฐ–๊ณ  ์žˆ๋˜ ์ƒํƒœ์˜€๊ธฐ์— ๋” ํ˜ผ๋ž€์Šค๋Ÿฌ์› ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ํŒ€์›๋“ค๊ฐ„์— ์ข‹์€ ํ† ๋ก ์„ ์ง„ํ–‰ํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์•„ ์ข‹๋‹ค. ๊ทธ๋ฆฌ๊ณ , ํ•ญ์ƒ ์ •๋‹ต์€ ์—†๋‹ค๋Š” ์ ์„ ๋˜ ๊นจ๋‹ซ๋Š”๋‹ค.