일반적으로 객체를 생성할 때는 생성자 ( Constructor ) 을 이용하죠? Object object = new Object(); 와 같이.. Static Factory Method 는 public static method 로서 외부 클래스에서 바로 접근할 수 있는 method 로, 생성자의 역할을 하는 녀석입니다.
Static Factory Method 를 쓰면 뭐가 좋은데요?
1. 생성자와는 달리 자기 나름의 이름을 가질 수 있습니다.
생성자는 Class 이름밖에 못 갖습니다. 예를 들어 Blog 라는 클래스를 만들면 생성자 이름은 무조건 Blog() 여야 하죠. 물론 Parameter 갯수와 종류는 달라 질 수 있지만, 이름 자체는 Blog 여야만 합니다. 이게 무슨 장점이냐고요? 그냥 Blog() 를 호출하는 것보다 반환하는 객체를 더 잘 나타낼 수 있습니다. 예를 들어봅시다. 다음과 같은 constructor 가 있습니다.
public Blog(); public Blog( String powerBloggerID );
둘 다 Blog 라는 이름으로 부르기 때문에 String parameter의 이름인 powerBloggerId 를 알기 전까지는
저 constructor의 역할을 정확히 알기가 힘듭니다.
게다가 모두가 다 저 위치에 변수 명을 powerBloggerID 라고 명시할거라 단언할 수도 없죠.
그렇게 되면 실제 구현 코드를 분석하거나 document 를 참조하는 수고가 더 들게 됩니다.
하지만 이번엔 static factory method 를 이용해봅시다.
public static Blog newBlogInstance(); public static Blog newPowerBlogInstance( String powerBloggerID );
둘 다 Blog 를 return 받지만, 가독성이 훨씬 늘어났습니다. 어떤 성질의 녀석을 return 하는지 한번에 알 수 있죠? 이름이 길어지겠지만, powerBloggerID 라는 변수명을 보장할 수 없는 경우를 고려한다면 두번째 static factory method의 이름은 newPowerBlogInstanceWithBloggerID( String powerBloggerID ); 요런 형태도 괜찮을 것 같습니다.
2. 중복 Signature Constructor의 형태가 가능합니다.
Constructor 가 중복 signature 를 가지지 못한다는 것은 이미 알고 계시죠? [Constructor의 중복 Signature에 대해 모른다면..]
(함수의 Signature) = (함수 이름) + (입력 매개변수들) + (출력 매개변수) 입니다.
함수 중 외부로 노출되는 part라고 보시면 되겠습니다. Constructor 는 물론이고, 모든 함수는 동일 Signature를 지원하지 않습니다. 엄밀히 이야기한다면 출력 매개변수 ( Return Type )은 고려대상에 포함되지 않습니다. 즉 다음 2개의 함수는 공존할 수 없습니다.
int getResult();
String getResult();
Constructor 입장에서 이야기 한다면 다음 2개의 함수는 공존할 수 없습니다.
Blog Blog( String bloggerID );
Blog Blog( String blogName );
하지만 static factory method 를 사용하면 중복 signature 형태도 가능합니다. 예제를 통한 학습이 정말 빠르니깐, 힘들더라도 또 예를 들어볼까 합니다.
public Blog(); public Blog( String powerBloggerID ); public Blog( String blogName );
Constructor 를 사용할 경우 2번째와 3번재 있는 Constructor 는 동시에 사용할 수 없습니다. 다시 말해 동시에 정의할 수 없고, compile 에러가 나게 되죠. 하지만 static factory method를 사용하면 다 가능합니다.
public static Blog newInstance(); public static Blog newPowerBlogInstanceWithBloggerID( String powerBloggerID ); public static Blog newInstanceWithBlogName( String blogName );
물론 저는 급작스럽게 예제를 만드느라 naming 이 누구나 공감할만한 것은 아니라고 생각하지만. 이런식으로 factory method 를 만들 때는 항상 naming 을 고려해야 합니다. 잘못된 naming 으로 확장한 static factory method 는 오히려 혼란을 줄 수도 있죠.
3. 생성자와 달리 호출될 때마다 새로운 객체(instance)를 생성할 필요가 없습니다.
생성자의 목적은 새로운 객체를 생성하는 것이죠. 따라서 생성자를 호출 할때마다 객체 생성을 피할 수 없습니다. ( 생성자에서 에러가 난 경우, 이런 예외 사항들은 우선 접어두고.. ) 하지만 static factory method 를 사용하면 기존에 만들어져 있는 객체를 반환하는 방식 ( Singleton )등을 통해 불필요한 객체 생성을 피할 수 있습니다. 이 경우에 equals(Object) 대신에 == 를 통해서 비교를 할 수 있어 성능 향상도 기대할 수 있죠. 객체 생성 계속 생성하면 뭐가 안 좋냐구요? 먼저 새로운 객체 생성은 new 명령어를 통해 만들어 지기 때문에 heap 메모리를 차지하게 됩니다. 생성자에서 아무 것도 안 하는 경우에는 "큰" 문제는 안 되겠지만, 생성자에서 무엇을 하느냐에 따라, 경우에 따라서는 객체 하나가 차지하는 메모리 양이 어마어마 할 수 있습니다. 모바일 환경에서는 특히 memory가 부족하기 때문에 불필요한 객체 생성은 피해야 합니다. 게다가 새로운 객체 생성은 꽤나 비중있는 operation입니다. 구현에 따라서 생성자에서 엄청난 작업을 할수도 있습니다. 물론 생성자에서 엄청난 작업을 하는 것이 권장되지 않는 사항이지만, 남이 만든 class 를 사용해야만 하거나, constructor에서 꼭 작업해야만 한다는 등의 경우를 베재할 수 없습니다.
4. static factory method 는 반환하는 타입의 subtype 도 반환할 수 있습니다.
일반 Constructor 는 자기 자신의 type 만을 반환할 수 있는 반면 static factory method 는 반환하는 타입의 subtype도 반환할 수 있습니다. Blog의 Subtype 인 ImageBlog와 VideoBlog가 있다는 가정하에 예를 한번 들어봅니다.
class ImageBlog extends Blog{ ... } class VideoBlog extends Blog{ ... }
Constructor는 무조건 자기 자신인 Blog만 Return 할 수 있기 때문에 예를 들 수 없고, static factory method의 경우만 예를 들어 보았습니다.
public static Blog newInstance( String type ){
if ( type.equals( "Image" )
return new ImageBlog();
else if ( type.equals( "video" )
return new VideoBlog();
return null;
}
이런 게 가능하다는 말이죠.
그럼 static factory method 의 단점은 없어요?
Naming 만 충실히 하면 큰 단점은 없다고 보여집니다. 하지만 naming 이 부실할 경우 다른 static method 들과 static factory method를 쉽게 구별하지 못 할 수도 있죠. 게다가 정확한 naming 없이 필요 이상으로 과다하게 static factory method 를 만들 경우에는 혼란을 야기하기도 합니다. 그래서 naming을 할 때 보통 newInstance, getInstance 등을 function name 에 곧잘 쓰곤 합니다.
3. Summary
- Static Factory Method 는 constructor 의 역할을 하는 public static function 이다. ex) public static Blog newInstance(){ .. } - 다음과 같은 장점이 있다.
1. 생성자와는 달리 자기 나름의 이름을 가질 수 있다. 2. 중복 Signature Constructor의 형태가 가능하다. 3. 생성자와 달리 호출될 때마다 새로운 객체(Instance)를 생성할 필요가 없다. 4. 반환하는 타입의 subtype도 반환 가능하다.
댓글