개발이슈/디자인패턴

멀티스레드 환경에서 안전한 Step Builder 패턴 이해하기

kalkin 2023. 11. 25. 23:58

멀티스레드 환경에서 안전한 Step Builder 패턴 이해하기


코딩을 배우면서 'Step Builder 패턴'이라는 용어를 접하실 수 있습니다. 

특히 많은 데이터를 한 번에 다루어야 하는 복잡한 객체를 생성할 때 이 패턴이 유용합니다. 

그러나 멀티스레드 환경에서는 이 패턴을 좀 더 주의 깊게 사용해야 합니다. 

멀티스레드란, 컴퓨터가 동시에 여러 작업을 처리하는 것을 의미합니다. 웹사용 환경을 생각 하시면 편하죠.

Step Builder 패턴이란?
Step Builder 패턴은 객체를 단계별로 구성하는 방법입니다. 

레고 블록을 차례대로 쌓아 올리는 것처럼, 각 단계에서 필요한 부분을 추가해 최종적인 객체를 완성합니다. 

이 방식은 코드의 가독성을 높이고 실수를 줄이는 데 도움이 됩니다.

멀티스레드 환경에서의 Step Builder 패턴
멀티스레드 환경에서 Step Builder 패턴을 사용할 때 중요한 것은 '스레드 안전성'입니다. 

스레드 안전성은 여러 스레드가 동시에 같은 데이터에 접근할 때 발생하는 문제를 방지하는 것입니다. 

이를 위해 몇 가지 방법을 사용할 수 있습니다.

불변 객체 사용: 객체가 생성된 후에는 변경되지 않도록 합니다. 이렇게 하면 여러 스레드가 동시에 같은 객체를 사용해도 안전합니다.

각 스레드에 독립적인 빌더 인스턴스 제공: 각 스레드가 자신만의 빌더를 사용하여 객체를 생성하게 합니다. 이렇게 하면 다른 스레드의 작업에 영향을 받지 않습니다.

 

스텝 빌더 패턴은 객체 생성을 위한 디자인 패턴으로, 특히 많은 수의 매개변수를 가진 객체를 생성할 때 유용합니다. 이 패턴의 주요 장점과 단점을 정리해보겠습니다.

### 장점

1. 가독성 향상: 스텝 빌더 패턴은 각 단계를 명확하게 구분하여 코드의 가독성을 높입니다. 이는 객체 생성 과정을 이해하기 쉽게 만들어 줍니다.

2. 사용의 용이성: 사용자가 객체를 생성할 때 필요한 단계를 순차적으로 따라갈 수 있어, 사용자가 필요한 매개변수를 놓치거나 잘못 입력하는 것을 방지합니다.

3. 객체의 불변성 유지: 빌더를 통해 생성된 객체는 변경이 불가능한 불변 객체로 만들어질 수 있어, 멀티스레드 환경에서 안전하게 사용될 수 있습니다.

4. 유연한 객체 생성: 다양한 조합의 매개변수를 통해 다른 형태의 객체를 유연하게 생성할 수 있습니다.

### 단점

1. 코드의 복잡성 증가: 각 단계를 위한 인터페이스와 구현 클래스를 추가해야 하므로, 단순한 객체에 비해 구현해야 할 코드가 많아집니다.

2. 초기 개발 비용 증가: 스텝 빌더 패턴을 구현하는 데는 추가적인 시간과 노력이 필요하며, 이는 개발 초기 단계에서 부담이 될 수 있습니다.

3. 오버 엔지니어링의 위험: 매우 간단한 객체에 스텝 빌더 패턴을 적용하는 것은 과도한 설계가 될 수 있습니다. 따라서 객체의 복잡성과 요구 사항에 따라 적절한 패턴을 선택해야 합니다.

4. 유지보수의 어려움: 패턴이 복잡해질수록 유지보수도 어려워질 수 있으며, 특히 패턴에 익숙하지 않은 개발자가 코드를 이해하고 수정하는 데 시간이 더 걸릴 수 있습니다.

스텝 빌더 패턴은 매우 유용하지만, 패턴의 적용은 각 프로젝트의 요구 사항과 복잡성을 고려하여 결정해야 합니다. 간단한 객체에는 과도한 설계가 될 수 있으며, 복잡한 객체의 경우에는 이 패턴이 큰 이점을 제공할 수 있습니다.



예제: 멀티스레드에 안전한 자동차 객체 만들기

public class Car {
    // 필드는 final로 선언해 변경 불가능하게 함
    private final String engine;
    private final String color;
    private final int seats;

    // 생성자는 private으로 선언해 직접 객체를 생성하지 못하게 함
    private Car(Builder builder) {
        this.engine = builder.engine;
        this.color = builder.color;
        this.seats = builder.seats;
    }

    // 빌더 객체 생성 메소드
    public static EngineStep newBuilder() {
        return new Builder();
    }

    // 여기서부터는 빌더의 단계별 인터페이스와 내부 클래스 정의
    public interface EngineStep {
        ColorStep withEngine(String engine);
    }

    public interface ColorStep {
        SeatsStep withColor(String color);
    }

    public interface SeatsStep {
        BuildStep withSeats(int seats);
    }

    public interface BuildStep {
        Car build();
    }

    private static class Builder implements EngineStep, ColorStep, SeatsStep, BuildStep {
        private String engine;
        private String color;
        private int seats;

        @Override
        public ColorStep withEngine(String engine) {
            this.engine = engine;
            return this;
        }

        @Override
        public SeatsStep withColor(String color) {
            this.color = color;
            return this;
        }

        @Override
        public BuildStep withSeats(int seats) {
            this.seats = seats;
            return this;
        }

        @Override
        public Car build() {
            return new Car(this);
        }
    }
}