Abstract Factory (추상화 팩토리)

JS (TIL & Remind)·2022년 2월 15일
1

Abstract Factory 패턴이란?

구체적인 클래스를 지정하지 않고, 관련성을 갖는 객체들의 집합을 생성하거나 서로 독립적인 객체들의 집합을 생성할 수 있는 인터페이스를 제공하는 패턴이다.

  • 클라이언트 입장에서 실제 구현 클래스를 알 필요 없이 인터페이스만으로 시스템을 조작할 수 있도록 하는 디자인 패턴.

Abstract Factory 패턴을 사용하는 이유

  • 실제 객체가 무엇인지 알지 못해도 객체를 생성하고 조작할 수 있도록 하여, 객체가 생성되거나 구성, 표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 할 때 사용하게 된다.
  • 관련성 있는 여러 종류의 객체를 일관된 방식으로 생성할 때 유용하다.

Abstract Factory 패턴 구조

  • AbstractFactory - 팩토리 인터페이스로, 만들고자 하는 객체를 생성하는 연산을 정의한다.
  • ConcreteFactory - AbstractFactory 인터페이스를 구현하는 클래스로, 구체적인 요소가 들어간다.
  • AbstractProduct - AbstractFactory 에서 만들고자 했던 객체에 대한 인터페이스를 정의한다.
  • ConcreteProduct - 팩토리에서 생성할 실제 객체를 정의하고, AbstractProduct의 인터페이스를 정의한다.
  • Client - 생성된 객체를 인터페이스만 이용하여 사용한다.

Abstract Factory 패턴의 장점과 단점

장점

  • 구체적인 클래스를 분리시킴으로써 재사용성을 증진시킬 수 있다.
    • 팩토리는 제품 객체를 생성하는 과정과 책임을 캡슐화 한 것이기 때문에, 구체적인 구현 클래스가 사용자에게서 분리된다.
  • 제품군을 쉽게 대체할 수 있다. (같은 추상 제품을 상속받은 객체를 쉽게 대체할 수 있다)
    • ConcreteFactory(구체 팩토리)의 클래스는 응용프로그램에서 한 번만 나타나기 때문에, 응용 프로그램이 사용할 구체 팩토리를 변경하기 쉽다.
    • AbstractFactory(추상 팩토리)는 모든 것을 생성하기 때문에 전체 제품군을 한 번에 변경할 수 있다.

단점

  • 새로운 종류의 제품을 제공하기 어렵다. (확장성이 떨어진다)
    • 새롭게 생성된 제품은 추상 팩토리가 생성할 수 있는 제품 집합에만 고정되어 있기 때문이다. 새로운 종류의 제품이 등장하면 팩토리의 구현을 변경해야 하는데, 이는 AbstractFactory(추상 팩토리)와 모든 서브 클래스를 변경해야 한다.

예제 코드

구현 예제는 Web App 을 구성하는 객체를 만드는 과정을 Abstract Factory 패턴을 이용하여 구현하였습니다.

예제의 구조는 다음와 같습니다.

💡 **AbstractFactory**: WebAppFactory **ConcreteFactory**: WebApp_version_1, WebApp_version_2 **AbstractProduct**: FrontEnd, BackEnd **ConcreteProduct**: FrontEndStructure, BackEndStructure ... **Client**: createWebApp
// AbstractFactory
// 팩토리 인터페이스로, 만들고자 하는 객체를 생성하는 연산을 정의한다.
interface WebAppFactory {
		createFrontEnd() : FrontEnd;
		createBackEnd()  : BackEnd;
}

// ConcreteFactory
// AbstractFactory 인터페이스를 구현하는 클래스로, 구체적인 요소가 들어간다.
class WebApp_version_1 implements webAppFactory {
		public createFrontEnd(): FrontEnd {
        return new FrontEndStructure();
    }

    public createBackEnd(): BackEnd {
        return new BackEndStructure();
    }
}

// AbstractProduct
// AbstractFactory 에서 만들고자 했던 객체에 대한 인터페이스를 정의한다.
interface FrontEnd {
    setFrameWork(): string;
}

interface BackEnd {
    setDataBase(): string;
}

// ConcreteProduct
// 팩토리에서 생성할 실제 객체를 정의하고, AbstractProduct의 인터페이스를 정의한다.
class FrontEndStructure implements FrontEnd {
		public setFrameWork(frameWork: string): string {
				return `사용 할 프레임워크는 ${frameWork} 입니다`;
		}
}

class BackEndStructure implements BackEnd {
    public setDataBase(dataBase: string): string {
				return `사용 할 데이터베이스는 ${dataBase} 입니다`;
    }
}

// Client
// 생성된 객체를 인터페이스만 이용하여 사용한다.
const createWebApp = (factory: WebAppFactory, frameWork: string, db: string) => {
		const frontEnd = factory.createFrontEnd();
		const backEnd = factory.createBackEnd();
		
		console.log(frontEnd.setFramework(frameWork));
		console.log(backEnd.setDataBase(db));
}

// Client 호출
createWebApp(new WebApp_version_1(), 'React', 'Redis');

// 결과
// '사용 할 프레임워크는 React 입니다'
// '사용 할 데이터베이스는 Redis 입니다'

// ================================================================
// 새로운 객체를 만들 땐, ConcreteFactory와 ConcreteProduct를 추가 한다.

// ConcreteFactory
class WebApp_version_2 implements WebAppFactory {
		public createFrontEnd(): FrontEnd {
        return new OtherFrontEndStructure();
    }

    public createBackEnd(): BackEnd {
        return new OtherBackEndStructure();
    }
}

// ConcreteProduct
class OtherFrontEndStructure implements FrontEnd {
		public setFrameWork(frameWork: string): string {
				return `변경 할 프레임워크는 ${frameWork} 입니다`;
		}
}

class OtherBackEndStructure implements BackEnd {
    public setDataBase(dataBase: string): string {
				return `변경 할 데이터베이스는 ${dataBase} 입니다`;
    }
}

// Client 호출
createWebApp(new WebApp_version_2(), 'Angular', 'MySQL');

// 결과
// '변경 할 프레임워크는 Angular 입니다'
// '변경 할 데이터베이스는 MySQL 입니다'
profile
노션에 더욱 깔끔하게 정리되어있습니다. (하단 좌측의 홈 모양 아이콘)

0개의 댓글