[CS] Java 클래스에서 생성자에 private을 사용하는 이유

Sungjin Cho·2025년 4월 1일
1

CS

목록 보기
9/9
post-thumbnail

의문점

Facade 패턴을 공부 중

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class Database {
    private Database() {    
    }
    public static Properties getProperties(String dbname) { 
        String filename = dbname + ".txt";
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream(filename));
        } catch (IOException e) {
            System.out.println("Warning: " + filename + " is not found.");
        }
        return prop;
    }
}

이러한 예제가 있었다.
Database 클래스와 HtmlWriter 클래스를 통해 maildata.txt 의 데이터를 읽어 Html을 작성하는 PageMaker 클래스를 작성하는 예제였는데, Database 의 생성자에 private를 붙여 외부 클래스에서 객체 생성을 막는 이유가 뭘까 궁금해졌었다.

클래스에서 private 생성자를 사용하는 이유?

클래스 정의를 할 때 생성자에 private 접근 제어자를 사용하는 경우가 있다. 이는 객체 생성을 제어하거나 제한하기 위해서인데, 왜/언제 객체 생성을 제한하는 것일까?

싱글톤 패턴을 구현할 때

싱글톤 패턴은 인스턴스가 단 하나만 존재하도록 보장하는 디자인 패턴으로, 외부에서 new 키워드로 생성을 막는다. 대신 클래스 내부에서 미리 객체를 생성하고, 정적 메서드를 통해서 인스턴스를 얻도록 한다.

싱글톤 패턴을 사용하는 이유
1. 메모리 측면의 이점 (한 개의 인스턴스만 고정 메모리 영역에 생성됨)
2. 속도 측면의 이점 (static 인스턴스는 클래스가 로딩될 때 생성되며 프로그램 종료시까지 메모리에 남아있음)
3. 데이터 공유가 쉬움 (전역으로 사용하는 인스턴스이기 때문)

예제

public class Singleton {
    private static Singleton instance;

    // 생성자를 private으로 설정
    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

유틸리티 클래스처럼 인스턴스화가 필요 없는 경우

Math 클래스나 Arrays 클래스처럼 모든 메서드가 static인 유틸리티 클래스는 객체를 생성할 필요가 없다. 이 경우 private 접근제어자를 통해 객체의 생성을 제한한다.

이유

인스턴스를 생성하는 것이 의미가 없기 때문에 생성을 막아 코드의 의도를 명확히 한다.

예제

public class Utility {
    private Utility() {
    }

    public static int add(int a, int b) {
        return a + b;
    }
}

객체 생성을 팩토리 메서드로 제한하고 싶을 때

팩토리 패턴을 사용할 때, 생성자를 private으로 설정하고 정적 팩토리 메서드를 통해 객체를 생성하도록 강제한다.

이유

팩토리 메서드를 통해 객체 생성 로직을 캡슐화하고, 유연한 방식으로 인스턴스를 제공할 수 있다. (특정 조건에 따라 객체를 다르게 생성하고 싶을 때 팩토리 메서드 패턴 사용)

예제

public class MyClass {
    private int value;

    private MyClass(int value) {
        this.value = value;
    }

    public static MyClass create(int value) {
        return new MyClass(value);
    }
}

불변 객체를 보장하거나 복잡한 초기화를 강제할 때

클래스가 불변 객체로 설계되었거나, 객체 생성 시 반드시 특정 초기화 과정을 거쳐야 하는 경우 생성자를 private으로 설정하고 정적 메서드나 빌더 패턴을 통해 객체 생성 유도

이유

객체 생성을 제어하여 불변성을 보장하거나 잘못도니 초기화 방지

예제

public class ImmutableClass {
    private final int value;

    private ImmutableClass(int value) {
        this.value = value;
    }

    public static ImmutableClass of(int value) {
        return new ImmutableClass(value);
    }

    public int getValue() {
        return value;
    }
}

생성자 내부에서 예외를 던지도록 설정하기도 한다.

private Singleton() {
    throw new IllegalStateException("이 클래스는 인스턴스화할 수 없습니다.");
}

예외 처리를 통해 리플렉션을 통한 생성자 호출을 막을 수 있다.

결론

예제에서 Database 생성자에 private를 사용한 이유는 Database가 데이터베이스 연결을 담당하는 것이 아니라 단순히 파일에서 데이터를 읽어오는 유틸리티 클래스이기 때문이라고 생각하였다. 따라서 Database 객체를 생성하는 것은 의미가 없어 private을 통해 제한하고, 메서드를 static으로 정의하여 객체를 생성하지 않고도 직접 호출할 수 있도록 하였다.

0개의 댓글