객체와 생성과 파괴 item01

Bong2·2022년 3월 25일
0

이팩티브자바

목록 보기
1/9

이팩티브 자바 3판 책을 읽고 정리하는 글입니다.!

생성자 대신 정적 팩터리 메소드를 고려하자

정적 팩터리 메서드란?
static factory method : 객체 생성의 역할을 하는 클래스 메서드라는 의미
팩터리 메서드의 디자인패턴이랑 별개의 이야기이다.!!

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
} 

public 생성자를 사용해서 객체를 생성하는 전통적인 방법말고 정적 팩터리 메소드를 사용해서 클래스의 인스턴스를 만든다.

장점

1.이름을 가질 수 있다.

public class Foo {
    String name;
	String address;
	
    public Foo(){}
    
    public Foo(String name){
        this.name=name;
    }
    //생성자는 하나의 시그니처만 ERROR
    public Foo(String address){
    	this.address=address;
    }
    
    public static Foo withName(String name){
        return new Foo(name);
    }
	
    public static Foo withAddress(String address){
    	Foo foo=new Foo();
        foo.address=address;
        return foo;
    }
    
    public static void main(String[] args) {
        //생성자를 이용해서 
        Foo foo=new Foo("bong");
        //이름을 가지기 때문에 메서드이름으로 확실히 무슨 기능을 하는지 알기 편함
        Foo.withName("bong");
    }
}

생성자에 넘기는 매개변수와 생성자 자체만으로는 반환될 객체의 특성을 제대로 설명 못하는 경우에, 잘만든 이름을 가진 static 팩터리를 사용하는 것이 읽기 편하고 쉽다.
ex) BigInteger.probblePrime
또한 생성자는 시그니처에 제약이 있다. 똑같은 타입을 파라미터로 생성자 두개를 만들 수 없다.

2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.

public class Foo {
    String name;
	String address;
	
    public Foo(){}
    
    public Foo(String name){
        this.name=name;
    }
	
    private static final Foo Test=new Foo();
    public static Foo withName(String name){
        return new Foo(name);
    }
	
    public static Foo getFoo(){
    	return Test;
    }
    
    public static void main(String[] args) {
        //호출될때 마다 새로운 인스턴스 생성
        Foo foo=new Foo("bong");
        Foo foo2=Foo.getFoo();// 매번 새로운 객체가 나오는 것이 아닌 Test객체만 생성
    }
}

불변 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다.
ex)Boolean.valueOf(boolean)

3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

객체의 클래스를 자유롭게 선택할 수 있게 하는 '엄청난 유연성'을 가진다.
반환 타입(리턴 타입)의 하위 타입의 인스턴스를 만들어줘도 되니까, 리턴 타입은 인터페이스로 지정하고 그 인터페이스의 구현체는 API로 노출시키지 않지만 그 구현체의 인스턴스를 만들어 줄 수 있다..
ex)java.util.Collections : 45개 클래스를 공개하지 않기 때문에 API 외견을 훨씬 작게 만들 수 있었다. 인터페이스 뒤에 감쳐줘 있고 그럼으로서 public으로 제공해야 할 API를 줄였을 뿐아니라 개념의 수와 난이도를 낮췄다.!!
private static 메소드는 자바 9부터 추가 가능! -> Why Use? : 스코프를 위해 만들어 진것으로 생각된다.

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

    
    public static Foo getFoo(boolean flag){
        return flag ? new Foo() : new BarFoo();
    }
    
    public static void main(String[] args) {
        Foo.getFoo(false); //flag에 따라 리턴하는 객체의 클래스가 매번 달라진다.
        
    }

    static class BarFoo extends Foo{

    }

반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다. EunmSet 클래스는 public 생성자 없이 오직 public static 메서드, allOf(), of() 등을 제공한다. enum 타입의 개수에 따라 ReqularEunmSet or JumboEnumSet으로 달라진다.

그런 객체 타입은 노출하지 않고 감춰져 있기 때문에 추후에 JDK의 변화에 따라 새로운 타입을 만들거나 기존 타입을 없애도 문제가 되지않는다.

5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

장점 3,4와 비슷한 개념. 이러한 유연성은 서비스 제공자 프레임워크를 만드는 근간이 된다.
서비스 제공자 프레임워크는 서비스의 구현체를 대표하는 서비스 인터페이스와 구현체를 등록하는데 사용하는 제공자 등록API 그리고 클라이언트가 해당 서비스의 인스턴스를 가져갈 때 사용하는 서비스 액세스 API 필수이다. 서비스 인터페이스의 인스턴스를 제공하는 서비스 제공자 인터페이스를 만들 수 도 있는데 그게 없다면 리플렉션을 이용!!

ex) JDBC : DriverManager.registerDriver() -> 제공자 등록API DriverManager.getConnection()-> 서비스 액세스 API
Driver : 서비스 제공자 인터페이스

단점

1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.

Collections 프레임워크에서 제공하는 편의성 구현체 java.util.Collection을 상속할 수 없다.
상속보다 컴포지션을 사용하도록 유도하고 불변 타입으로 만들려면 이 제약을 지켜야 한다는 점에서 오히려 장점으로 받아들일 수도 있다.

2.정적 팩터리 메서드는 프로그래머가 찾기 어렵다.

생성자처럼 API 설명에 명확히 드러나지 않으니 사용자는 정적 팩터리 메서드 방식 클래스를 인스턴스화 할 방법을 알아내야한다. -> javadoc 상단에 모아서 보여주지만 static 팩터리 메서드는 API 문서에서 특별히 다뤄주지 않는다. 그래서 인터페이스 나 클래스 문서 상단에 팩터리 메서드에 대한 문서를 제공해야 좋다.

참고

https://github.com/keesun/study/blob/master/effective-java/item1.md

profile
자바 백엔드 개발자로 성장하자

0개의 댓글

관련 채용 정보