팩토리 메서드 패턴 (Factory Method Pattern)
팩토리 패턴의 확장 = 팩토리 + 템플릿 메서드
추상화
팩토리 메서드 : 팩토리 패턴을 추상화로 확장한 패턴이다.
추상 개념
- 추상 (Abstract)
- 중요한 부분만 분리하여 이해하기 쉽게 만드는 작업
= 코드를 요약한다.
= 세부사항을 분리한다.
- 파악하려는 기능이 요약된 정보가 있다. ⇒ 이해에 용이하다.
- 추상적 개념 도입 = 객체의 동작을 보다 쉽게 파악하기 위함이다.
코드 요약
- 추상화 = 코드 요약
- 요약 시 상세한 내용을 무시한다.
- 요약된 정보만으로 실 구현 코드를 상세히 보지 않고도 동작을 쉽게 이해할 수 있다.
- 추상화 작업
- 요약된 정보와 실제 구현부 분리
⇒ 추상 클래스를 이용하여 구조에서 정보 부분만 분리한다.
#include <iostream>
using namespace std;
#ifndef OBJECT_H
#define OBJECT_H
class Object
{
public:
virtual string get_name() = 0;
virtual void set_name(string n) = 0;
protected:
string name;
};
#endif
using System;
abstract class Object{
private string name;
public virtual void setName(string n);
public virtual string getName();
}
패턴 확장
팩토리
- 기존 팩토리 패턴
추상화
- 기존 팩토리 패턴에 추상화를 통해 패턴을 확장 구현한다.
- 팩토리에 추상화를 결합 시,
- 팩토리 메서드 패턴
- 추상 팩토리 패턴
위의 두 가지 형태로 구분 가능하다.
- 기존 팩토리에 추상화 및 추상 클래서 적용
#include "../objs/Object.h"
#ifndef FACTORY_METHOD_H
#define FACTORY_METHOD_H
class FactoryMethod
{
public:
FactoryMethod();
~FactoryMethod();
virtual Object create_product();
Object create() { return create_product(); };
};
#endif
- 추상 클래스는 선언과 구현이 자연스럽게 분리된다.
- 추상 클래스는 바로 인스턴스화할 수 없다.
- 추상 메서드의 구현이 이루어지지 않았기 때문 ⇒ 실제적인 구현부가 없다.
- 하위 클래스에서 상속받고 추상 메서드를 작성한 후
하위 클래스를 통해서 인스턴스화할 수 있다.
인터페이스
- 클래스 설계 방법을 규정하는 약속이다.
- 인터페이스 적용 시, 반드시 인터페이스에 선언된 정의에 따라 클래스에 구현해야 한다.
- 추상 클래스도 인터페이스와 유사한 선언적 규정 정의가 가능하다.
- 추상 클래스는 구체화되지 않은 추상 메서드를 선언할 수 있다. = 인터페이스와 유사하다.
- 선언된 추상 메서드는 인터페이스와 동일하게
상속 받은 하위 클래스에서 반드시 구현해야 한다.
= 하위 클래스에 메서드 구현을 강제화한다. ⇒ 실제적인 동작처리를 위임한다.
- 추상화
→ 실제적인 동작을 하위 클래스에 위임한다.
⇒ 추상 클래스 안에서 미리 위임된 메서드를 사용할 수 있다.
템플릿 메서드 패턴
- 추상화로 확장된 팩토리 메서드 패턴 = 템플릿 메서드 패턴과 유사
- 템플릿 메서드 또한 추상화 기법을 통해 정의과 구현을 분리해서 사용한다.
- 팩토리 메서드도 실제 생성되는 알고리즘을 하위 메서드에 위임한다.
- 실제 구현을 위임한다는 측면에서 서로 유사하다.
- 추상화를 이용한 메서드 선언
- 추상화를 사용하면 실제 내용이 구현되지 않은 상태에서도
미리 메서드를 호출해 사용할 수 있다.
패턴 차이
- 팩토리 메서드 vs 템플릿 메서드
- 유사한 동작을 갖고 있다.
- 해결하려는 목적에 따라 두 패턴에 차이가 있다.
- 유사한 2개의 패턴을 결합한 것 ⇒ 패턴 이해가 어려울 수도 있다.
상위 클래스
추상화 적용
→ 추상 클래스 사용 ⇒ 상위 클래스, 하위 클래스로 강제적 분리
분리
- 추상화 → 클래스의 골격 구조가 변경된다.
- 일반 클래스 → 추상화 적용 → 분리
- 추상 클래스 (상위 클래스)
- 구현 클래스 (하위 클래스)
- 추상 클래스 = abstract 키워드로 변경된 타입의 클래스이다.
- 추상화로 선언된 클래스 → 일반적 클래스보다 객체 결합 관계를 더 느슨하게 한다.
- 추상화 적용 시, 실제 추상 클래스는 상위 클래스 하나 뿐이고
하위 클래스는 일반 클래스와 동일하다.
- 추상화 → 상세한 구현을 분리한다. → 하위 클래스에 위임 및 작성한다.
⇒ 위임을 통해 분리된 하위 클래스는 실제 내용을 자유롭게 변경하거나 처리할 수 있는 느슨한 관계를 형성한다.
공통된 기능
- 추상화
- 상위 클래스와 이를 상속한 하위 클래스로 분리한다.
- 인터페이스와 유사한 기능이 있지만 인터페이스와는 별개의 기능이다.
- 추상 클래스 ≠ 인터페이스
- 인터페이스 : 설계 규약만 표현가능하다.
- 추상 클래스 : 행위 메서드를 추상 클래스 안에 같이 구현할 수 있다.
- 행위 메서드
- 추상 클래스 안에 직접 구현된 메서드
- 상속할 경우 하위 클래스에서도 사용 가능하다.
- 추상 클래스 상속 키워드
- C++ : 인터페이스와 동일하게 상속
- PHP, Java, JS 등
- implements : 인터페이스 상속 방법
- extends : 클래스 상속 방법
- 상속
- 상위 클래스의 특징을 포함하는 포괄적인 승계이다.
- 추상 클래스도 메서드를 구현해 하위 클래스에 필요한 기능을 전달할 수 있다.
- 주로 하위 클래스에서 사용되는 공통된 메서드를 위주로 추상 클래스 안에 구현한다.
개념적 정의
- 분리된 객체의 추상 클래스가 가진 특징
- 인터페이스와 같은 개념적 메서드 선언이 가능하다. = 추상 메서드
- 추상 메서드
- 인터페이스와 같이 정의 부분만 선언한다.
- 실제 구현부는 존재하지 않는다.
- 실제 구현부는 상속 받는 하위 클래스에서 구현한다.
(인터페이스 사용법과 유사)
- 메서드 선언 앞에 키워드가 붙는다.
- PHP : abstract
- C, C++ : virtual
- 추상 클래스에 선언적 메서드를 정의했다면
→ 상속받은 하위 클래스는 선언된 메서드의 실제 구현부를 반드시 작성해야 한다.
→ 추상 클래스 또한 인터페이스의 규약과 같이 구현부 작성의 의무가 존재한다.
⇒ 이를 “하위 클래스에 구현을 위임한다” 표현 (위임 : 추상화의 특징)
템플릿
- 팩토리 메서드 & 템플릿 메서드 공통점
- 템플릿 구성 → 모두 추상화를 통해 구성한다.
- 템플릿 (template)
- 형판, 견본, 본보기.
- 자주 사용하는 모습을 본 떠 만들어 둔 것.
- 추상화된 메서드를 이용해 객체를 구성하는 모습이 비슷했다.
⇒ 템플릿의 개념 도입
- 상위 클래스 : 구조의 모습만 정의
- 하위 클래스 : 실제 모습 구현을 위임
- 추상화 기법 : 템플릿 설계 시, 요약과 세부 정보를 분리하는데 유용하다.
계층 구조
- 추상화 : 일반 클래스 → 추상화 → 개념적 클래스, 구현 클래스로 분리
- 개념적 클래스 = 추상 클래스 = 상위 클래스
- 구현 클래스 = 일반 클래스 = 하위 클래스
- 분리된 클래스는 상속을 통해 계층적 구조를 갖는다.
- 상위 클래스
- 문제 해결을 위한 공통 부분과 개념을 정의한다.
= 상속 시 필요한 공통 기능 + 인터페이스와 유사한 개념적 선언들로 구성한다.
- 상위 클래스 : 구조적 공격만 형성한다.
- 하위 클래스 : 실제 구현 내용은 팩토리 메서드 패턴마다 다르게 적용할 수 있다.
(하위 클래스에서 작업)
- 템플릿화된 구조적 골격 → 다양한 생성 패턴에 응용 가능하다.
- 기존 팩토리 : 고정적인 생성 패턴들만 결합
- 팩토리 메서드 : 느슨한 생성으로 결합
- 팩토리와 팩토리 메서드가 유사한 성격일 경우
⇒ 생성 방법을 팩토리 메서드로 통일 가능하다.
- 템플릿의 특징을 이용해 세부적인 생성 방법을 다르게 결정 가능하다.
하위 클래스
상속
- 상위, 추상 클래스만으로는 객체 생성이 불가능하다.
- 실제 구현부가 없기 때문이다.
- 그래서, 실체 객체 생성을 위한 추상 클래스를 상속받아 구현 클래스를 작성한다.
#include "FactoryMethod.h"
#ifndef CONCRETE_FACTORY_METHOD_H
#define CONCRETE_FACTORY_METHOD_H
class ConcreteFactoryMethod : public FactoryMethod
{
public:
ConcreteFactoryMethod();
~ConcreteFactoryMethod();
virtual Object create_product();
};
#endif
#include <iostream>
#include "ConcreteFactoryMethod.h"
ConcreteFactoryMethod::ConcreteFactoryMethod(){}
ConcreteFactoryMethod::~ConcreteFactoryMethod(){}
Object ConcreteFactoryMethod::create_product(){
return Object();
}
- 추상 클래스는 하위 클래스에서 상속받아 추상 메서드를 실제 구현 작성해야만 한다.
- 강제적 의무 사항으로 위반 시 오류가 발생한다.
- 인터페이스 규약과 유사
메서드 구현
- 추상화 적용
- 실제 동작 코드는 분리되어 하위 클래스에 위치한다.
- 상위 클래스는 인터페이스의 성격과 유사하다. = 메서드의 형태만 선언
- 하위 클래스 메서드 작성 시
- 상위 클래스의 메서드가 어떻게 선언되었는지 확인한다.
- 하위 클래스 구현 시, 상위 클래스에서 형태와 모양이 일치하지 않으면 오류가 난다.
virtual Object create_product();
Object ConcreteFactoryMethod::create_product(){
return Object();
}
- 하위 클래스 : 상속 받은 추상 클래스의 메서드를 오버라이드한다.
- → 상위 클래스의 추상 메서드 선언 + 하위 클래스의 오버라이드
⇒ 상황별로 대응할 수 있는 실제 동작 메서드를 다르게 정의할 수 있다.
= 상황별로 동작 메서드를 다르게 작성할 수 있다.
- 추상 메서드 오버라이드
- 팩토리
- 생성하려는 클래스 이름을 팩토리 클래스의 메서드에서 구체적으로 직접 명시
- 팩토리 메서드
- 위임된 하위 클래스에서 메서드 호출로 객체 생성
- 필요한 객체를 new 키워드를 사용하지 않고 추상화로 재구현된 메서드를 호출하여 객체 생성.
- 팩토리 메서드 패턴
- 메서드 호출로 객체 생성을 반환
⇒ 이를 활용하는 상위 클래스는 느슨한 코드 작성 가능
- 단계별 클래스를 상속하고 하위 클래스로 위임 ⇒ 의존성 제거 가능 = 구체적인 구현부 클래스를 직접 호출하지 않고
추상적인 상위 클래스를 호출하여 객체를 생성할 수 있다. ⇒ 구현부의 명칭이 바뀌어도 문제 없다.
⇒ 팩토리 메서드 패턴은 객체 생성의 뼈대 형성 시 자주 응용된다.
- 실체 객체 생성, 반환을 위해서 상속 받은 하위 클래스를 이용한다. = 실체화된 하위 클래스의 객체를 통해 요청된 객체의 생성을 처리한다.
- 필요한 객체가 있을 경우, 팩토리 안 메서드만 호출한다.
- 최종 생성되는 객체는 구현 메서드에서 자유롭게 수정 가능하다.
객체를 생성하는 객체
- 생성 패턴의 특징 : 생성을 담당하는 객체가 요청된 객체를 생성한다.
- 팩토리 메서드는 일반 팩토리보다 동작이 다소 복잡하다.
- 팩토리 메서드
- 객체 생성을 위한 인터페이스를 정의한다.
- 의존성을 낮출 수 있다.
- 보다 일반적인 코드로 생성 패턴을 구현할 수 있다.
- 추상화
- 쉽게 위임된 객체의 생성 코드를 분리 가능하다.
- 객체 생성 처리 메서드에 if 조건문으로 새로운 클래스를 추가할 수 있다.
다형성
- 다형성 : 구현 객체를 규격에 맞게 다양한 형태로 구현 가능한 성질.
- 다형성 적용을 위해선 인터페이스 처리가 필요하다.
- 팩토리 메서드
- 추상화를 통해 하위 클래스에 다형성을 부여한다.
- 상위 클래스에서 선언된 추상화 ⇒ 규격에 맞는 하위 클래스를 다르게 구현한다.
- 다형성의 특징 + 추상화 기법 응용
⇒ 상속 받은 클래스마다 다른 기능의 메서드를 구현할 수 있다.
- 추상화의 다형성
- 실제 동작하는 객체의 생성을 다양하게 처리하는데 적용 가능하다.
- 어떤 객체를 생성해야 할지 예측할 수 없을 때 매우 유용하다.
개방-폐쇄 원칙 (OCP : Open-Closed Principle)
- SOLID 중 하나
- 바뀌지 않는 공통된 부분을 분리하여 관리한다.
- 팩토리 패턴 : 객체 생성을 다른 클래스로 캡슐화 처리
- 팩토리 메서드 패턴 : 추상화를 통해 객체 생성을 더욱 유연하게 분리
- 코드가 임의적으로 변하도록 (수정) 놔두는 것은 좋지 않다.
⇒ 인터페이스로 다형성 적용 시, 메서드를 여러가지 방법으로 재사용 가능하다.
의존성
- 생성 패턴 사용 이유
- 객체 생성 과정을 외부에 공개하지 않기 위해.
- 직접적으로 new 키워드를 사용하는 빈도를 줄이기 위해.
⇒ 객체 생성 과정에서 강한 의존 관계를 느슨한 관계로 처리할 수 있도록 변경한다.
⇒ 의존성을 줄이기 위함
캡슐화와 관리
- 팩토리 메서드 패턴
- 캡슐화된 객체를 다시 하위 클래스로 분리 → 분리된 클래스는 상속을 통해 실체 객체 생성 처리가 위임된다. ⇒ 패턴에 객체 생성 요청 시, 사용 코드에선 자세한 객체 생성 방식을 알 필요가 없다.
- 클래스 결합도 낮추고, 유연성 향상시킨다.
기능 개선 시, 보완을 위한 리팩터링 작업도 편리하다.
매개변수
- 객체 생성 과정을 분리 → 다양한 객체 생성을 처리할 수 있다.
- 객체 생성을 매개변수화하여 선택적 생성이 가능하다.
= 분리된 객체는 어떤 객체를 최종 생성, 반환해야 하는지 결정한다.
재위임
- 팩토리 : 분리된 클래스에 객체 생서을 위임
- 팩토리 메서드 : 추상화 → 정의와 구현을 재분리 ⇒ 실체 객체 생성은 재분리된 하위클래스에서
객체 생성을 위한 중간 단계가 존재
- 팩토리 : 부장 → 사원
- 팩토리 메서드 : 부장이 중간 위치 과장에게 일을 위임.
- 장점 :
하위 클래스 추가로 객체지항의 다형성 적용
- 단점:
하위 클래스 파일이 하나 더 생김.
파일 수가 늘어남.
다형성을 이용한 클래스 선택
- 팩토리에서 매개변수를 통해 다양한 객체를 생성했듯,
팩토리 메서드에서도 매개변수로 다양한 객체 선택 생성이 가능하다.
- 팩토리 메서드
- 추상 클래스를 상속 받아 하위 클래스를 추가
- 객체의 생성을 군집화, 군집된 객체를 매개변수로 선택
⇒ 추상 클래스 안 create() 메서드에 매개변수를 추가한다.
⇒ 전달 받은 값을 추상 메서드의 인자값으로 전달한다.
⇒ 매개변수 값을 이용하여 군집에 필요한 객체를 생성한다.
매개변수 팩토리 메서드
Parameterized Factory Method
- 추상 팩토리 패턴은 2개 이상의 추상 클래스로 더 큰 객체의 그룹을 형성하지만,
단일 그룹의 객체일 경우, 팩토리 메서드의 매개변수만으로도 충분하다.
- 대형 프로그램 개발 시
-
생각보다 많은 수의 객체를 생성한다.
-
객체 관계 설정이 복잡해진다.
-
객체 생성을 처리하는 위치도 분산된다.
⇒ 객체 생성을 한 곳에 집중해서 처리하면 유지보수와 수정이 수월하다.
- 팩토리 메서드에서 입력받은 매개변수 처리 시,
매개변수에 맞는 클래스를 선택하여 객체를 생성한다.
- 생성 패턴은 분산된 객체의 생성 처리가 한 곳에 집중되도록 개선한다.
- 수정이 필요한 경우, 하위 클래스만 변경한다. ⇒ 유지 보수가 쉽다.
오류 처리
- 매개변수 사용 시, 객체 생성 과정에서 오류 발생이 가능하다.
- 매개변수값과 일치하지 않는 조건이 발생
- new 키워드로 객체 생성 시, 선언된 클래스 파일을 읽을 수 없는 경우
- 오류 방지를 위해
- 패턴에서 객체 생성 시 예상되는 오류들을 체크하는 코드 삽입 가능.
- 객체 생성 시, 예외 처리 코드 추가 ⇒ 다양한 오류를 방지 가능.
정리
- 팩토리 메서드
- 구조화를 통해 선언부와 실현 구현부를 분리한다.
⇒ 프레임워크같은 응용 프로그램에서 많이 이용하는 패턴 중 하나이다.
- 응용 프로그램에 클래스가 종속되지 않도록 관리 가능하다.
⇒ 객체의 생성 과정을 캡슐화, 이를 분리하여 관리한다.
= 알고리즘 처리 시, 프로세스 별로 구별하여 구현하는 것과 비슷하다.