반응형

이 글은 Effective Java를 참조하여 작성하였습니다.


클래스를 통해 객체를 만드는 방법은 생성자를 이용하는 것입니다. 하지만 public으로 선언된 정적 팩토리 메서드를 추가하는 방법으로도 객체를 생성할 수 있습니다. 여기서 말하는 정적 팩토리 메서드는 디자인패턴에 팩토리 메서드 개념과 다릅니다.


생성자 대신 정적 팩토리 메서드를 사용하는 첫 번째 장점은 생성자와는 달리 정적 팩토리 메서드에는 이름이 있다는 것입니다. 즉 이름이 있기 때문에 네이밍을 잘 한다면 사용하기 쉽고 가독성도 높아집니다. 생성자가 다양하다면 각각의 생성자 용도를 기억하기 힘들 것이고 (아니 절대로 불가능 할 것이다) API가 없다면 제대로 파악하기 힘들것입니다. 하지만 정적 팩토리 메서드에는 이름이 있으므로 이런 문제는 생기지 않기 때문에 같은 시그니처를 가지는 생성자를 여러개 정의해야 할 경우에는 생성자들을 정적 팩토리 메서드로 바꾸고 네이밍에 신경 써야 합니다.


정적 팩토리 메서드?? 그러면 그게 뭔데 ??

Object object = new Object(); 와 같은 구문을 이용해서

일반적으로 객체를 생성할 때는 생성자를 사용할 것입니다. 


Static Factory Method 는 public static method 로서 외부 클래스에서 바로 접근할 수 있는 method인것이죠. static의 개념이 없다면 static을 먼저 검색하고 오세요~!

class Human{

String name;

int age;

int height;

int weight;

//Constructor

public Human(String name, int age, int height, int weight)

{

this.name=name;

this.age=age;

this.height=height;

this.weight=weight;

}

public Human(String name, int age)

{

this.name=name;

this.age=age;

}

//Static static Factory Method

public Human allInfoHuman() {

return new Human("sw",26,179,75);

}

public static Human portionHuman() {

return new Human("sw",26);

}

}



두번째 장점은 생성자와는 달리 호출할 때마다 새로운 객체를 생성할 필요가 없다는 것입니다. 이는 경량 패턴과 유사한데 동일한 객체가 요청되는 일이 잦고, 객체 생성시 비용이 클 경우 적용하면 성능을 크게 개선시킬 수 있습니다. 사실 위 예제 소스에서도 new연산을 계속 쓰고 있습니다. 아래에 보는 예제 소스와 같이 미리 만들어둔 객체를 리턴할 수 있기 때문에 new와 같은 비용이 큰 연산의 횟수를 줄일 수 있습니다.


public static final BigInteger ZERO = new BigInteger(new int[0], 0);


private final static int MAX_CONSTANT = 16;

private static BigInteger posConst[] = new BigInteger[MAX_CONSTANT+1];

private static BigInteger negConst[] = new BigInteger[MAX_CONSTANT+1];


static {

    /* posConst에 1 ~ 16까지의 BigInteger 값을 담는다. */

    /* negConst에 -1 ~ -16까지의 BigInteger 값을 담는다. */

}


public static BigInteger valueOf(long val) {

    // 미리 만들어둔 객체를 리턴한다

    if (val == 0)

        return ZERO;

    if (val > 0 && val <= MAX_CONSTANT)

        return posConst[(int) val];

    else if (val < 0 && val >= -MAX_CONSTANT)

        return negConst[(int) -val];


    // 새로운 객체를 만들어 리턴한다

    return new BigInteger(val);

}



세번째 장점은 생성자와 달리 반환값 자료형의 하위 자료형 객체를 반환할 수 있다는 것입니다. 즉 반환되는 객체의 클래스를 훨씬 유연하게 결정할 수 있는 것이죠. 음.. 생성자는 리턴값이 없는데 정적 팩토리 메서드는 메서드입니다. 메서드라는 이름이 붙여진 이유에 대해 잠시 생각해 보면 리턴값이 있다는 것이고, 그 리턴값이 생성자처럼 자기 자신만이 아닌 하위 객체도 리턴할 수 있다는 뜻입니다. 


 package effectiveJava;


public class Regulation {

public static void main() {

Human h = Human.setSexInstance("man");

h.getInfo();

}

}


abstract class Human {

String name;

public abstract void getInfo();


public static Human setSexInstance(String sex) {

if (sex == "man")

return new Man();

else

return new Woman();

}

}


class Man extends Human {

public void getInfo() {

System.out.println("I'm man");

}

}


class Woman extends Human {

public void getInfo() {

System.out.println("I'm woman");

}

}


마지막 장점은 형인자 자료형 객체를 만들 때 편리하다는 것입니다. 이부분은 JDK1.7이후부터는 의미가 없을 듯 하네요


Map<String, List<String>> map = new HashMap<String, List<String>>();

이와 같은 소스를

public static <K,V> HashMap<K,V> newInstance() {

       return new HashMap<K,V>();

}

와 같은 메서드가 있으면 

Map<String, List<String>> map = HashMap.newInstance()와 같이 줄여서 사용할 수 있다는 것인데 JDK1.7부터는


Map<String, List<String>> map = new HashMap<>();을 지원하니까 말이죠


지금까지 장점에 대해 알아보았는데요 장점이 있다면 단점도 역시 존재합니다.


첫번째 단점은 critical하다고 생각되는데 정적 팩토리 메서드만 있는 클래스를 만든다면 public, protected로 선언된 생성자가 없기 때문에 하위 클래스를 만들 수 없다는 것입니다. 예시로 자바 컬렉션 프레임워크에 포함된 기본 구현 클래스들의 하위 클래스는 만들 수 없다는 예시가 있네요!


두번째 단점은 정적 팩토리 메서드가 다른 정적 메서드와 확실하게 구분되지 않는다는 것입니다. 물론 API문서를 본다면 생성자와 다른 메서드는 뚜렷하게 구별되지만 정적 팩토리 메서드는 그렇지 않습니다. 때문에 더더욱 메서드 네이밍을 잘해야 겠죠.


하지만 정적 팩토리 메서드가 주는 장점은 분명 있으며, 생성자와 용도가 같다고 생각하면 큰 오산입니다. 그 차이와 장단점을 이해해서 정적 팩토리 메서드가 효과적인 경우 고려해보고 무조건적으로 public 생성자를 만드는 것을 삼가해야 합니다.


반응형

+ Recent posts