[Design Pattern] 1-4. 추상 팩토리 패턴 (Abstract factory pattern)
안녕하세요. 명월입니다.
이 글은 디자인 패턴의 추상 팩토리 패턴(Abstract factory pattern)에 대한 글입니다.
디자인 패턴의 생성 패턴 중에서 가장 복잡한 패턴인 추상 팩토리 패턴입니다.
구조는 복잡하지만 자세히 보면 팩토리 메서트 패턴에서 팩토리를 클래스로 만들고 그 위로 추상 인터페이스를 만들어서 사양에 따라 팩토리를 취득하고 그 팩토리 안에서 클래스를 취득하는 구조입니다.
그러니깐 팩토리 메서드 패턴이 중첩되어 있다라고 생각하면 쉽습니다.
출처 - https://en.wikipedia.org/wiki/Abstract_factory_pattern#pragma once
#include <stdio.h>
#include <iostream>
using namespace std;
// 추상 클래스
class IDao {
public:
// 추상 메서드
virtual string getData() = 0;
};
// 추상 팩토리 클래스
class IFactory {
public:
// 추상 메서드 (IDao 타입의 클래스를 리턴한다.)
virtual IDao* getTypeDao() = 0;
};
// ATypeDAO 클래스, IDao 추상 클래스를 상속
class ATypeDAO : public IDao {
public:
// 함수 재정의
virtual string getData() {
// string 값 리턴
return "ATypeDAO - getData()";
}
};
// 팩토리 클래스, IFactory 추상 팩토리 클래스를 상속
class AFactory : public IFactory {
public:
// 함수 재정의해서 ATypeDAO 클래스의 인스턴스를 리턴한다.
virtual IDao* getTypeDao() {
return new ATypeDAO();
}
};
// BTypeDAO 클래스, IDao 추상 클래스를 상속
class BTypeDAO : public IDao {
public:
// 함수 재정의
virtual string getData() {
// string 값 리턴
return "BTypeDAO - getData()";
}
};
// 팩토리 클래스, IFactory 추상 팩토리 클래스를 상속
class BFactory : public IFactory {
public:
// 함수 재정의해서 BTypeDAO 클래스의 인스턴스를 리턴한다.
virtual IDao* getTypeDao() {
return new BTypeDAO();
}
};
// 팩토리 패턴, 파라미터 값의 의해 팩토리 클래스의 인스턴스를 리턴한다.
IFactory* getFactory(int type) {
// 0의 값이라면 AFactory 클래스의 인스턴스를 리턴
if (type == 0) {
return new AFactory();
}
// 0의 값이 아니라면 AFactory 클래스의 인스턴스를 리턴
else {
return new BFactory();
}
}
// 실행 함수
int main() {
// 팩토리 함수로부터 팩토리 인스턴스를 받는다.
IFactory* factory = getFactory(0);
// 함수를 통해서 ATypeDAO 클래스의 인스턴스를 받는다.(여기서는 빌드 패턴(여기서 다시 팩토리 패턴을 넣어도 된다.))
IDao* dao = factory->getTypeDao();
// 콘솔에 출력
cout << dao->getData() << endl;
// 메모리 해제
delete dao;
delete factory;
// 팩토리 함수로부터 팩토리 인스턴스를 받는다.
factory = getFactory(1);
// 함수를 통해서 BTypeDAO 클래스의 인스턴스를 받는다.(여기서는 빌드 패턴(여기서 다시 팩토리 패턴을 넣어도 된다.))
dao = factory->getTypeDao();
// 콘솔에 출력
cout << dao->getData() << endl;
// 메모리 해제
delete dao;
delete factory;
return 0;
}
위 예제에서 보면 Factory 클래스를 getFactory라는 함수로 인스턴스를 받았습니다.
다시 Factory 클래스에서는 getTypeDao를 통해서 인스턴스를 받습니다. 저는 여기서 빌드 패턴을 통해서 IDao를 받지만 getTypeDao에 파라미터를 넣고 다시 팩토리 메서드 패턴을 사용할 수 있습니다.
// 실행 함수가 있는 클래스
public class Program {
// 팩토리 메서드 패턴으로 팩토리 클래스를 받는다.
private static IFactory getFactory(String type) {
// 입력 값이 A라면 AFactory 클래스의 인스턴스를 리턴
if ("A".equals(type.toUpperCase())) {
return new AFactory();
// 입력 값이 B라면 BFactory 클래스의 인스턴스를 리턴
} else if ("B".equals(type.toUpperCase())) {
return new BFactory();
}
// 조건에 맞지 않으면 null
return null;
}
// 실행 함수
public static void main(String[] args) {
// 팩토리 메서드 패턴으로 팩토리 클래스를 받는다.
var factory = getFactory("A");
// 여기서 다시 getType 함수를 통해서 AType1Dao 클래스의 인스턴스를 받고 getData() 함수를 콘솔에 출력
System.out.println(factory.getTypeDao(1).getData());
// 여기서 다시 getType 함수를 통해서 AType2Dao 클래스의 인스턴스를 받고 getData() 함수를 콘솔에 출력
System.out.println(factory.getTypeDao(2).getData());
// 팩토리 메서드 패턴으로 팩토리 클래스를 받는다.
factory = getFactory("B");
// 여기서 다시 getType 함수를 통해서 BType1Dao 클래스의 인스턴스를 받고 getData() 함수를 콘솔에 출력
System.out.println(factory.getTypeDao(1).getData());
// 여기서 다시 getType 함수를 통해서 BType2Dao 클래스의 인스턴스를 받고 getData() 함수를 콘솔에 출력
System.out.println(factory.getTypeDao(2).getData());
}
}
// 인터페이스
interface IDao {
// 최종 클래스에서 받은 데이터
String getData();
}
// 추상 팩토리 클래스 인터페이스
interface IFactory {
// 여기도 역시 팩토리 메서드를 통해서 IDao 클래스의 인스턴스를 받음
IDao getTypeDao(int type);
}
// AType1Dao 클래스, IDao 인터페이스를 상속
class AType1Dao implements IDao {
// 함수 재정의
@Override
public String getData() {
// 결과값 리턴
return "AType1Dao - getData()";
}
}
// AType2Dao 클래스, IDao 인터페이스를 상속
class AType2Dao implements IDao {
// 함수 재정의
@Override
public String getData() {
// 결과값 리턴
return "AType2Dao - getData()";
}
}
// 팩토리 클래스, IFactory 인터페이스를 상속
class AFactory implements IFactory {
// 함수 재정의
@Override
// 팩토리 메서드 패턴을 통해서 IDao 타입의 클래스를 취득
public IDao getTypeDao(int type) {
// 값이 1이라면 AType1Dao 클래스의 인터페이스를 리턴
if (type == 1) {
return new AType1Dao();
// 값이 2이라면 AType1Dao 클래스의 인터페이스를 리턴
} else if (type == 2) {
return new AType2Dao();
}
// 조건에 맞지 않으면 null
return null;
}
}
// BType1Dao 클래스, IDao 인터페이스를 상속
class BType1Dao implements IDao {
// 함수 재정의
@Override
public String getData() {
// 결과값 리턴
return "BType1Dao - getData()";
}
}
// BType2Dao 클래스, IDao 인터페이스를 상속
class BType2Dao implements IDao {
// 함수 재정의
@Override
public String getData() {
// 결과값 리턴
return "BType2Dao - getData()";
}
}
// 팩토리 클래스, IFactory 인터페이스를 상속
class BFactory implements IFactory {
// 함수 재정의
@Override
// 팩토리 메서드 패턴을 통해서 IDao 타입의 클래스를 취득
public IDao getTypeDao(int type) {
// 값이 1이라면 BType1Dao 클래스의 인터페이스를 리턴
if (type == 1) {
return new BType1Dao();
// 값이 2이라면 BType1Dao 클래스의 인터페이스를 리턴
} else if (type == 2) {
return new BType2Dao();
}
// 조건에 맞지 않으면 null
return null;
}
}
위 예제는 Java로 작성된 추상 팩토리 패턴 예제입니다.
C/C++과는 다르게 팩토리 클래스 안에서 빌드 패턴 대신에 다시 팩토리 메서드 패턴으로 인스턴스를 취득합니다.
using System;
namespace Example
{
// 인터페이스
interface IDao
{
// 콘솔 출력
void Print();
}
// 추상 팩토리 클래스 인터페이스
interface IFactory
{
// 추상 메서드 (IDao 타입의 클래스를 리턴한다.)
IDao GetTypeDao();
}
// ATypeDAO 클래스, IDao 추상 클래스를 상속
class ATypeDao : IDao
{
// 콘솔 출력 함수
public void Print()
{
// 콘솔 출력
Console.WriteLine("ATypeDao - GetData()");
}
}
// 팩토리 클래스, IFactory 추상 팩토리 클래스를 상속
class AFactory : IFactory
{
// ATypeDao 클래스의 인스턴스를 리턴
public IDao GetTypeDao()
{
// ATypeDao 클래스 인스턴스 생성
return new ATypeDao();
}
}
// BTypeDAO 클래스, IDao 추상 클래스를 상속
class BTypeDao : IDao
{
// 콘솔 출력 함수
public void Print()
{
// 콘솔 출력
Console.WriteLine("BTypeDao - GetData()");
}
}
// 팩토리 클래스, IFactory 추상 팩토리 클래스를 상속
class BFactory : IFactory
{
// BTypeDao 클래스의 인스턴스를 리턴
public IDao GetTypeDao()
{
// BTypeDao 클래스 인스턴스 생성
return new BTypeDao();
}
}
// 실행 함수가 있는 클래스
public class Program
{
// 팩토리 메서드 패턴으로 팩토리 클래스 인스턴스를 취득하는 함수
private static IFactory GetFactory(String type)
{
// 파라미터 값이 A의 경우
if ("A".Equals(type, StringComparison.OrdinalIgnoreCase))
{
// AFactory 클래스 인스턴스를 생성해서 리턴
return new AFactory();
}
// 파라미터 값이 B의 경우
else if ("B".Equals(type, StringComparison.OrdinalIgnoreCase))
{
// BFactory 클래스 인스턴스를 생성해서 리턴
return new BFactory();
}
// 조건에 맞지 않으면 null
return null;
}
// 실행 함수
public static void Main(string[] args)
{
// 팩토리 메서드 패턴 함수로 팩토리를 취득한다.
var factory = GetFactory("A");
// 취득된 팩토리에서 ATypeDao 인스턴스의 Print 함수를 실행
factory.GetTypeDao().Print();
// 팩토리 메서드 패턴 함수로 팩토리를 취득한다.
factory = GetFactory("B");
// 취득된 팩토리에서 BTypeDao 인스턴스의 Print 함수를 실행
factory.GetTypeDao().Print();
// 아무 키나 누르면 종료
Console.WriteLine("Press Any key...");
Console.ReadLine();
}
}
}
위 예제는 C/C++과 같이 팩토리 클래스에서 빌드 패턴으로 인스턴스를 취득 후에 실행합니다.
제가 Factory 클래스 말고 일반 클래스를 Dao라는 클래스 명으로 사용했습니다.
왜냐하면 이 추상 팩토리 패턴이 ORM 프레임워크에서 가장 많이 사용되는 패턴이기 때문입니다.
예를 들면 데이터 베이스의 각 테이블의 Dao 클래스를 만듭니다. 그런데 사양에 따라서 이게 Oracle이 될 수 있고, Mssql이 될 수 있고, Mysql(MariaDB)가 될 수도 있습니다.
각 데이터 베이스 시스템의 테이블의 설계 구조는 같다는 가정하에 이 추상 팩토리 패턴을 사용하면 사양에 따라 Oracle용 Dao 생성 팩토리를 생성할 수 있고, Mssql용 생성 팩토리를 생성할 수 있는 것입니다.
그 밖에 데이터 관리나 생성, PDF 생성이나 Excel 생성등에서 사양에 따른 장치를 구분할 때, 해당 클래스의 구조는 같게 만든다고 할 때, 자주 사용되는 패턴입니다.
여기까지 디자인 패턴의 추상 팩토리 패턴(Abstract factory pattern)에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.