Programming/Java

[Youtube][이팩티브 자바] #1 생성자 대신 static 팩토리 메소드를 고려해 볼 것

bisi 2020. 5. 6. 20:27

백기선님의 유투브 강의 내용을 정리하였습니다.

 

백기선님 Github 바로가기

Youtube 바로가기

 

 

 

강의내용 필기 


Item 1. 생성자 대신 static 팩토리 메소드를 고려해 볼 것.

 

public 생성자를 사용해서 객체를 생성하는 전통적인 방법 말고, 

아래와 같이 public static 팩토리 메소드를 사용해서 해당 클래스의 인스턴스를 만드는 방법도 있다. 

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

 

이런 방법에는 각각 장단점이 있는데 아래 장점 5가지, 단점 2가지로 정리하였다. 

 

장점 1. 이름을 가질 수 있다. 

 

생성자는 만들면 보통 클래스 이름과 동일하게 만들어야 한다.  

# public 생성자를 사용해서 객체를 생성하는 전통적인 방법

public class Foo {

    String name;

    String address;

    public Foo() {
    }

    public Foo(String name) {
        this.name = name;
    }
}

 

# public static 팩토리 메소드를 사용해서 해당 클래스의 인스턴스를 만드는 방법

 public class Foo {

    String name;

    String address;

    public static Foo withName(String name) {
        return new Foo(name);
    }
    
}

 

두번째 방법이 읽기가 어떤 value를 받는지 method에 보이므로 읽기 더 편하다 

 public static void main(String[] args) {
        Foo foo = new Foo("keesun");
		// 읽기 편함. 어떤 이름인지 보임.
        Foo foo1 = Foo.withName("keesun");
 }

 

그리고 똑같은 생성자를 두개 만들수 없는 것을 이름을 수정한 public static 메소드를 사용하면 두 개로 생성 가능하다. 

 

# 아래는 안되는 케이스 

public class Foo {

    String name;

    String address;
    
    
    public Foo withName(String name) {
       this.name = name;
    }

    public Foo withAddress(String address) {
         this.address = address;
    }
    
 }

 

# 가능한 케이스 

public class Foo {

    String name;

    String address;

    public Foo() {
    }

    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;
    }
}

 

 

 

장점 2. 반드시 새로운 객체를 만들 필요가 없다.

 

아래의 예시를 참고하면, boolean이라는 새로운 객체를 만들어서 return 안해줘도 된다.

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

 

미리 만들어준 인스턴스 또는 캐시해두 인스턴스를 반환 가능하다.

Foo라는 객체를 매번 호출할수 있지만, 원한다면 아래와 같이 구현도 가능하다. 

 

public class Foo {

    String name;

    String address;

    public Foo() {
    }

    private static final Foo GOOD_NIGHT = new Foo();

    public static Foo getFoo() {
        return GOOD_NIGHT;
    }

    public static void main(String[] args) {
        Foo foo3 = Foo.getFoo();
    }
}
    


 

 

장점 3. 리턴 타입의 하위 타입 인스턴스를 만들 수도 있다. 

 

밖에서 보는 클라이언트의 입장에서는 인터페이스만 보고 코딩을 한다. 

 

java.util.Collections 가 그 예에 해당된다.

인터페이스의 구현체가 45개이지만, 구현체들은 전부 non-public이다. 즉, 인터페이스 뒤에 감춰져 있고, 그럼으로써 public으로 제공해야할 API로 줄고, API를 사용할때 알아야할 개념의 개수와 난이도도 줄어 들었다. 

 

자바 8부터는 public static 메소드를 interface에 추가할 수 있다. 하지만 private static 메소드는 자바 8이 아닌 자바 9부터 제공한다. 

 

 

장점 4. 리턴하는 객체의 클래스가 입력 매개변수에 따라 매번 다를 수 있다.

 

장점 3의 연장선, flag에 따라 다른 객체를 리턴한다.

유연성에 대해 언급!

public class Foo {

    String name;

    String address;

    public Foo() {
    }
    
    public static Foo getFoo(boolean flag) {
        return flag ? new Foo() : new BarFoo();
    }
    
    public static void main(String[] args) {
        Foo foo3 = Foo.getFoo(false);
    }
    
    static class BarFoo extends Foo {
    }
}

 

 

장점 5. 리턴하는 객체의 클래스가 public static 팩토리 메소드를 작성할 시점에 반드시 존재하지 않아도 된다. 

 

장점3으로부터 시작되는 유연함!

 

public static Foo getFoo(boolean flag) {
        Foo foo = new Foo();

        // TODO 어떤 특정 약속 되어 있는 텍스트 파일에서 Foo의 구현체의 FQCN 을 읽어온다.
        // TODO FQCN 에 해당하는 인스턴스를 생성한다.
        // TODO foo 변수를 해당 인스턴스를 가리키도록 수정한다.

        return foo;
    }

드라이버 마다 다르다. connection 은 static method이고,  서비스 프레임워크를 공급받는것. 

 

 

단점 1. public 또는 protected 생성자 없이 static public 메소드만 제공하는 클래스는 상속할 수 없다. 

 

상속을 필요하지 않아도됨..

단점으로 애매... 장점이 될수도..

 

단점 2. 프로그래머가 static 팩토리 메소드를 찾는게 어렵다. 

 

자바독을 보면, 

SpringApplication static method 주석을 달아야 한다. summary 없다.