reference: "모던 C++ 디자인패턴" / 디미트리 네스터룩
객체를 생성하는 팩토리 메서드를 클래스 내부에 static으로 선언하는 것이 아닌 별도의 클래스에 몰아넣을 수도 있다. 이러한 클래스를 팩토리 클래스라 한다.
이러한 팰토리 클래스를 만들기 위해 먼저 앞선 글의 예시가 되었던 Point 클래스를 아래와 같이 다시 정의한다.
class Point {
private:
float x, y;
// 클라이언트가 직접 생성자를 호출하는 경우가 없게 한다.
Point(const float x, const float y)
: x{ x }, y{ y }
{}
friend class PointFactory;
// cf) friend 선언은 클래스 내에 어디든 위치할 수 있음
};
먼저 클라이언트가 생성자를 직접 호출하는 일이 없게 만든다. 이를 위해 생성자를 private 접근 지정자로 제한한다.
(필수적인 부분은 아니지만 클라이언트가 일관된 접근 방법으로 사용하도록 한다.)
그리고 Point 클래스가 팩토리 클래스를 friend로 선언하여 팩토리가 Point의 생성자에 접근할 수 있게 한다.
이제 팩토리 클래스를 구현하면 다음과 같다.
class PointFactory {
public:
static Point NewCartesian(float x, float y) {
return Point{ x, y };
}
static Point NewPolar(float r, float theta) {
return Point{ r * cos(theta), r * sin(theta) };
}
};
friend 키워드가 없는 언어는 내부 팩토리 방식으로 팩토리 클래스를 구현해야 한다. 대표적으로 Java가 그렇다.
C++에서도 내부 팩토리로 구현할 수 있는데, 이것의 장점은 생성할 타입의 내부 클래스이기에 private 멤버들에 자동적으로 자유로운 접근 권한을 가진다는 점이다.
class Point {
private:
float x, y;
// 클라이언트가 직접 생성자를 호출하는 경우가 없게 한다.
Point(const float x, const float y)
: x{ x }, y{ y }
{}
// friend class PointFactory;
// cf) friend 선언은 클래스 내에 어디든 위치할 수 있음
// => 내부 팩토리로..
class PointFactory {
private:
PointFactory() {}
public:
static Point NewCartesian(float x, float y) {
return { x, y };
}
static Point NewPolar(float r, float theta) {
return { r * cos(theta), r * sin(theta) };
}
};
public:
// 클라이언트에 외부 팩터리 노출
static PointFactory factory;
};
이러한 내부 팩토리를 사용하는 클라이언트 코드는 아래와 같다.
auto pp1 = Point::factory.NewCartesian(2, 3);
auto pp2 = Point::factory.NewPolar(3, 60);
원본 객체 자체에서 팩토리를 얻을 수 있게 하면 API의 사용성에 큰 도움을 줄 수 있다. Point 객체를 얻고 싶은 사용자가 Point::
라 입력하면 코드 자동완성 목록을 통해 팩토리에 대한 정보를 얻을 수 있다.