[Java] OOP, Builder 패턴을 사용한 Person 클래스 정의

2023. 6. 11. 16:16Tip

728x90
  • 조건 1: 최초 생성 후 수정이 불가능해야 한다.
  • 조건 2: Instance 생성 시, name을 제외한 모든 속성에 대한 인자는 optinal이다.
  • 조건 3: Instance를 구분할 수 있는 unique key값이 필요하다.

define class Person using Builder pattern

특징

  • (조건 1 완료) Person의 모든 멤버 변수가 final형이다. (즉, 최초 생성 간 초기화 이후 수정 불가)
  • (조건 2 완료) 생성 인자가 순서로부터 자유롭다. (단, non-null 파라미터는 순서 강제)
  • (조건 3 완료) PersonBuilder 내 static 멤버 변수 ID 활용

코드

< 코드 전문 텍스트로 보기 >

더보기
public class Main {
    public static void main(String[] args) {

        // Use Builder Pattern
        Person p1 = new Person.PersonBuilder("Tomas")
                .age(37)
                .height(180)
                .weight(78).build();
        p1.showInfo();

        System.out.println();

        Person p2 = new Person.PersonBuilder("2")
                .weight(2)
                .age(2)
                .height(2).build();
        p2.showInfo();

        System.out.println();

        Person p3 = new Person.PersonBuilder("re-assignable")
                .name("cannot be null").build();
        p3.showInfo();

    }
}

 

public class Person {
    private final int id;
    private final String name;
    private final Integer age;
    private final Double height;
    private final Double weight;

    private Person(int id, String name, Integer age, Double height, Double weight) {
        System.out.println(this + " 생성");
        this.id = id;
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }

    public void showInfo() {
        System.out.println("id:\t\t" + id);
        System.out.println("name:\t" + name);
        System.out.println("age:\t" + age);
        System.out.println("height:\t" + height);
        System.out.println("weight:\t" + weight);
    }

    protected static class PersonBuilder {
        private static int ID = 1;
        private String name;
        private Integer age;
        private Double height, weight;

        protected PersonBuilder(String name) {
            this.name = name;
        }

        protected Person build() {
            return new Person(ID++, name, age, height, weight);
        }

        protected PersonBuilder name(String name) {
            this.name = name;
            return this;
        }

        protected PersonBuilder age(int age) {
            this.age = age;
            return this;
        }

        protected PersonBuilder height(double height) {
            this.height = height;
            return this;
        }

        protected PersonBuilder weight(double weight) {
            this.weight = weight;
            return this;
        }
    }
}

 

class Person

특징

 
    private final int id;
    private final String name;
    private final Integer age;
    private final Double height;
    private final Double weight;

    private Person(int id, String name, Integer age, Double height, Double weight) {
        System.out.println(this + " 생성");
        this.id = id;
        this.name = name;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }
 

Person 클래스의 객체는 이 생성자를 통해 최초 1회 생성 및 초기화된다. (멤버 변수 모두 final)

단, private 생성자이므로 외부에서는 직접 호출할 수 없으며,

이후에 소개할 Person.PersonBuilder.build() 를 통해 이 생성자를 호출하게 된다.

 

** non-null이 보장되지 않는 멤버 변수는 primitive 대신 래퍼 클래스를 사용하여 null값 처리를 손쉽게 했다.


Inner-class of Person, PersonBuilder

 
    protected static class PersonBuilder {
 

protected static PersonBuilder는 Person과 동일한 패키지에 포함된 Inner class이므로

Person 클래스객체 생성 이전 시점부터 정의한 빌더를 사용할 수 있게 된다.

Person 클래스는 이후 PersonBuilder 내부 메소드를 사용해야 하므로,

PersonBuilder 내 모든 메소드의 접근 제어자는 protected가 되어야 한다.

 

* outer-class Person을 상속하는 class Student 또한 Builder 패턴을 사용해야 한다고 가정해보자. 보다 깔끔한 Package 구조의 측면에서는 아직 생각해보지 않았지만, 막연하게 Builder inner-class의 확장성을 생각한다면 protected를 사용하는 것이 무난할 것 같다. 확장하지 않는다면 접근 제어자를 생략하여 default로 사용하면 더 깔끔해지긴 하겠다.

 

 
        private static int ID = 1;
        private String name;
        private Integer age;
        private Double height, weight;
 

일반적으로 멤버 변수는 Person과 동일한 형태로 사용하나,

여기서는 조건 3 때문에 ID만 변화를 주었다.

 

 
        protected PersonBuilder name(String name) {
            this.name = name;
            return this;
        }
 

모든 초기화 method x(DataType x)는 this.x=x; return this; 의 형태이다.

마지막 return this; 구문을 통해 이전 PersonBuilder를 이어받아 연속적인 초기화가 가능하다.

물론 build() 호출 이전까지 재할당이 가능하고, 가장 마지막 값이 반영된다.

 

 
        protected Person build() {
            return new Person(ID++, name, age, height, weight);
        }
 

최종적으로 Person 객체를 만들어 반환하는 메소드 PersonBuilder.build()이다.

이 함수의 호출 이전에는 Person 객체가 생성되지 않으므로, 모든 생성 구문은 반드시 한 줄로만 작성할 수 있다.

즉, Setter가 없는 Person class에서 값을 설정할 수 있는 유일한 순간은 이 객체를 생성할 때가 된다.

728x90

'Tip' 카테고리의 다른 글

[MacOS/sdkman] JDK version 관리  (0) 2023.06.15
Download Eclipse  (0) 2023.06.15
[MySQL] Workbench LOAD DATA LOCAL INFILE ERROR  (0) 2023.05.31
[Java] Stream to List  (0) 2023.04.12
[해커톤] 웹 개발 팁 정리  (0) 2023.04.02