[Java] Builder 패턴

jhkim·2022년 10월 10일
0

Java

목록 보기
1/3

Spring boot에서 application.property와 yml의 차이

외부 설정 파일이란?
애플리케이션에서 사용하는 여러 가지 설정값들을 정의하는 파일
외부 API를 사용하기 위한 API key 등은 이 외부 설정 파일에 넣어서 정의한다.
application.yml이나 properties파일은 스프링 부트가 자동으로 로딩하는 설정 파일이다.

//properties
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password

properties는 key-value 형식으로 지정하면 애플리케이션에 참조하여 사용할 수 있다.

spring:
	h2:
		console:
			path: /h2-console
    datasource:
        url: jdbc:h2:~/test
        driverClassName: org.h2.Driver
        username: sa
        password: password

yml은 계층 구조로 표현할 수 있어 가독성이 좋다




LiveCodingSample


^디렉토리 구조

entity를 보면 study와 studyRepository가 있다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Study {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private String skillStack;
    private String leader;
    private String password;

    @Builder
    public Study(String name, String skillStack, String leader, String password) {
        this.name = name;
        this.skillStack = skillStack;
        this.leader = leader;
        this.password = password;
    }
  • @Entity : 이 어노테이션을 클래스에 붙이면 해당 클래스는 jpa가 관리한다. 따라서 db 테이블과 클래스(dto, vo)와 매핑한다면 반드시 붙여야 한다
    속성) name: 엔티티 이름 지정. 기본적으로 클래스 이름을 그대로 사용한다.

  • @Id : jpa가 객체를 관리할 때 식별하는 기본 키 지정. 해당 프로퍼티가 테이블의 기본키 역할을 한다는 것을 나타낸다. @GeneratedValue로 주키 값 생성 전략을 명시한다. 디폴트는 auto

  • @Getter, @Setter : Lombok라이브러리에서 지원. 각각 getter와 setter를 생성한다.

  • @RequiredArgsConstructor, @NoArgsConstructor : Lombok 지원. 각각 모든 멤버 변수를 초기화시키는 생성자 생성 / 파라미터가 없는 기본 생성자 생성 역할을 한다.
    NoArgsConstructor의 경우, 초기값 세팅이 필요한 final 변수가 있으면 컴파일 에러 발생하므로 주의해야함.




Builder패턴

builder를 쓰는 이유?

생성자 매개변수의 수가 많을 경우 빠르게 처리할 수 없고,
앞으로 매개변수가 더 늘어날 경우 수정 및 관리가 복잡해진다.
빌더 패턴을 이용하면 유연성 및 가독성을 높일 수 있고, 필요한 데이터만 설정할 수 있다.

순수 JAVA로만 빌더 패턴을 사용하면 클래스마다 Builder클래스를 만들어야 하므로 귀찮고 복잡해진다. 이를 Lombok이 지원하는 @Builder어노테이션으로 편리하게 쓸 수 있다

Student student = new Student("이름", 0, "wellsy"); //더미 값


public class Student {
	private String name;
    private int age;
   	private String id;
    
    //age가 없는 생성자
    public Student(String name, int age, String id){
    	this.name = name;
        this.age = age;
        this.id = id;
    }
    
    //age에 더미 값
    public Student static func(String name, int age, String id) {
    	return new Student(name, 0, "wellsy");
    
}

만약 age라는 파라미터가 필요 없는 상황일 경우, age에 더미 값을 넣어주거나 age가 없는 생성자를 만들어줘야 한다




Builder 패턴 필수조건

  • class 내부에 static class Builder가 정의된다.
  • Builder클래스 내부에는 class의 매개변수들이 선언되는데, 이 때 필수 매개변수는 final이 선언되고 선택 매개변수는 기본값이 설정된다.
  • Builder생성자에는 필수 매개변수들이 파라미터로 들어간다.




Builder 패턴 장점

1. 동적 처리가 가능하여 유연성 확보가 가능하다.

다음 예시를 보자(Lombok 없이 작성)

public class Student {
    private String name; //필수
    private String id; //필수
    private int age;

    Student(Builder builder){
        this.name = builder.name;
        this.id = builder.id;
        this.age = builder.age;
    }

    public static class Builder {

        private final String name;

        private final String id;

        private int age = 10; //선택적 인자의 기본값

        public Builder(String name, String id) {
            this.name = name;
            this.id = id;

        }

		//필수가 아닌 것은 메소드로 추가할 수 있도록 함
        public Builder withAge(int age) {
            this.age = age;
            return this;
        }

        public Student build() { //실제 인스턴스 생성
            return new Student(this);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        
        Student student = new Student
                .Builder("이름", "wellsy") //builder생성자를 먼저 거치고 student생성자 거침
                .withAge(20) //필요할 경우만 추가
                .build();

        Student student2 = new Student
                .Builder("이름", "wellsy")
                .build();
    }
}

필수가 아닌 age는 필요할 때에만 withAge를 사용해 추가할 수 있도록 하는 코드이다.

필요한 객체를 직접 생성하는 대신, 필수 인자들을 전달하여 빌더 객체를 먼저 만든 뒤, 빌더 객체에 정의된 설정 메소드들을 호출하여 인스턴스를 생성하는 방식이다.

withAge를 쓰지 않는 객체는 디폴트값이 들어간다.

2. 가독성을 높인다.

Student student = new Student("이름", 20, "wellsy");

Student student2 = new Student
	.Builder("이름", "wellsy")
	.build();

아래 코드가 훨씬 더 직관적이다.

3. 불변성을 확보할 수 있다.
완전히 생성된 인스턴스라도 setter 메소드를 노출하고 있으므로, 불변을 보장할 수 없다.
빌더 패턴으로 객체를 생성하면 불변성을 보장할 수 있다.

Builder 어노테이션

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'

    compileOnly 'org.projectlombok:lombok:1.18.24'
    annotationProcessor 'org.projectlombok:lombok:1.18.24'

    testCompileOnly 'org.projectlombok:lombok:1.18.24'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.24'
}

build.gradle에 lombok 의존성을 추가하면 @Builder 어노테이션을 사용할 수 있다.

사용하고자 하는 클래스에 @Builder 어노테이션을 붙이면 Builder클래스를 따로 만들지 않아도 빌더 패턴 사용이 가능하다.

@Builder
public class Student {
    private String name;
    private String id;
    private int age;
}



**java의 정적 메소드

자바의 static은 메모리에 한 번 할당되어 프로그램이 종료될 때 해제되는 것을 의미한다.
일반적으로 우리가 만든 class는 static영역에 생성되고 new연산을 통해 생성한 객체는 heap영역에 존재한다. heap영역의 메모리는 가비지 콜렉터를 통해 수시로 관리되지만, static 키워드로 static영역에 할당된 메모리는 가비지 콜렉터의 관리 범위 밖이므로, 자주 사용하게 되면 시스템에 악영향을 미친다.

//static 메소드 예시
public class Calculator {
	public static int add(int x, int y) {
    	return x + y;
    }
}

Calculator.add(1,2); //인스턴스 생성 없이 static메소드 사용 가능

Calculator cal = new Calculator();
cal.add(1,2); //가능하지만 권장되지 않는 방법



빌더 패턴이 무조건 좋은가?

성능이 민감한 상황에서는 문제가 된다. 코드가 장황해지므로 파라미터가 여러 개일 때에 사용하는것이 좋다.




참고

https://velog.io/@bey1548/JAVA-%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4Builder-Pattern%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0
https://mangkyu.tistory.com/47

0개의 댓글

관련 채용 정보