GoF 디자인 패턴 5 - [생성] 팩토리 메서드 패턴

김정환·2024년 9월 2일
0

GoF 디자인패턴

목록 보기
5/9

팩토리 메서드 패턴 (Factory Method Pattern)


팩토리 패턴의 확장 = 팩토리 + 템플릿 메서드

추상화


팩토리 메서드 : 팩토리 패턴을 추상화로 확장한 패턴이다.

추상 개념

  • 추상 (Abstract)
    • 중요한 부분만 분리하여 이해하기 쉽게 만드는 작업
      = 코드를 요약한다.
      = 세부사항을 분리한다.
    • 파악하려는 기능이 요약된 정보가 있다. ⇒ 이해에 용이하다.
    • 추상적 개념 도입 = 객체의 동작을 보다 쉽게 파악하기 위함이다.

코드 요약

  • 추상화 = 코드 요약
    • 요약 시 상세한 내용을 무시한다.
    • 요약된 정보만으로 실 구현 코드를 상세히 보지 않고도 동작을 쉽게 이해할 수 있다.
  • 추상화 작업
    • 요약된 정보와 실제 구현부 분리
      ⇒ 추상 클래스를 이용하여 구조에서 정보 부분만 분리한다.

#include <iostream>
using namespace std;

#ifndef OBJECT_H
#define OBJECT_H

// abstract 키워드 대신 virtual 키워드가 붙은 함수가 있다면 추상 클래스로 정의함.
class Object 
{
public:
    virtual string get_name() = 0;
    virtual void set_name(string n) = 0;

protected:
    string name;
};

#endif
using System;

// abstract 키워드를 붙여 선언
abstract class Object{
    private string name;

    public virtual void setName(string n);
    public virtual string getName();
}

패턴 확장


팩토리

  • 기존 팩토리 패턴

추상화

  • 기존 팩토리 패턴에 추상화를 통해 패턴을 확장 구현한다.
    • 팩토리에 추상화를 결합 시,
      1. 팩토리 메서드 패턴
      2. 추상 팩토리 패턴
        위의 두 가지 형태로 구분 가능하다.
    • 기존 팩토리에 추상화 및 추상 클래서 적용
      • 객체 생산과 사용하는 것을 분리한다.

#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 키워드로 변경된 타입의 클래스이다.
    1. 추상화로 선언된 클래스 → 일반적 클래스보다 객체 결합 관계를 더 느슨하게 한다.
    2. 추상화 적용 시, 실제 추상 클래스는 상위 클래스 하나 뿐이고
      하위 클래스는 일반 클래스와 동일하다.
    3. 추상화 → 상세한 구현을 분리한다. → 하위 클래스에 위임 및 작성한다.
      위임을 통해 분리된 하위 클래스는 실제 내용을 자유롭게 변경하거나 처리할 수 있는 느슨한 관계를 형성한다.

공통된 기능

  • 추상화
    • 상위 클래스와 이를 상속한 하위 클래스로 분리한다.
    • 인터페이스와 유사한 기능이 있지만 인터페이스와는 별개의 기능이다.
  • 추상 클래스 ≠ 인터페이스
    • 인터페이스 : 설계 규약만 표현가능하다.
    • 추상 클래스 : 행위 메서드를 추상 클래스 안에 같이 구현할 수 있다.
      • 행위 메서드
        • 추상 클래스 안에 직접 구현된 메서드
        • 상속할 경우 하위 클래스에서도 사용 가능하다.
  • 추상 클래스 상속 키워드
    • C++ : 인터페이스와 동일하게 상속
    • PHP, Java, JS 등
      • implements : 인터페이스 상속 방법
      • extends : 클래스 상속 방법

  • 상속
    • 상위 클래스의 특징을 포함하는 포괄적인 승계이다.
    • 추상 클래스도 메서드를 구현해 하위 클래스에 필요한 기능을 전달할 수 있다.
    • 주로 하위 클래스에서 사용되는 공통된 메서드를 위주로 추상 클래스 안에 구현한다.

