[JAVA] 빌더(Builder) 패턴

yrok·2023년 10월 13일

📌 Builder Pattern

빌더 패턴은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 맴개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드를 통해 객체를 생성하는 방식이다.

1. 점증적 생성자 패턴


@Getter @Setter
public class Member {
	
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private int age;
    private int phoneNumber;
    
    public Member(String name) {
    	
        this.name = name;
    }
    
    public Member(String name, int age) {
    	
        this.name = name;
        this.age = age;
    }
    
    public Member(String name, int age, int phoneNumber) {
    	
        this.name = name;
        this.age = age;
        this.phoneNumber = phoneNumber;
    }
}
Member memberA = new Member("memberA", 10, 112);

점증자 생성 패턴의 단점

  • 파라미터의 위치와 타입을 개발자가 알고 있어야 사용할 수 있다.
  • 파라미터가 많아지면 유지 보수가 어렵다.

문제를 해결하기 위해 "자바빈즈 패턴" 사용

2. 자바 빈즈 패턴

@Getter @Setter
public class Member {
	
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private int age;
    private int phoneNumber;
    
    public void setName(String name) {
    	this.name = namee;
    }
    
    public void setAge(int age) {
    	this.age = age;
    }
    
    public void setPhoneNumber(int phoneNumber) {
    	this.phoneNumber = phoneNumber;
    }
}
Member member = new Member();
member.setName("memberA");
member.setAge(20);
member.setPhoneNumber(112);

자바 빈즈 패턴 단점

  • 코드량이 늘어나고, 객체 일관성이 깨진다.
    • 객체 일관성이 깨진다. -> 객체를 생성하고 차후에 그 객체가 변할 여지가 있다. (set 함수가 남발될 수 있다.)

3. 빌더 패턴

둘의 단점을 모두 보완한 것이 빌더 패턴이다.
데이터는 자바 빈즈 패턴처럼 받고, 데이터 일관성을 위해 정보들을 모두 받은 후에 객체를 생성한다.

직접 빌더 클래스 생성

public class Member {
	
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
    
    private String name;
    private int age;
    private int phoneNumber;
    
    private Member() {}
    
    public static class Builder {
    	
        private String name;
        private int age;
        private int phoneNumber;
        
        public Builder(String name){
        	this.name = name;
        } 
        
        public Builder age(int age) {
        	this.age = age;
            return this;
        }
        
        public Builder phoneNumber(int phoneNumber) {
        	this.phoneNumber = phoneNumber;
            return this;
        }
        
        // 빌드 메서드
        public Member build() {
        	Member member = new Member();
            
            member.name = name;
            member.age = age;
            member.phoneNumber = phoneNumber;
            
            return member;
        }
    }
}
Member member = new Member.Builder("memberA")
    .age(25)
    .phoneNumber(112);
    .build(); //빌드 메서드로 새로운 객체 생성

@Builder

위와 같은 과정을 lombok의 @Builder를 사용하면 생략할 수 있다.

@Builder
@ToString
public class Member {
	
    @Id @GeneratedValue(strategy = GnerationType.IDENTITY)	
    private Long id;    
    
    private String name;
    private int age;
    private int phoneNumber;
}
@Transactional
@Test
public void buildTest() {
	
    Member memberA = Member.builder()
    		.name("memberA")
            .age(20)
            .phoneNumber(112)
            .build();
    
    Member memberB = Member.builder()
    		.name("memberB")
            .age(25)
            .phoneNumber(119)
            .build();
    
    memberRepository.save(memberA);
    memberRepository.save(memberB);
    
    List<Member> members = memberRepository.findAll();
    
    for (member : members) {
    	log.info("member={}", member);
    }
}

빌더 패턴을 사용해서 memberA, memberB를 생성하고 로그를 찍어보았다.

정상적으로 memberA, memberB 객체가 생성된 것을 확인할 수 있다.

참고

@Builder
@ToString
public class Member {
	
    @Id @GeneratedValue(strategy = GnerationType.IDENTITY)	
    private Long id;    
    
    private String name;
    private int age;
    private int phoneNumber = 10;
}
@Test
@Transactional
public void save() {

	Member memberA = Member.builder()
			.name("memberA")
			.age(20)
			.build();
    
    log.info("memberA={}", memberA);
}                

그래서 phoneNumber를 10으로 설정해놓고 테스트를 돌려봤다.

phoneNumber=10으로 나올줄 알았는데 0으로 나왔다. 왜지?? 하고 찾아보니..

  • 빌더 패턴은 빌드할 때 값을 설정해주지 않으면 null/0/false를 타입에 따라 반환한다.
  • String 타입은 null, int 타입은 0, boolean 타입은 false를 반환한다.

그럼 기본 값 설정 방법이 없을까 ? @Builder.Default를 사용하면 된다.

@Builder.Default

@Builder
@ToString
public class Member {
	
    @Id @GeneratedValue(strategy = GnerationType.IDENTITY)	
    private Long id;    
    
    private String name;
    private int age;
    
    @Builder.Default
    private int phoneNumber = 10;
}
@Test
@Transactional
public void save() {

	Member memberA = Member.builder()
			.name("memberA")
			.age(20)
			.build();
    
    log.info("memberA={}", memberA);
}                

profile
공부 일기장

0개의 댓글