ITEM 04. 인스턴스화를 막으려거든 private 생성자를 사용하라

NAKTA·2023년 11월 16일
0

effective java 3e

목록 보기
4/5
post-thumbnail
post-custom-banner

🌱 들어가면서

이번 주제는 이전 ITEM 에서도 자주 거론됐던 private 생성자 이다. 객체의 수를 제한 두는 것이 왜 의미가 있는지는 ITEM 01 에서 설명했으니 한번 읽어보면 좋을 것 같다. 이번 글을 private 생성자 가 어떤 상황에서 필요한지를 중점으로 다룰 예정이다.



🙃 private 생성자

public class Something {
    private Something(){}
}

다음과 같이 생성자 접근 제한자private 로 설정하면 어떻게 될까?

더이상 해당 클래스의 객체 생성자는 접근 제한을 오로지 클래스 내부에 제한시켜 외부에서는 해당 생성자를 호출할 수 없다.


💡 접근 제한자란?
Java 에서 클래스, 필드, 생성자, 메소드를 접근할 수 있는 권한을 제한하는 키워드



🙃 private 생성자를 쓰는 경우

앞서 private 생성자 가 어떤 역할을 하는지 알았다.

그렇다면, private 생성자 를 이용하여객체 의 생성을 컨트롤하는 경우는 어떤게 있을까?

1. 싱글톤 (Singleton) 패턴

public class Singleton {
    private static Singleton INSTANCE = new Singleton();

    private Singleton(){}
}

싱글톤 (Singleton) 패턴은 해당 클래스의 객체 의 수를 1개 로 제한을 두기 때문에 생성자를 이용한객체 생성을 막을 수 밖에 없다.

ITEM 03 에서 다뤘던 내용이니 한번 읽어보면 좋을 것 같다.



2. 유틸리티 클래스 (Utility Class)

일부 클래스는 문자열 관련, 랜덤값 생성, 날짜 및 시간 처리 등 전역에서 사용되는 특정 로직이나 독립적인 기능을 구현해둔 클래스로 설계될 수 있다.

다음은 java에서 기본적으로 제공하는 java.util.Collections 코드의 일부이다.

public class Collections {

    private Collections() {
    }

    private static final int BINARYSEARCH_THRESHOLD   = 5000;
    private static final int REVERSE_THRESHOLD        =   18;
    private static final int SHUFFLE_THRESHOLD        =    5;
    private static final int FILL_THRESHOLD           =   25;
    private static final int ROTATE_THRESHOLD         =  100;
    private static final int COPY_THRESHOLD           =   10;
    private static final int REPLACEALL_THRESHOLD     =   11;
    private static final int INDEXOFSUBLIST_THRESHOLD =   35;

    @SuppressWarnings("unchecked")
    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }
    
    ...
    
}

java.util.Collections 클래스는 자바에서 컬렉션 프레임워크를 위한 유틸리티 메서드를 제공하는 유틸리티 클래스이다.

이 클래스는 모든 메서드 및 상수가 static 으로 선언되어 있기 때문에 객체 를 만들 필요가 없다.



3. 상수 클래스 (Constant Class)

public final class Constants {
    // private 생성자로 외부에서의 인스턴스화 방지
    private Constants() {
    }

    public static final int MAX_SIZE = 100;
    public static final String DEFAULT_NAME = "John Doe";
}

상수 클래스란 주로 상수 값을 정의하고 관리하기 위한 클래스로,

메서드를 가지지 않고 모든 멤버가 상수이기에 위의 2번과 같이 static 으로 모두 선언되어 있어 객체 를 만들 필요가 없다.



4. 정적 팩토리 메서드 (static factory method)

public class Laptop {
    private String brand;
    private String model;
    private int price;

    private Laptop(){}

    public static Laptop withBrand(String brand) {
        Laptop laptop = new Laptop();
        laptop.brand = brand;
        return laptop;
    }

    public static Laptop withModel(String model) {
        Laptop laptop = new Laptop();
        laptop.model = model;
        return laptop;
    }
}

생성자를 통한 객체 생성을 막고 정적 팩토리 메서드 를 이용해서 객체 생성을 하는 방법이다.

해당 방법을 쓰는 이유에 대해서는 ITEM 01 에서 정리를 해둔 글이 있으니 한번 읽어보면 좋을 것 같다.



5. 빌더 (Builder) 패턴

public class Car {
    private final String brand;           // 필수
    private final String model;           // 필수
    private final String color;           // 필수
    private final String country;         // 선택
    private final int year;               // 선택
    private final int weight;             // 선택

    private Car (Builder builder) {
        brand   =   builder.brand;
        model   =   builder.model;
        color   =   builder.color;
        country =   builder.country;
        year    =   builder.year;
        weight  =   builder.weight;
    }

    public static class Builder {
        private final String brand;           // 필수
        private final String model;        	  // 필수
        private final String color;           // 필수

        // 선택 매개변수 기본값 초기화
        private String country = "";          // 선택
        private int year = 0;         	      // 선택
        private int weight = 0;               // 선택

        public Builder (String brand, String model, String color) {
            this.brand = brand;
            this.model = model;
            this.color = color;
        }

        public Builder country(String country) {
            this.country = country;
            return this;
        }

        public Builder year(int year) {
            this.year = year;
            return this;
        }

        public Builder weight(int weight) {
            this.weight = weight;
            return this;
        }

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

빌더 (Builder) 패턴 은 매개변수가 많을 때 생성자를 통한 객체 생성이 아닌 내부 Builder 클래스를 따로 만들어 객체 생성 역할을 위임하는 방법이다.

해당 패턴을 쓰는 이유에 대해서는 ITEM 02 에서 자세하게 정리를 해뒀으니 한번 읽어보면 좋을 것 같다.


📑 정리

  • private 생성자는 외부에서 생성자를 호출할 수 없다.
  • private 생성자는 다양한 경우에 사용하지만, 주된 목적은 객체 생성을 컨트롤하기 위해서 필요하다.



🔎 참조

profile
느려도 확실히
post-custom-banner

0개의 댓글