개념적 정의

  • 분리된 객체의 추상 클래스가 가진 특징
    • 인터페이스와 같은 개념적 메서드 선언이 가능하다. = 추상 메서드
  • 추상 메서드
    • 인터페이스와 같이 정의 부분만 선언한다.
      • 실제 구현부는 존재하지 않는다.
      • 실제 구현부는 상속 받는 하위 클래스에서 구현한다.
        (인터페이스 사용법과 유사)
      • 메서드 선언 앞에 키워드가 붙는다.
        • PHP : abstract
        • C, C++ : virtual
    • 추상 클래스에 선언적 메서드를 정의했다면
      → 상속받은 하위 클래스는 선언된 메서드의 실제 구현부를 반드시 작성해야 한다.
      → 추상 클래스 또한 인터페이스의 규약과 같이 구현부 작성의 의무가 존재한다.
      ⇒ 이를 “하위 클래스에 구현을 위임한다” 표현 (위임 : 추상화의 특징)

템플릿

  • 팩토리 메서드 & 템플릿 메서드 공통점
    • 템플릿 구성 → 모두 추상화를 통해 구성한다.
      • 템플릿 (template)
        • 형판, 견본, 본보기.
        • 자주 사용하는 모습을 본 떠 만들어 둔 것.
    • 추상화된 메서드를 이용해 객체를 구성하는 모습이 비슷했다.
      ⇒ 템플릿의 개념 도입
      • 상위 클래스 : 구조의 모습만 정의
      • 하위 클래스 : 실제 모습 구현을 위임
    • 추상화 기법 : 템플릿 설계 시, 요약세부 정보를 분리하는데 유용하다.

계층 구조

  • 추상화 : 일반 클래스 → 추상화 → 개념적 클래스, 구현 클래스로 분리
    • 개념적 클래스 = 추상 클래스 = 상위 클래스
    • 구현 클래스 = 일반 클래스 = 하위 클래스
    • 분리된 클래스는 상속을 통해 계층적 구조를 갖는다.
  • 상위 클래스
    • 문제 해결을 위한 공통 부분개념을 정의한다.
      = 상속 시 필요한 공통 기능 + 인터페이스와 유사한 개념적 선언들로 구성한다.
    • 상위 클래스 : 구조적 공격만 형성한다.
    • 하위 클래스 : 실제 구현 내용은 팩토리 메서드 패턴마다 다르게 적용할 수 있다.
      (하위 클래스에서 작업)
  • 템플릿화된 구조적 골격 → 다양한 생성 패턴에 응용 가능하다.
    • 기존 팩토리 : 고정적인 생성 패턴들만 결합
    • 팩토리 메서드 : 느슨한 생성으로 결합
    • 팩토리와 팩토리 메서드가 유사한 성격일 경우
      ⇒ 생성 방법을 팩토리 메서드로 통일 가능하다.
    • 템플릿의 특징을 이용해 세부적인 생성 방법을 다르게 결정 가능하다.

하위 클래스


상속

  • 상위, 추상 클래스만으로는 객체 생성이 불가능하다.
    • 실제 구현부가 없기 때문이다.
    • 그래서, 실체 객체 생성을 위한 추상 클래스를 상속받아 구현 클래스를 작성한다.
// ConcreteFactoryMethod.h
#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

// ConcreteFactoryMethod.cpp
#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 중 하나
  • 바뀌지 않는 공통된 부분을 분리하여 관리한다.
    • OCP 원칙 적용의 대표적인 방법 : 다형성
  • 팩토리 패턴 : 객체 생성을 다른 클래스로 캡슐화 처리
  • 팩토리 메서드 패턴 : 추상화를 통해 객체 생성을 더욱 유연하게 분리
    • 코드 분리 시, OCP 원칙 적용 가능
  • 코드가 임의적으로 변하도록 (수정) 놔두는 것은 좋지 않다.
    ⇒ 인터페이스로 다형성 적용 시, 메서드를 여러가지 방법으로 재사용 가능하다.

의존성

  • 생성 패턴 사용 이유
    1. 객체 생성 과정을 외부에 공개하지 않기 위해.
    2. 직접적으로 new 키워드를 사용하는 빈도를 줄이기 위해.

⇒ 객체 생성 과정에서 강한 의존 관계를 느슨한 관계로 처리할 수 있도록 변경한다.
⇒ 의존성을 줄이기 위함

