팩토리 메소드 패턴이란
객체 생성을 위한 인터페이스를 정의하지만, 인스턴스 생성은 서브클래스가 담당하는 패턴을 말합니다.
부모 클래스에서 객체의 생성 인터페이스를 제공하고, 자식 클래스에서 실제 생성될 객체의 타입을 결정하게 됩니다.
팩토리 메소드 패턴
1) 추상 제품 (Abstract Product)
export default abstract class Grimpan {
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
abstract initialize(): void;
abstract initializeMenu(): void;
static getInstance() {};
}
2) 구체적 제품 (Concrete Product)
export default abstract class Grimpan {
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
abstract initialize(): void;
abstract initializeMenu(): void;
static getInstance() {};
}
import Grimpan from "./AbstractGrimpan";
class ChromeGrimpan extends Grimpan {
private static instance: ChromeGrimpan;
initialize() {}
initializeMenu() {}
static override getInstance() {
if (!this.instance) {
this.instance = new ChromeGrimpan(document.querySelector('#canvas'))
}
return this.instance
}
}
export default ChromeGrimpan;
3) 추상 생성자 (Abstract Creator)
import Grimpan from "./AbstractGrimpan";
abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
export default AbstractGrimpanFactory;
4) 구체적 생성자 (Concrete Creator)
import ChromeGrimpan from './ChromeGrimpan.js';
import IEGrimpan from './IEGrimpan.js';
import AbstractGrimpanFactory from './AbstractGrimpanFactory.js';
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
}
class IEGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return IEGrimpan.getInstance();
}
}
function main(){
const grimpan = ChromeGrimpanFactory.createGrimpan();
grimpan.initialize();
grimpan.initializeMenu();
}
main();
해당 패턴 및 구조가 팩토리 메소드 패턴인 이유는
a) 먼저 추상화된 생성 로직인 아래의 코드를 통해 추상 메소드 정의하고
// AbstractGrimpanFactory에서 추상 메소드 정의
abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
b) 구체적인 생성 책임을 하위 클래스에 위임합니다.
// 각 구체적 팩토리가 자신의 제품을 생성
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
}
class IEGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return IEGrimpan.getInstance();
}
}
c) 그럼으로 제품의 계층 구조를 갖게 됩니다.
// 추상 제품 클래스
abstract class Grimpan {
abstract initialize(): void;
abstract initializeMenu(): void;
}
// 구체적 제품 클래스들
class ChromeGrimpan extends Grimpan { /* ... */ }
class IEGrimpan extends Grimpan { /* ... */ }
설계과정 및 고려사항
설계과정
1) 먼저 추상 제품(Abstract Product) 클래스 정의
// AbstractGrimpan.ts
export default abstract class Grimpan {
// 공통 생성자 정의
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
// 공통 인터페이스 정의
abstract initialize(): void;
abstract initializeMenu(): void;
// 싱글톤을 위한 getInstance 추상 메소드
static getInstance() {};
}
2) 구체적 제품(Concrete Product) 클래스들 구현
// ChromeGrimpan.ts
import Grimpan from "./AbstractGrimpan";
class ChromeGrimpan extends Grimpan {
private static instance: ChromeGrimpan;
initialize() {
// Chrome 특화 초기화 구현
}
initializeMenu() {
// Chrome 특화 메뉴 초기화 구현
}
static override getInstance() {
if (!this.instance) {
this.instance = new ChromeGrimpan(document.querySelector('#canvas'))
}
return this.instance
}
}
export default ChromeGrimpan;
3) 추상 팩토리(Abstract Factory) 클래스 정의
// AbstractGrimpanFactory.ts
import Grimpan from "./AbstractGrimpan";
abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
export default AbstractGrimpanFactory;
4) 구체적 팩토리(Concrete Factory) 클래스들 구현
// ChromeGrimpanFactory.ts
import ChromeGrimpan from './ChromeGrimpan';
import AbstractGrimpanFactory from './AbstractGrimpanFactory';
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
}
export default ChromeGrimpanFactory;
5) 클라이언트 코드 작성
// main.ts
function main() {
// 팩토리를 통한 객체 생성
const grimpan = ChromeGrimpanFactory.createGrimpan();
// 생성된 객체 사용
grimpan.initialize();
grimpan.initializeMenu();
}
main();
설계시 고려사항
- 먼저 제품의 공통 인터페이스를 정의하여 전체 구조를 잡습니다
- 구체적 제품을 구현하여 실제 기능을 채워넣습니다
- 팩토리 계층을 만들어 객체 생성을 추상화합니다
- 마지막으로 클라이언트 코드에서 실제 사용 방식을 구현합니다
팩토리 메소드 패턴의 장점
브라우저별 구현을 쉽게 확장 가능하며 객체 생성 로직과 비즈니스 로직의 분리할 수 있습니다.
또한, 단일 책임 원칙(SRP) 준수하고 OCP(개방-폐쇄 원칙) 준수하여 새로운 브라우저 지원 추가 시 기존 코드 수정 없이 확장 가능하게 됩니다.
팩토리 메소드 패턴의 단점
물론 팩토리 메소드 패턴을 통해 브라우저별 그림판 객체 생성을 추상화하고 실제 구현을 하위 클래스에 위임함으로써 유연하고 확장 가능한 설계가 가능하게 하지만 이러한 팩토리 메소드 패턴에도 단점이 존재합니다.
1) 클래스 계층구조의 복잡성 증가
먼저 클래스 계층구조의 복잡성 증가됩니다.
// 제품 클래스 계층
abstract class Grimpan { ... }
class ChromeGrimpan extends Grimpan { ... }
class IEGrimpan extends Grimpan { ... }
// 팩토리 클래스 계층
abstract class AbstractGrimpanFactory { ... }
class ChromeGrimpanFactory extends AbstractGrimpanFactory { ... }
class IEGrimpanFactory extends AbstractGrimpanFactory { ... }
새로운 제품을 추가할 때마다 최소 2개의 클래스가 필요 (제품 클래스 + 팩토리 클래스)
클래스 수가 많아져 코드베이스가 복잡해짐
2) 불필요한 추상화 계층
// 간단한 객체 생성에도 복잡한 구조가 필요
class FirefoxGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return FirefoxGrimpan.getInstance();
}
}
class FirefoxGrimpan extends Grimpan {
initialize() { /* 간단한 초기화 로직 */ }
initializeMenu() { /* 간단한 메뉴 초기화 */ }
}
단순한 객체 생성의 경우 과도한 패턴 적용이 될 수 있음
3) 디버깅과 테스트의 복잡성
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
const instance = ChromeGrimpan.getInstance();
// 객체 생성 과정에서 문제 발생 시 디버깅이 어려움
// - 팩토리 문제인가?
// - 제품 클래스 문제인가?
// - 싱글톤 구현 문제인가?
return instance;
}
}
팩토리 메소드 패턴에 대한 나의 이해
여태까지 내용을 정리하며 든 나의 생각 및 이해는
추상 제품 클래스란 결국 구체적 제품에서 구현될 특정 공통 부분을 추상화시켜 놓는거고
abstract class Grimpan {
// 모든 그림판이 공통적으로 가져야 할 기능들을 추상화
abstract initialize(): void;
abstract initializeMenu(): void;
// 공통으로 사용할 생성자 로직
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
}
- 모든 구체적 제품(Chrome, IE 등)이 반드시 구현해야 할 공통 인터페이스 정의
- 제품들의 공통 동작을 보장하는 계약서 역할
추상 팩토리는 하위 클래스에서 객체의 생성 인터페이스를 제공, 구체적 팩토리에서 이를 직접 구현한다.
abstract class AbstractGrimpanFactory {
// 객체 생성을 위한 인터페이스 제공
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
- 객체 생성 방법을 선언하는 인터페이스 제공
- 구체적 팩토리들이 따라야 할 템플릿 역할
그리고 이렇게 하는 이유는 OCP 와 SRP 을 위해 이렇게 분리시켜 새분화시킨 것이디.
// OCP: 새로운 브라우저 지원을 추가할 때
class FirefoxGrimpan extends Grimpan {
// 기존 코드 수정 없이 새로운 구현 추가
}
class FirefoxGrimpanFactory extends AbstractGrimpanFactory {
// 기존 코드 수정 없이 새로운 팩토리 추가
}
// SRP: 각 클래스가 단일 책임을 가짐
class ChromeGrimpan extends Grimpan {
// Chrome 그림판 구현에만 책임
}
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
// Chrome 그림판 객체 생성에만 책임
}
해당 글은 아래의 강의 내용을 참고하여 작성되어 있습니다.
TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기 강의 | 제로초(조현영) - 인프런
제로초(조현영) | 타입스크립트/자바스크립트로 그림판을 만들어보며 다양한 디자인 패턴의 쓰임과 장단점을 알아봅니다. canvas api를 배울 수 있는 것은 보너스!, 디자인 패턴 배워서 저한테 도
www.inflearn.com
'디자인패턴' 카테고리의 다른 글
디자인패턴 : 빌더 패턴(Builder Pattern) (0) | 2025.01.10 |
---|---|
디자인패턴 : 추상 팩토리 패턴(Abstract Factory Pattern) (0) | 2025.01.09 |
디자인패턴 : 심플 팩토리 패턴(Simple Factory Pattern) (0) | 2025.01.08 |
디자인패턴 : SOLID 원칙 (0) | 2025.01.08 |
디자인패턴 : 싱글톤 패턴 (0) | 2025.01.03 |
팩토리 메소드 패턴이란
객체 생성을 위한 인터페이스를 정의하지만, 인스턴스 생성은 서브클래스가 담당하는 패턴을 말합니다.
부모 클래스에서 객체의 생성 인터페이스를 제공하고, 자식 클래스에서 실제 생성될 객체의 타입을 결정하게 됩니다.
팩토리 메소드 패턴
1) 추상 제품 (Abstract Product)
export default abstract class Grimpan {
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
abstract initialize(): void;
abstract initializeMenu(): void;
static getInstance() {};
}
2) 구체적 제품 (Concrete Product)
export default abstract class Grimpan {
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
abstract initialize(): void;
abstract initializeMenu(): void;
static getInstance() {};
}
import Grimpan from "./AbstractGrimpan";
class ChromeGrimpan extends Grimpan {
private static instance: ChromeGrimpan;
initialize() {}
initializeMenu() {}
static override getInstance() {
if (!this.instance) {
this.instance = new ChromeGrimpan(document.querySelector('#canvas'))
}
return this.instance
}
}
export default ChromeGrimpan;
3) 추상 생성자 (Abstract Creator)
import Grimpan from "./AbstractGrimpan";
abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
export default AbstractGrimpanFactory;
4) 구체적 생성자 (Concrete Creator)
import ChromeGrimpan from './ChromeGrimpan.js';
import IEGrimpan from './IEGrimpan.js';
import AbstractGrimpanFactory from './AbstractGrimpanFactory.js';
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
}
class IEGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return IEGrimpan.getInstance();
}
}
function main(){
const grimpan = ChromeGrimpanFactory.createGrimpan();
grimpan.initialize();
grimpan.initializeMenu();
}
main();
해당 패턴 및 구조가 팩토리 메소드 패턴인 이유는
a) 먼저 추상화된 생성 로직인 아래의 코드를 통해 추상 메소드 정의하고
// AbstractGrimpanFactory에서 추상 메소드 정의
abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
b) 구체적인 생성 책임을 하위 클래스에 위임합니다.
// 각 구체적 팩토리가 자신의 제품을 생성
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
}
class IEGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return IEGrimpan.getInstance();
}
}
c) 그럼으로 제품의 계층 구조를 갖게 됩니다.
// 추상 제품 클래스
abstract class Grimpan {
abstract initialize(): void;
abstract initializeMenu(): void;
}
// 구체적 제품 클래스들
class ChromeGrimpan extends Grimpan { /* ... */ }
class IEGrimpan extends Grimpan { /* ... */ }
설계과정 및 고려사항
설계과정
1) 먼저 추상 제품(Abstract Product) 클래스 정의
// AbstractGrimpan.ts
export default abstract class Grimpan {
// 공통 생성자 정의
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
// 공통 인터페이스 정의
abstract initialize(): void;
abstract initializeMenu(): void;
// 싱글톤을 위한 getInstance 추상 메소드
static getInstance() {};
}
2) 구체적 제품(Concrete Product) 클래스들 구현
// ChromeGrimpan.ts
import Grimpan from "./AbstractGrimpan";
class ChromeGrimpan extends Grimpan {
private static instance: ChromeGrimpan;
initialize() {
// Chrome 특화 초기화 구현
}
initializeMenu() {
// Chrome 특화 메뉴 초기화 구현
}
static override getInstance() {
if (!this.instance) {
this.instance = new ChromeGrimpan(document.querySelector('#canvas'))
}
return this.instance
}
}
export default ChromeGrimpan;
3) 추상 팩토리(Abstract Factory) 클래스 정의
// AbstractGrimpanFactory.ts
import Grimpan from "./AbstractGrimpan";
abstract class AbstractGrimpanFactory {
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
export default AbstractGrimpanFactory;
4) 구체적 팩토리(Concrete Factory) 클래스들 구현
// ChromeGrimpanFactory.ts
import ChromeGrimpan from './ChromeGrimpan';
import AbstractGrimpanFactory from './AbstractGrimpanFactory';
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return ChromeGrimpan.getInstance();
}
}
export default ChromeGrimpanFactory;
5) 클라이언트 코드 작성
// main.ts
function main() {
// 팩토리를 통한 객체 생성
const grimpan = ChromeGrimpanFactory.createGrimpan();
// 생성된 객체 사용
grimpan.initialize();
grimpan.initializeMenu();
}
main();
설계시 고려사항
- 먼저 제품의 공통 인터페이스를 정의하여 전체 구조를 잡습니다
- 구체적 제품을 구현하여 실제 기능을 채워넣습니다
- 팩토리 계층을 만들어 객체 생성을 추상화합니다
- 마지막으로 클라이언트 코드에서 실제 사용 방식을 구현합니다
팩토리 메소드 패턴의 장점
브라우저별 구현을 쉽게 확장 가능하며 객체 생성 로직과 비즈니스 로직의 분리할 수 있습니다.
또한, 단일 책임 원칙(SRP) 준수하고 OCP(개방-폐쇄 원칙) 준수하여 새로운 브라우저 지원 추가 시 기존 코드 수정 없이 확장 가능하게 됩니다.
팩토리 메소드 패턴의 단점
물론 팩토리 메소드 패턴을 통해 브라우저별 그림판 객체 생성을 추상화하고 실제 구현을 하위 클래스에 위임함으로써 유연하고 확장 가능한 설계가 가능하게 하지만 이러한 팩토리 메소드 패턴에도 단점이 존재합니다.
1) 클래스 계층구조의 복잡성 증가
먼저 클래스 계층구조의 복잡성 증가됩니다.
// 제품 클래스 계층
abstract class Grimpan { ... }
class ChromeGrimpan extends Grimpan { ... }
class IEGrimpan extends Grimpan { ... }
// 팩토리 클래스 계층
abstract class AbstractGrimpanFactory { ... }
class ChromeGrimpanFactory extends AbstractGrimpanFactory { ... }
class IEGrimpanFactory extends AbstractGrimpanFactory { ... }
새로운 제품을 추가할 때마다 최소 2개의 클래스가 필요 (제품 클래스 + 팩토리 클래스)
클래스 수가 많아져 코드베이스가 복잡해짐
2) 불필요한 추상화 계층
// 간단한 객체 생성에도 복잡한 구조가 필요
class FirefoxGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
return FirefoxGrimpan.getInstance();
}
}
class FirefoxGrimpan extends Grimpan {
initialize() { /* 간단한 초기화 로직 */ }
initializeMenu() { /* 간단한 메뉴 초기화 */ }
}
단순한 객체 생성의 경우 과도한 패턴 적용이 될 수 있음
3) 디버깅과 테스트의 복잡성
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
static override createGrimpan() {
const instance = ChromeGrimpan.getInstance();
// 객체 생성 과정에서 문제 발생 시 디버깅이 어려움
// - 팩토리 문제인가?
// - 제품 클래스 문제인가?
// - 싱글톤 구현 문제인가?
return instance;
}
}
팩토리 메소드 패턴에 대한 나의 이해
여태까지 내용을 정리하며 든 나의 생각 및 이해는
추상 제품 클래스란 결국 구체적 제품에서 구현될 특정 공통 부분을 추상화시켜 놓는거고
abstract class Grimpan {
// 모든 그림판이 공통적으로 가져야 할 기능들을 추상화
abstract initialize(): void;
abstract initializeMenu(): void;
// 공통으로 사용할 생성자 로직
protected constructor(canvas: HTMLElement | null) {
if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
throw new Error('canvas 엘리멘트를 입력하세요.');
}
}
}
- 모든 구체적 제품(Chrome, IE 등)이 반드시 구현해야 할 공통 인터페이스 정의
- 제품들의 공통 동작을 보장하는 계약서 역할
추상 팩토리는 하위 클래스에서 객체의 생성 인터페이스를 제공, 구체적 팩토리에서 이를 직접 구현한다.
abstract class AbstractGrimpanFactory {
// 객체 생성을 위한 인터페이스 제공
static createGrimpan() {
throw new Error('하위 클래스에서 구현하셔야 합니다.')
}
}
- 객체 생성 방법을 선언하는 인터페이스 제공
- 구체적 팩토리들이 따라야 할 템플릿 역할
그리고 이렇게 하는 이유는 OCP 와 SRP 을 위해 이렇게 분리시켜 새분화시킨 것이디.
// OCP: 새로운 브라우저 지원을 추가할 때
class FirefoxGrimpan extends Grimpan {
// 기존 코드 수정 없이 새로운 구현 추가
}
class FirefoxGrimpanFactory extends AbstractGrimpanFactory {
// 기존 코드 수정 없이 새로운 팩토리 추가
}
// SRP: 각 클래스가 단일 책임을 가짐
class ChromeGrimpan extends Grimpan {
// Chrome 그림판 구현에만 책임
}
class ChromeGrimpanFactory extends AbstractGrimpanFactory {
// Chrome 그림판 객체 생성에만 책임
}
해당 글은 아래의 강의 내용을 참고하여 작성되어 있습니다.
TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기 강의 | 제로초(조현영) - 인프런
제로초(조현영) | 타입스크립트/자바스크립트로 그림판을 만들어보며 다양한 디자인 패턴의 쓰임과 장단점을 알아봅니다. canvas api를 배울 수 있는 것은 보너스!, 디자인 패턴 배워서 저한테 도
www.inflearn.com
'디자인패턴' 카테고리의 다른 글
디자인패턴 : 빌더 패턴(Builder Pattern) (0) | 2025.01.10 |
---|---|
디자인패턴 : 추상 팩토리 패턴(Abstract Factory Pattern) (0) | 2025.01.09 |
디자인패턴 : 심플 팩토리 패턴(Simple Factory Pattern) (0) | 2025.01.08 |
디자인패턴 : SOLID 원칙 (0) | 2025.01.08 |
디자인패턴 : 싱글톤 패턴 (0) | 2025.01.03 |