kok202
[2019.03.09] 이팩티브 자바 3판 (1,2장 : 객체 생성 파괴)

2019. 3. 6. 00:33[공부] 독서/이펙티브 자바 3판

1장

코드는 복사되는게 아니라 재사용 되야한다.

책의 목표 : 명료성, 단순성

 

 

 

 

객체를 생성하는법

  • 생성자
  • 정적 팩토리 메소드
    정적 팩토리 메소드의 의미 : (메소드) 인데 클래스 밖에서 new 없이 메소드를 호출하기 위해선 static(정적)이여야 한다. 그리고 객체를 만들어주는 메소드이니까 (팩토리) 이다. 

    GoF 패턴에는 존재하지 않는 기법이다. 팩토리 메소드 패턴과는 다르다.  가능하면 생성자보다 이 방법을 사용하라.

    참고
    템플릿 메소드 패턴 : 메소드들이 템플릿화 되어 실행순서가 알고리즘 화 되도록 하는 것 onPrepare() -> onRun()-> onPost()
    팩토리 메소드 패턴 : 팩토리 메소드 패턴은 잘못 붙여진 이름이라고 함
    템플릿 팩토리 클래스 패턴이라고 생각하는게 좋을 듯 싶다.
    팩토리 클래스가 하위 객체를 생성하는 메카니즘이 똑같을 때 사용한다. 팩토리 메소드 패턴의 목적은 상위 클래스가 create 할 때 받는 매개 변수를 통해 어떤 클래스를 생성할지 하위 클래스를 결정하여 생성해주는 것이다.

    장점 + 생성자는 이름이 없어서 이게 뭘 생성하는지 몰랐다. 하지만 이름있는 함수를 호출하므로 이제 알 수 있다.
    장점 + 싱글톤과 같이 중복되는 인스턴스에 대응하여 객체 생성 자체를 피할 수도 있다.
    장점 + 입력 매개변수에 따라 다른 클래스를 반환 할 수 있다.
    단점 - public, protected로 생성자를 만들지 않기 대문에 정적 팩토리 메소드를 적용한 클래스를 상속할 수 없다.
    단점 - 뭐가 객체를 생성해주는 메소드인지 찾기 힘들다.

    대표적으로 많이 사용하는 객체 생성 키워드
    • create : 인스턴스 생성
    • from : 하나의 매개변수를 받아 인스턴스 반환
    • of : 여러개 매개변수를 받아 인스턴스 반환
    • valueOf : from, of의 더 자세한 버전
    • getInstance, getType : 매개변수의 인스턴스를 반환
    • newInstance, newType : 매개변수의 인스턴스를 반환, 매번 새로 생성을 보장
  • 빌더 패턴
    빌더 패턴은 연쇄 호출 후 build()하는 방식이다.
    단 이 방식은 메소드 연쇄가 발생할 수 있다. (매소드 연쇄 = fluent API => 너무 길면 train wreck)
    생성자의 매개변수가 많을 때 효율적이다. (적어도 4개 이상)
    멤버변수의 유효성 검사후 오류일 경우 IllegalArgumentException 을 던질 수도 있다.

    빈 객체를 만들고 내립다 setter를 사용하는 자바 빈즈 패턴과는 다르다. 자바 빈즈 패턴은 일관성이 무너진 상태다.
    빌더를 가진 클래스를 상속받는 클래스를 생성하는 법도 익혀두자.

    참고 : 제네릭에서는 this를 반환할 수 없다. 이 때 simulated self-type 구조를 이용하면 해결 가능하다.
Pizza ∋ Builder<T extends Builder<T>>

CombiPizza ∋ Pizza.Builder<Builder>
ShrimpPizza ∋ Pizza.Builder<Builder>
 

 

 

싱글톤

싱글톤 클래스의 생성자는 무조건 private 이여야한다.

싱글톤을 만드는 방법

  • public 필드 방식
    public static final Singleton INSTANCE = new Singleton();
    + 코드가 간결하다.
    + 싱글턴임이 확실히 보인다.
  • 정적 팩토리 메소드 방식
    private static final Singleton INSTANCE = new Singleton();
    public static Singleton getInstance() { return INSTANCE; }
    + 스레드 별로 다른 INSTANCE를 넘겨 줄 수 있게 할 수도 있다. (장점인가?)
    + 제네릭 싱글톤 패턴으로 변형할 수 있다.
    + 초기화 지연을 적용하게 할 수 도 있다. (그런데 초기화 지연은 프로그램의 복잡도만 올리고 성능도 그리 개선되지 않는다.)
  • Holder 방식
    public static Singleton getInstance() { return SingletonHolder.INSTANCE; }
    private static class SingletonHolder { public static final Singleton INSTANCE = new Singleton(); }
    + 보통 사용하는 방식이다.
    - 리플렉션 API 를 이용한 공격이 존재한다.
  • 싱클톤을 클래스가 아니라 enum으로 만드는 방법
    + 리플렉션 API 를 이용한 공격도 방어가 된다.
    + 저자는 이 방법을 추천한다.
    - 단 이 방법을 사용하면 싱글톤 상속은 안된다.

 

 

 

상황별 팁

  • 어떤 클래스를 사용자가 객체로 만들어서 못쓰게 하고 싶으면 생성자를 private으로 지정해버려라
    인터페이스나 추상 클래스로 만드는 것은 답이 아니다. 사용자가 해당 클래스를 상속해서 쓸 수도 있다.
  • new를 사용하는 것 보다. 의존 객체 주입 패턴을 적극 활용하라. 유연하고 테스트 용이한 코드가 될 것이다.
  • 객체를 생성하는 것조차 자원 낭비라는 것을 유념해라
  • 오토박싱을 피해라 이것도 객체를 만드는 행위다.(Long number -> long number)
  • 객체를 다 사용하면 참조 해지를 통해 GC에 걸리도록 해라.
    • 참조 해지 방법
    • 참조 변수에 null을 할당한다.
    • 참조 변수를 Scope이 다 되서 자동으로 해지 되게 한다.
  • finalizer와 cleaner 사용을 피해라. C++의 파괴자와는 다른 개념이다. finalizer와 cleaner 의 호출은 gc에 의해 결정되므로 실행 시점, 실행 여부를 알 수 없다. 이들은 gc의 성능에도 영향을 주고 보안 이슈도 있다.
  • 자바에는 close를 해야하는 자원이 너무 많다. 전통적인 방식에서는 try-finally를 많이 사용했다. 그러나 이는 너무 지저분하다 최근에는 try-with-resources 방식을 지원한다 이를 사용하자. 훨씬 읽기 쉽다. 단) 이 방식을 사용하려면 close해야하는 자원이 AutoCloseable을 반드시 implements 해야한다.

 

try-finally

InputStream in = new InputStream(srcPath) 
try { 
	//...
} 
finally {
	if (in != null)
		in.close();
}

try-with-resources

try (InputStream in = new InputStream(srcPath)){
	//...
}