캡슐화와 관리

  • 팩토리 메서드 패턴
    • 캡슐화된 객체를 다시 하위 클래스로 분리 → 분리된 클래스는 상속을 통해 실체 객체 생성 처리가 위임된다. ⇒ 패턴에 객체 생성 요청 시, 사용 코드에선 자세한 객체 생성 방식을 알 필요가 없다.
    • 클래스 결합도 낮추고, 유연성 향상시킨다.
      기능 개선 시, 보완을 위한 리팩터링 작업도 편리하다.

매개변수


  • 객체 생성 과정을 분리 → 다양한 객체 생성을 처리할 수 있다.
    • 객체 생성을 매개변수화하여 선택적 생성이 가능하다.
      = 분리된 객체는 어떤 객체를 최종 생성, 반환해야 하는지 결정한다.

재위임

  • 팩토리 : 분리된 클래스에 객체 생서을 위임
  • 팩토리 메서드 : 추상화 → 정의와 구현을 재분리 ⇒ 실체 객체 생성은 재분리된 하위클래스에서

객체 생성을 위한 중간 단계가 존재

  • 팩토리 : 부장 → 사원
  • 팩토리 메서드 : 부장이 중간 위치 과장에게 일을 위임.
    - 장점 :
    하위 클래스 추가로 객체지항의 다형성 적용
    - 단점:
    하위 클래스 파일이 하나 더 생김.
    파일 수가 늘어남.

다형성을 이용한 클래스 선택

  • 팩토리에서 매개변수를 통해 다양한 객체를 생성했듯,
    팩토리 메서드에서도 매개변수로 다양한 객체 선택 생성이 가능하다.
  • 팩토리 메서드
    • 추상 클래스를 상속 받아 하위 클래스를 추가
    • 객체의 생성을 군집화, 군집된 객체를 매개변수로 선택

⇒ 추상 클래스 안 create() 메서드에 매개변수를 추가한다.

⇒ 전달 받은 값을 추상 메서드의 인자값으로 전달한다.

⇒ 매개변수 값을 이용하여 군집에 필요한 객체를 생성한다.

매개변수 팩토리 메서드

Parameterized Factory Method

  • 추상 팩토리 패턴은 2개 이상의 추상 클래스로 더 큰 객체의 그룹을 형성하지만,
    단일 그룹의 객체일 경우, 팩토리 메서드의 매개변수만으로도 충분하다.
  • 대형 프로그램 개발 시
    1. 생각보다 많은 수의 객체를 생성한다.

    2. 객체 관계 설정이 복잡해진다.

    3. 객체 생성을 처리하는 위치도 분산된다.

      ⇒ 객체 생성을 한 곳에 집중해서 처리하면 유지보수와 수정이 수월하다.

  • 팩토리 메서드에서 입력받은 매개변수 처리 시,
    매개변수에 맞는 클래스를 선택하여 객체를 생성한다.
  • 생성 패턴은 분산된 객체의 생성 처리가 한 곳에 집중되도록 개선한다.
    • 수정이 필요한 경우, 하위 클래스만 변경한다. ⇒ 유지 보수가 쉽다.

오류 처리

  • 매개변수 사용 시, 객체 생성 과정에서 오류 발생이 가능하다.
    1. 매개변수값과 일치하지 않는 조건이 발생
    2. new 키워드로 객체 생성 시, 선언된 클래스 파일을 읽을 수 없는 경우
  • 오류 방지를 위해
    1. 패턴에서 객체 생성 시 예상되는 오류들을 체크하는 코드 삽입 가능.
    2. 객체 생성 시, 예외 처리 코드 추가 ⇒ 다양한 오류를 방지 가능.

정리

  • 팩토리 메서드
    • 구조화를 통해 선언부실현 구현부를 분리한다.
      ⇒ 프레임워크같은 응용 프로그램에서 많이 이용하는 패턴 중 하나이다.
    • 응용 프로그램에 클래스가 종속되지 않도록 관리 가능하다.
      ⇒ 객체의 생성 과정을 캡슐화, 이를 분리하여 관리한다.
      = 알고리즘 처리 시, 프로세스 별로 구별하여 구현하는 것과 비슷하다.
profile
만성피로 개발자

0개의 댓글