[이펙티브 자바] 객체의 생성과 파괴 Item4 - 인스턴스화를 막으려거든 private 생성자를 사용하라

이성훈·2022년 3월 14일
1

이펙티브 자바

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

이펙티브 자바의 첫 시작은 객체를 생성하고 파괴하는 것에 대한 고찰이다.

"2장 - 객체의 생성과 파괴" 는 다음과 같은 기준으로 맥락을 잡고 있다.

  • 객체를 만들어야 할 때는 언제인가
  • 객체를 만들지 말아야 할 때는 언제인가
  • 올바른 객체 생성 방법은 무엇인가
  • 객체의 불필요한 생성을 피하는 방법은 무엇인가
  • 객체를 제 때에 파괴시키는 방법은 무엇인가
  • 파괴 전에 수행해야 할 정리 작업을 관리하는 요령이 있는가

위와 같은 맥락을 계속 기억하며 공부하자.


  • Item1. 생성자 대신 정적 팩터리 메서드를 고려하라.
  • Item2. 생성자에 매개변수가 많다면 빌더를 고려하라.
  • Item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라.
  • Item4. 인스턴스화를 막으려거든 private 생성자를 사용하라.
  • Item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라.
  • Item6. 불필요한 객체 생성을 피하라.
  • Item7. 다 쓴 객체 참조를 해체하라.
  • Item8. finalizer와 cleaner 사용을 피하라.
  • Item9. try-finally 보다는 try-with-resources를 사용하라.




<"인스턴스화를 막으려거든 private 생성자를 사용하라">


다행이도 이번 제목은 그렇게 이질감이 들지는 않는다.

조금 가볍게 생각해볼수 있는 Item 인것 같다.



  • 배경

우리는 "객체 지향 프로그래밍 (Object Oriented Programming : OOP)"
이라는 단어를 참 많이 들어봤다.


당시는 "마구 잡이식 개발 방식"에서 goto 문을 사용하지 않기로 약속한
"절차 지향 방법론"이 주를 이루던 때였다.

하지만 코드 수정이 어려워 재사용이 힘들다는 점에서 너무 큰 문제점을 가지고 있었고,

결국 코드를 재사용 하기 좋게 붙였다 뗐다 할 수 있는 모듈(객체 : Object) 단위로 개발하자는 의견이 나온다.

그때부터 큰 변화의 바람이 불기 시작했고,
바로 그것이 오늘날까지 강력하게 영향을 발휘하고 있는 객체 지향 방법론이다.




  • 인스턴스화 하지 않는 객체?

갑자기 이 얘기를 왜 했을까?

결과적으로 OOP의 핵심은 코드의 재사용이다.

이 목적을 위해서 우리는,
일단 어떤 틀의 형태인 클래스를 만들고,
그를 통해 인스턴스를 만들어 사용하는 방식을 취하기로 했다.


그런데 간혹가다 단순히 정적 메서드와 정적 필드 (쉽게 말해 응용 가능성이 없는),
만을 담은 클래스를 만들고 싶을 때가 있다.

마치 C언어에서의 함수 (Function)처럼 말이다.


사실 객체 지향 방법론의 사고방식에서 벗어난 행동이라 부정적으로 보일수 있지만, 나름 필요할 때가 있다.

< 정적인 클래스가 도움이 될 때 >

  • java.lang.Math 나 java.utils.Arrays 처럼,
    기본 타입 값이나 배열 관련 메서드들을 모아놓을때

  • java.utils.Collections 처럼 특정 인터페이스의 구현체들에 대하여,
    그것들을 생성해주는 정적 팩토리 메서드들을 모아놓을 때

  • final 클래스와 관련한 메서드들을 모아놓을때

어쨋든 OOP에서도 그런게 필요한 경우가 있다는 말이다.

여기서 문제는, 인스턴스를 만들어 쓰지 않을 정적 클래스라면
아예 인스턴스를 못만들게 막아줘야 한다는거다.

단순히 생성자를 안만드는 걸로는 안된다.
생성자를 안만들면 어차피 컴파일러가 public으로 된 default 생성자를 만든다.




  • 추상 클래스로 만들면?

이 방법 역시도 별다른 도움이 되지는 못한다.

그것을 상속받아 하위 클래스를 만든다음 인스턴스화 하면 그만이지 않은가.

오히려 추상 클래스로 만들었다가는, 그걸 본 개발자들이
'아 이건 상속받아서 인스턴스화 해서 쓰라는건가?'
라고 생각할 수도 있다.




#   private 생성자


위 문제를 해결하는 방법은 그렇게 어렵지 않다.

private 생성자로 만들면 클래스의 인스턴스화를 막을 수 있다.


다음의 예제 코드를 보자.

	private UtilityClass()
	{
  		throw new AssertionError();
	}
  	...

UtilityClass는 현재 private으로 선언되었기 때문에,

이를 만든 사람이 내부적으로 인스턴스를 만들려고 하지 않는 이상
외부에서 인스턴스화를 만드는건 불가능하다.


위에서 AssertionError는 혹시라도 클래스 안에서 실수로 생성자가 호출됨을 방지하는 역할이다.


또한 이 방식은 생성자가 private 이므로 상속 또한 불가능하게 한다.

기본적으로 상속을 받는 하위 클래스는 상위 클래스의 생성자를 호출할 수 있어야 하는데,

private으로 선언됬으니 하위 클래스가 상위 클래스에 접근할 길이 없다.

profile
IT 지식 공간
post-custom-banner

0개의 댓글