본문 바로가기

Programming/JAVA

[Effective JAVA 3S/E] 객체 생성과 파괴 - item1

개요

  • 객체를 만들어야 할 때와 만들지 말아야 할 때 구분
  • 올바른 객체 생성 방법과 불필요한 생성 피하기
  • 제때 파괴됨을 보장하며, 파괴 전 수행할 정리 작업 관리 요령

[1] 생성자 대신 정적 팩터리 메서드 고려하기

  • 클래스의 인스턴스 생성 방법
    • public 생성자를 통한 생성
    • 정적 팩터리 메서드(static factory method)
      - 클래스의 인스턴스를 반환하는 단순한 정적 메서드 임
      - 디자인 패턴과 일치하는 패턴은 없음

정적 팩토리 메소드 — 소중한 나의 개발일기 (tistory.com)

 

정적 팩토리 메소드

이펙티브 자바 3판 아이템 1. 생성자 대신 정적 팩토리 메소드를 고려하라 인스턴스 생성법 보통 클래스의 인스턴스를 생성하기 위해서는 public 생성자를 이용합니다. 이 생성자를 이용하여 인스

ddinsg73.tistory.com

public 생성자를 통한 생성의 문제점 [ 클래스 인스턴스 생성으로 인한 문제]

1. 생성된 인스턴스의 목적성

  • 어떤 객체가 어떤 목적으로 생성되었는지 나타낼 수 없다는 단점이 있음

  • 다행히 intelliJ는 각 Parameter의 명칭이 나타나 변수명으로 확인할 수 있으나, 기본적으로 직관성이 떨어짐

2. 매번 새로운 인스턴스의 생성의 필요가 없음

조건을 걸어보겠습니다. 만약 한 번 기록된 필드 값을 변경할 수 없는 불변 클래스에서 설농탕을 순두부찌개로 바꿔보겠습니다.

 

 

위에서 볼 수 있듯 인스턴스를 다시 생성해야 합니다. 그러면 Garbage가 늘어나겠지요. Hashcode를 조회하면 다른 값이 나타남을 확인할 수 있습니다.

 

Hashcode가 다르다는 것은 새로운 메모리가 할당된 것입니다.

 

3. 반환할 객체의 클래스가 존재하지 않으면 반환할 수 없다.

public 생성자를 이용한 방법에서는 해당 클래스의 인스턴스를 반환하는 것이 불가합니다.


정적 팩토리 메소드

장점

  • public 생성자(인스턴스 생성)을 통한 호출에서 생기는 문제점을 해결하기 위해 고안된 기술

  • 클래스의 인스턴스를 반환하는 static 메소드를 의미함 

  • 팩토리 메소드 내부에 이미 생성된 객체를 반환하거나, 새로운 객체 생성 가능

1. 이름 지정을 통한 public 생성자의 목적성 문제 해결 가능

  • 이름을 지정할 수 있어 생성된 인스턴스의 의미를 명시할 수 있음

  • 즉, 정적 팩터리 메서드는 이름만 잘 지은다면 반환될 객체의 특성을 쉽게 묘사할 수 있음

    [예시] BigInteger(int, int, Random)과 정적 팩터리 메서드인 BigInteger.probablePrime 중 어느 쪽이 소수인 BigInteger를 반환하는지 직관적으로 알 수 있음

2. 매번 새로운 인스턴스 생성의 문제 해결 가능

     즉, 불변 클래스의 인스턴스를 미리 만들드는 등의 불필요한 객체 생성을 피할 수 있음

  • Coke -> Apple -> Coke 하였을 때, Coke의 hashcode가 변하지 않았음을 확인할 수 있음
  • 이로 생성 비용이 큰 같은 객체가 자주 요청되는 상횡에서 성능을 상당히 끌어올려 줌

  • 대표적 예시: Boolean.valueOf(boolean)
  • 객체를 생성하지 않음
    • 반복되는 요청에 같은 객체를 반환하는 식으로 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아있게 할지 철저히 통제할 수 있음 :: 이와 같은 클래스를 인스턴스 통제(instance-controlled) 클래스라고 함
  • 같은 객체가 자주 요청되는 상황에 사용한다면 성능으 상당히 끌어올릴 수 있음
  • 인스턴스 통제 이유
    • 싱글턴으로 만들 수 있음
    • 인스턴스화 불가로 만들 수 있음
    • 불편 값 클래스에서 동치인 인스턴스가 단 하나뿐임을 보장할 수 있음
  • 인스턴스 통제의 이점

 

