반응형
추상 팩토리 패턴(Abstract Factory Pattern)이란
추상 팩토리 패턴이란 서로 관련있는 여러 객체를 일관된 방식으로 생성하기 위한 인터페이스를 제공하는 생성 패턴입니다. 구체적인 클래스를 지정하지 않고도 서로 관련된 객체들의 그룹을 생성할 수 있게 합니다.
주로 제품군의 일관성 유지가 필요할 때, 관련된 객체들이 함께 사용되어야 할 때, 시스템이 여러 제품군 중 하나를 사용해야 할 때, 구체적인 클래스를 분리하여 client 코드와 결합도를 낮추고 싶을 때 사용되는 패턴입니다.
추상 팩토리 패턴 코드
1) 추상 제품들의 정의
export default abstract class Grimpan {
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
abstract initialize(): void;
static getInstance() {};
}
export abstract class GrimpanMenu {
grimpan: Grimpan;
protected constructor(grimpan: Grimpan) {
this.grimpan = grimpan
}
abstract initialize(): void;
static getInstance(grimpan: Grimpan) {};
}
export abstract class GrimpanHistory {
grimpan: Grimpan;
protected constructor(grimpan: Grimpan) {
this.grimpan = grimpan
}
abstract initialize(): void;
static getInstance(grimpan: Grimpan) {};
}
2) 구체적인 제품들의 구현
// ChromeGrimpan
import Grimpan from "./AbstractGrimpan";
class ChromeGrimpan extends Grimpan {
private static instance: ChromeGrimpan;
initialize() {}
static override getInstance() {
if (!this.instance) {
this.instance = new ChromeGrimpan(document.querySelector('#canvas'))
}
return this.instance
}
}
export default ChromeGrimpan;
// GrimpanMenu
export class ChromeGrimpanMenu extends GrimpanMenu {
private static instance: ChromeGrimpanMenu;
override initialize(): void {
}
static override getInstance(grimpan: ChromeGrimpan): ChromeGrimpanMenu {
if (!this.instance) {
this.instance = new ChromeGrimpanMenu(grimpan)
}
return this.instance;
};
}
// GrimpanHistory
export class ChromeGrimpanHistory extends GrimpanHistory {
private static instance: ChromeGrimpanHistory;
override initialize(): void {
}
static override getInstance(grimpan: ChromeGrimpan): ChromeGrimpanHistory {
if (!this.instance) {
this.instance = new ChromeGrimpanHistory(grimpan)
}
return this.instance;
};
}
3) 추상 팩토리의 정의
export abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.');
}
static createGrimpanMenu(grimpan: Grimpan) {
throw new Error('하위 클래스에서 구현하셔야 합니다.');
}
static createGrimpanHistory(grimpan: Grimpan) {
throw new Error('하위 클래스에서 구현하셔야 합니다.');
}
}
4) 구체적인 팩토리들의 구현
import Grimpan from "./AbstractGrimpan.js";
import ChromeGrimpan from "./ChromeGrimpan.js";
import { ChromeGrimpanHistory, IEGrimpanHistory } from "./GrimpanHistory.js";
import { ChromeGrimpanMenu, IEGrimpanMenu } from "./GrimpanMenu.js";
import IEGrimpan from "./IEGrimpan.js";
export abstract class AbstractGrimpanFactory {
(...)
}
export class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
static override createGrimpanMenu(grimpan: ChromeGrimpan) {
return ChromeGrimpanMenu.getInstance(grimpan);
}
static override createGrimpanHistory(grimpan: ChromeGrimpan) {
return ChromeGrimpanHistory.getInstance(grimpan);
}
}
export class IEGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return IEGrimpan.getInstance();
}
static override createGrimpanMenu(grimpan: IEGrimpan) {
return IEGrimpanMenu.getInstance(grimpan);
}
static override createGrimpanHistory(grimpan: ChromeGrimpan) {
return IEGrimpanHistory.getInstance(grimpan);
}
}
5) 클라이언트 코드 작성
import { ChromeGrimpanFactory } from './GrimpanFactory.js';
function main(){
const factory = ChromeGrimpanFactory;
const grimpan = factory.createGrimpan();
const grimpanMenu = factory.createGrimpanMenu(grimpan);
const grimpanHistory = factory.createGrimpanHistory(grimpan);
grimpan.initialize();
grimpanMenu.initialize();
grimpanHistory.initialize();
}
main();
이 코드가 추상 팩토리 패턴의 좋은 예시인 이유
- 제품군의 일관성: Chrome과 IE 각각의 제품군(Grimpan, Menu, History)이 일관되게 생성됩니다.
- 확장성: 새로운 브라우저(예: Firefox)를 추가하고 싶다면, 새로운 구체적인 제품들과 팩토리를 추가하면 됩니다.
- 클라이언트 코드와의 분리: main 함수를 보면 클라이언트는 구체적인 클래스를 알 필요 없이 팩토리를 통해 필요한 객체들을 생성할 수 있습니다.
- 의존성 관리: 각 제품군 내의 객체들은 서로 호환되며, 팩토리를 통해 올바른 조합의 객체들이 생성됨을 보장합니다.
설계과정
1) 추상 제품 정의
// 각 제품의 공통 인터페이스 정의
abstract class Grimpan {
abstract initialize(): void;
}
abstract class GrimpanMenu {
abstract initialize(): void;
}
abstract class GrimpanHistory {
abstract initialize(): void;
}
2) 추상 팩토리 정의
abstract class AbstractGrimpanFactory {
abstract createGrimpan(): Grimpan;
abstract createGrimpanMenu(grimpan: Grimpan): GrimpanMenu;
abstract createGrimpanHistory(grimpan: Grimpan): GrimpanHistory;
}
3) 구체적 제품 구현
// Chrome 제품군
class ChromeGrimpan extends Grimpan {
initialize() { /* Chrome 특화 구현 */ }
}
class ChromeGrimpanMenu extends GrimpanMenu {
initialize() { /* Chrome 특화 구현 */ }
}
class ChromeGrimpanHistory extends GrimpanHistory {
initialize() { /* Chrome 특화 구현 */ }
}
// IE 제품군도 동일한 방식으로 구현
4) 구체적 팩토리 구현
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
createGrimpan() { return new ChromeGrimpan(); }
createGrimpanMenu(grimpan) { return new ChromeGrimpanMenu(grimpan); }
createGrimpanHistory(grimpan) { return new ChromeGrimpanHistory(grimpan); }
}
5) 클라이언트 코드 작성
function createGrimpanApplication(factory: AbstractGrimpanFactory) {
const grimpan = factory.createGrimpan();
const menu = factory.createGrimpanMenu(grimpan);
const history = factory.createGrimpanHistory(grimpan);
grimpan.initialize();
menu.initialize();
history.initialize();
return { grimpan, menu, history };
}
추상 팩토리 패턴 장/단점
추상 팩토리 패턴 장점
1) 제품군의 일관성 보장합니다.
- 관련된 객체들이 함께 동작하도록 보장
- 잘못된 조합의 객체 생성을 방지
2) 확장성
- 새로운 제품군 추가가 용이
- 기존 코드 수정 없이 새로운 변형 추가 가능
3) 코드 분리
- 생성 코드와 사용 코드의 분리
- 높은 유지보수성
추상 팩토리 패턴 단점
1) 복잡성 증가
- 많은 인터페이스와 클래스 생성 필요
- 코드량 증가
2) 새로운 제품 추가의 어려움
- 기존 제품군에 새로운 종류의 제품을 추가하려면 모든 팩토리를 수정해야 함
3) 오버헤드
- 단순한 객체 생성에도 팩토리 계층을 거쳐야 함
추상 팩토리 패턴과 팩토리 메소드 패턴의 주요 차이점
1) 생성 대상의 범위
팩토리 메소드 패턴: 한 종류의 객체를 생성하는 단일 메소드를 다룹니다.
abstract class Creator {
abstract createProduct(): Product; // 단일 제품 생성
}
추상 팩토리 패턴: 여러 종류의 관련된 객체들을 함께 생성하는 인터페이스를 제공합니다.
abstract class AbstractFactory {
abstract createProductA(): ProductA;
abstract createProductB(): ProductB;
abstract createProductC(): ProductC;
}
2) 목적과 사용 상황
팩토리 메소드
- 단일 제품의 다양한 변형을 생성할 때 사용
- 객체 생성 로직을 서브클래스로 캡슐화할 때 적합
class WindowsButton extends Button { }
class MacButton extends Button { }
abstract class ButtonCreator {
abstract createButton(): Button;
}
추상 팩토리
- 여러 관련 제품들의 제품군을 생성할 때 사용
- 제품들 간의 일관성이 중요할 때 적합
// Windows 제품군
class WindowsButton extends Button { }
class WindowsCheckbox extends Checkbox { }
// Mac 제품군
class MacButton extends Button { }
class MacCheckbox extends Checkbox { }
3) 확장성의 차이
팩토리 메소드
- 새로운 제품 유형 추가가 쉬움
- 새로운 Creator 서브클래스만 만들면 됨
추상 팩토리
- 새로운 제품군 추가는 쉬움
- 하지만 새로운 제품 유형 추가는 모든 팩토리에 영향을 미침
이를 정리하면
- 팩토리 메소드 패턴은 동일한 인터페이스를 가진 단일 제품의 다양한 구현체를 생성하는 패턴이고
- 추상 팩토리 패턴은 하나의 큰 제품을 구성하는 연관된 여러 객체들을 세트로 생성하는 패턴을 말하는 것 같습니다.
해당 글은 아래의 강의 내용을 참고하여 작성되어 있습니다.
TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기 강의 | 제로초(조현영) - 인프런
제로초(조현영) | 타입스크립트/자바스크립트로 그림판을 만들어보며 다양한 디자인 패턴의 쓰임과 장단점을 알아봅니다. canvas api를 배울 수 있는 것은 보너스!, 디자인 패턴 배워서 저한테 도
www.inflearn.com
반응형
'디자인패턴' 카테고리의 다른 글
디자인패턴 : 빌더 패턴(Builder Pattern) (0) | 2025.01.10 |
---|---|
디자인패턴 : 팩토리 메소드 패턴(Factory method pattern) (1) | 2025.01.09 |
디자인패턴 : 심플 팩토리 패턴(Simple Factory Pattern) (0) | 2025.01.08 |
디자인패턴 : SOLID 원칙 (0) | 2025.01.08 |
디자인패턴 : 싱글톤 패턴 (0) | 2025.01.03 |