BUILDER PATTERN

한결·2023년 4월 18일
0

Study

목록 보기
13/13

빌더패턴 이란?

빌더는 복잡한 object들을 단계별로 구축할 수 있는 생성 디자인 패턴.
이 패턴을 사용하면, 동일한 구성코드를 사용하여 다양한 타입과 표현을 제공함
=> 객체를 잘 만들 수 있게 해주는 도구

객체를 생성하는 방법
1. 생성자에 인자를 넣어 인스턴스를 생성한다
2. SETTER 사용한 자바빈 패턴
이 있는데, 단점을 개선한 방법으로
3. 빌더 패턴
이 존재한다

생성자에 인자를 넣어 인스턴스를 생성하는 방법

public class Pizza {
 
    private String name;
    private String dough;
    private String sauce;
    private String topping;
    private int price;
 
    public Pizza(String name, int price) {
        this.name = name;
        this.price = price;
    }
 
    public Pizza (String name, String dough, String sauce, int price) {
        this.name = name;
        this.dough = dough;
        this.sauce = sauce;
        this.price = price;
    }
 
    public Pizza(String name, String dough, String sauce, String topping, int price) {
        this.name = name;
        this.dough = dough;
        this.sauce = sauce;
        this.topping = topping;
        this.price = price;
    }
 
    @Override
    public String toString() {
        return "name: " + name + ", " + "dough: " + dough + ", " +  "sauce: " + sauce +  ", " + "topping: " + topping + "price: " + price;
    }
}

피자에 필수 인자인 name, price만 인자로 받는 pizza 생성자를 선언
name, price, dough, sauce를 추가하는 생성자 선언
나머지 모든 인자 값을 사용하는 피자 생성자도 만들었음

이 방식의 장점
-> 하나의 메서드를 통해서 객체의 생성과 값 설정을 통해 완성된 객체를 반환할 수 있어 일관성을 지킬 수 있음

이 방식의 단점
-> new Pizza("슈프림 피자", "씬도우", "핫소스", 5000)같이 호출이 많은 경우 그만큼 생성자를 많이 만들어야 함.
-> 객체의 프로퍼티가 많으면 그에 따른 생성자도 많이 만들어야 함
-> 인자에 대한 설명이 없기 때문에 인자가 많은 경우에 헷갈림
-> 설정하지 않는 값을 null로 채워서 사용해야 함
-> 변수를 어느 위치에 세팅해야하는지 알기 어려움.

등이 있음

SETTER를 이용한 자바 빈 패턴

public class PizzaJavaBean {
 
    private String name;
    private String topping;
    private int price;
 
 
    public PizzaJavaBean() {
        // 기본 생성자
    }
 
    public String getName() {
        return name;
    }
 
    public String getTopping() {
        return topping;
    }
 
    public int getPrice() {
        return price;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setTopping(String topping) {
        this.topping = topping;
    }
 
    public void setPrice(int price) {
        this.price = price;
    }
}

생성자에 인자를 넣어 인스턴스를 생성하는 방법을 보완해서 만든 방법. setter를 사용하여 생성한 객체에 인자를 정확하게 파악하여 세팅함


장점
-> 각 인자의 의미를 setter 메서드에 맞는 값을 넣기 때문에 정확하게 파악이 가능
-> 객체 클래스 내부에 복잡하게 여러 개의 생성자를 만들 필요가 없음
단점
=> 객체가 완전히 생성되기 전까지는 '일관성이 무너진 상태에 놓이게 된다'라고 말함

Pizza pizza = new Pizza();
//pizza.setName("dilicious");
//pizza.setTopping("olive");
//pizza.setPrice("10000");

주석처리 되어도 실행에는 문제가 없음 - 컴파일 시점에서 오류로 잡을 수 없음 - 고객들이 회원가입을 할 때 오류를 겪을 수 있음 == 일관성이 불안정한 상태로도 사용이 가능.
-> public으로 선언되어있기 때문에 정보의 일관성을 지키기 힘듦
-> 1회 호출로 끝나지 않고 setter를 통해 값을 계속 세팅해줘야 함, 이 때 값을 초기 설정하기 위한 것인지 변경하기 위한 것인지 알 수 없음.

빌더 패턴을 통한 객체 생성

public class PizzaBuilder {
 
    private final String name;
    private final String dough;
    private final String sauce;
    private final String topping;
    private final int price;
 
 
    // 객체 생성 전, 값을 세팅해줄 Builder 내부 클래스
    public static class Builder {
        private String name;
        private String dough;
        private String sauce;
        private String topping;
        private int price;
 
        public Builder name(String name) {
            this.name = name;
            return this;
        }
 
        public Builder dough(String dough) {
            this.dough = dough;
            return this;
        }
 
        public Builder sauce(String sauce) {
            this.sauce = sauce;
            return this;
        }
 
        public Builder topping(String topping) {
            this.topping = topping;
            return this;
        }
 
        public Builder price(int price) {
            this.price = price;
            return this;
        }
 
        // 값 세팅이 끝난 후 내부 클래스를 넘겨주어 본 객체에 값을 세팅해주는 메소드
         public PizzaBuilder build() {
            return new PizzaBuilder(this);
         }
    }
 
    // 값 세팅이 끝난 후 내부 클래스를 넘겨주어 본 객체에 값을 세팅해주는 메서드
    public PizzaBuilder(Builder builder) {
        this.name = builder.name;
        this.dough = builder.dough;
        this.sauce = builder.sauce;
        this.topping = builder.topping;
        this.price = builder.price;
    }
 
    @Override
    public String toString() {
        return "name: " + name + ", " + "dough: " + dough + ", " + "sauce: " + sauce + ", " + "topping: " + topping + "price: " + price;
    }
}

사용 방법

public class Main {
 
    // 빌더 패턴 사용
    PizzaBuilder pizzaBuilder = new PizzaBuilder.Builder()
            .name("불고기피자")
            .dough("thin")
            .sauce("핫소스")
            .topping("페퍼로니")
            .price(20000)
            .build();
}

값을 세팅 후 자기 자신을 리턴하여 price, dough, 메서드를 계속 호출할 수 있고, 모든 세팅이 끝나면 마지막에 build()를 호출


장점
-> 가독성을 높여주고, 어디에 어떤 값을 세팅해야 하는지 명확하게 알고 사용할 수 있음.
-> 필요한 값만 설정 하면, 나머지 값은, null로 설정 가능
-> 하나의 메서드를 사용하는 것처럼 사용가능하고, 마지막에 build()메서드까지 호출해야지만 객체로써 활용할 수 있으므로 일관성을 지킬 수 있음.
-> 객체 생성이 되는 순간 setting이 되기 때문에 변경 불가능한 객체 만들 수 있음
-> 객체 클래스 내부에 복잡하게 여러 개의 생성자를 만들 필요가 없음
-> build() 함수로 잘못된 값이 세팅되어있는지 검증 가능

단점
-> 빌더 인터페이스의 변화는 이를 구현한 모든 클래스에 영향을 미침
-> 클래스 작성으로 인해 코드가 길어짐
.. 등

LOMBOK - @BUILDER

빌더 패턴을 적용할 객체에 @Builder 어노테이션을 달기만 하면됨

@Builder
public class Bag {
	private String name;
        private int money;
        private STring memo;
}

생성자에서 사용하게 되면 지정한 값만 빌더 메서드를 사용 가능하게 함

  
    public Pizza(String name, int price) {
        this.name = name;
        this.price = price;
    }
   Pizza pizza = pizza.builder()
   					.name(name)	
                       .price(price)
                       .build();
   

출처 :: https://dreamcoding.tistory.com/56

0개의 댓글