불변 클래스 이점

 

Item 17. 변경 가능성을 최소화하라

불변 클래스란? 그 인스턴스의 내부 값을 수정할 수 없는 클래스로, 인스턴스 내 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대로 변경되지 않는다. 예) 자바 플랫폼 라이브러리에는 St

bearstark.tistory.com

 

3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력을 가짐 (이해 안 감 ㅠ)

  • 반환 객체의 클래스를 자유롭게 선택할 수 있어 엄청난 유연성을 가짐
  • 이 유연성으로 API를 만들 때, 구현 클래스를 공개하지 않고도 객체를 반환할 수 있어 API를 작게 유지할 수 있음
    [하늘쓰 이해] 그러니까.. 사용하는 클라이언트는 내부 로직을 이해할 필요 없이 매개인자로 원하는 값을 잘 받아 올 수 있다는 건가?
    ==> ?? 그러면 4번이랑 같은 내용인데;;

  • API를 작게 유지한다. = 프로그래머가 별도의 문서를 찾아가며 실제 구현 클래스가 무엇인지 알아보지 않아도 됨
    [예시] java.util.Collections는 45개 클래스를 공개하지 않기에 API 외견을 작게 만들 수 있었음

  • 이로 정적 팩터리 메서드를 사용하는 클라이언트는 얻은 객체를 인터페이스만으로 다루게 됨

4. 입력 매개변수에 따라 다른 클래스 객체 반환 가능

  • 반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관 없음

  • 예시로 들자면..
    EnumSet 클래스는 public 생성자 없이 오직 정적 팩터리만 제공함. 이는 OpenJDK에서 원소의 수에 따라 두 하위 클래스 중 하나의 인스턴스를 반환함. 64개 이하면 long 변수로 관리하는 RegularEnumSet 인스턴스, 65개 이상이면 long 배열로 관리하는 JumboEnumSet의 인스턴스를 반환함

  • 위 예시에서 클라이언트는 팩터리가 주는 객체가 어느 클래스의 인스턴스인지에 상관 없이 EnumSet의 하위 클래스이기만 하면 됨
    즉, EnumSet이라는 상위 개념을 받은 하위 클래스를 받을 수 있으면 된다는 뜻!

  • 쉽게 얘기하자면 이는 리스코프 치환의 원칙을 따른 하위 클래스를 반환하면 된다는 것!
    단, 이는 입력된 매개변수에 따라 반환되는 클래스가 달라진다는 것임.

5. 반환할 객체의 클래스가 존재하지 않으면 반환할 수 없는 문제 해결

  • 서비스 제공자 프레임워크를 만드는 근간
    • 대표적: JDBC(Java Databse Connectivity)
    • 서비스 제공자 프레임워크에서의 제공자 => 서비스 구현체
    • 구현체들은 클라이언트에서 제공하는 역할을 프레임워크가 통제해 클라이언트를 구현체로부터 분리함
  • 서비스 제공자 프레임워크의 구성
    • 서비스 인터페이스 (service interface) : 구현체 동작 정의
    • 제공자 등록 API (provider registration API) : 구현체 등록
    • 서비스 접근 API (service access API) : 클라이언트가 서비스의 인스턴스를 얻을 때 사용
    • 서비스 제공자 인터페이스 (service provider interface) : 서비스 인터페이스의 인스턴스를 생성하는 팩토리 객체

    • 예시. JDBC
      - Connection : 서비스 인터페이스 역할
      - DriverManager.registerDriver : 제공자 등록 API 역할
      - DriverManager.getConnection : 서비스 접근 API 역할
      - Driver : 서비스 제공자 인터페이스 역할

 

단점

 

1. 정적 팩토리 메소드만 제공할 경우 하위 클래스를 만들 수 없음

 

  • 상속은 public이나 protected 생성자가 필요함
  • 정적 팩터리 메서드는 private 생성자로 인해 하위 클래스 생성을 할 수 없음

2. 정적 팩토리 메서드는 프로그래머가 찾기 어려움

 

  • API 설명에 명확히 드러나지 않아 사용자가 직접 정적 팩토리 메소드 방식 클래스를 인스턴스화할 방법을 알아야 함
  • 그렇기에 API 문서를 잘 작성해야 하며, 메서드 이름 또한 널리 알려진 규약을 따라 지는 방식을 택해야 함