논리를 계속해서 복제하지 않고 애플리케이션에서 다양한 객체 계열의 다양한 변형을 생성해야 하는 경우를 발견한 적이 있습니까?
아니면 애플리케이션을 구축했지만 새로운 요구 사항이나 고객의 변경된 선호 사항으로 인해 완전히 새로운 개체가 필요하다는 사실을 깨닫고 전체 코드베이스를 다시 작업해야 했을 수도 있습니다.
새 구현을 연결하는 것만으로 기존 코드를 손상시키지 않고 새로운 변형을 원활하게 도입
할 수 있는 방법이 있다면 어떨까요?여기서 추상 팩토리
디자인 패턴이 등장합니다!이 튜토리얼에서는 다양한 형식과 테마를 지원하는 다양한 유형의 이력서를 만들기 위한 Node.js CLI 애플리케이션
을 구축하여 이 강력한 디자인 패턴을 분석해 보겠습니다.추상 팩토리는 창조적 디자인 패턴으로, 을 사용하여 객체를 생성하는 기본 방식에서 발생하는 다양한 문제를 다루는 디자인 패턴 카테고리입니다. >new
키워드 또는 연산자.Abstract Factory
디자인 패턴은 이번 블로그 글에서 다룬 팩토리 메소드 디자인 패턴을 일반화한 것이라고 생각하시면 됩니다.
Abstract Factory
디자인 패턴은 다음 문제를 해결합니다.
추상 팩토리
디자인 패턴은 각 제품 유형에 대한 인터페이스 또는 추상 클래스를 선언하여 이러한 문제를 해결합니다.
export abstract class PDFResume {} export abstract class JSONResume {} export abstract class MarkdownResume {}
그런 다음 패턴 이름에서 알 수 있듯이 모든 유형의 제품을 생성하는 팩토리 메소드를 선언하는 인터페이스인 추상 팩토리
를 만듭니다.export interface ResumeFactory { createPDFResume(): PDFResume createMarkdownResume(): MarkdownResume createJSONResume(): JSONResume }
이제 가능한 모든 유형의 제품을 반품하는 일반 공장이 생겼습니다. 그런데 제품당 여러 변형을 어떻게 지원할 수 있을까요?
답은 추상 팩토리( ResumeFactory )를 구현하는 ConcreteFactory를 만드는 것입니다.
export abstract class PDFResume {} export abstract class JSONResume {} export abstract class MarkdownResume {}
이제 클라이언트 클래스에서 팩토리를 사용하려면 ResumeFactory 유형의 변수를 선언한 다음 사용자 입력에 따라 해당 콘크리트 팩토리를 인스턴스화하기만 하면 됩니다.
클라이언트 코드:
export interface ResumeFactory { createPDFResume(): PDFResume createMarkdownResume(): MarkdownResume createJSONResume(): JSONResume }
추상 팩토리 디자인 패턴의 구조는 다음 클래스로 구성됩니다.
이 경우 Factory에 선언된 팩토리 메소드는 createProductA 및 createProductB
입니다.
ConcretProductA1과 ConcretProductA2는 IProductA를 구현합니다. ConcretProductB1과 ConcretProductB2는 IProductB를 구현합니다.
이 섹션에서는 사용자가 선택한 테마와 형식을 기반으로 이력서를 생성하는 완벽하게 작동하는 Node.js TypeScript CLI 애플리케이션을 구축하여 이전 예를 실행해 보겠습니다.
이 저장소를 컴퓨터에 복제하여 전체 작동 코드를 자유롭게 확인해 보세요.
그런 다음 다음 명령을 실행하세요.
export abstract class PDFResume {} export abstract class JSONResume {} export abstract class MarkdownResume {}
유형 안전성을 보장하기 위해 튜토리얼 전체에서 사용할 유형을 선언하는 것부터 시작하겠습니다.
인터페이스/유형
export interface ResumeFactory { createPDFResume(): PDFResume createMarkdownResume(): MarkdownResume createJSONResume(): JSONResume }
이제 지원되는 다양한 제품 유형에 해당하는 세 가지 팩토리 메소드를 정의하는 일반 팩토리 유형을 선언해 보겠습니다. PDFResume , MarkdownResume 및 JSONResume.
인터페이스/ResumeFactory
export class CreativeResumeFactory implements ResumeFactory { createPDFResume(): CreativePDFResume { return new CreativePDFResume() // CreativePDFResume implements PDFResume } createMarkdownResume(): CreativeMarkdownResume { return new CreativeMarkdownResume() // CreativeMarkdownResume implements MarkdownResume } createJSONResume(): CreativeJSONResume { return new CreativeJSONResume() // CreativeJSONResume implements JSONResume } }
다음 섹션에서 코드를 살펴보겠습니다.
다음으로 일반 제품 클래스를 생성해 보겠습니다.
해당 하위 유형 간에 속성과 메서드를 모두 공유하기를 원하기 때문에 모든 제품 유형은 추상 클래스가 됩니다.
클래스 정의:
이력서/json/JSONResume
// User inputs... let theme = "minimalist" let format = "pdf" let factory: ResumeFactory switch (theme) { case "minimalist": factory = new MinimalistResumeFactory() break case "modern": factory = new ModernResumeFactory() break case "creative": factory = new CreativeResumeFactory() break default: throw new Error("Invalid theme.") } const userInput = await getUserInput() let resume switch (format) { case "pdf": resume = factory.createPDFResume() break case "markdown": resume = factory.createMarkdownResume() break case "json": resume = factory.createJSONResume() break default: throw new Error("Invalid format.") }
추상 키워드는 클래스가 인스턴스화할 수 없는 일반 유형임을 의미합니다. 다른 클래스에서만 상속할 수 있습니다.
클래스 정의:
이력서/markdown/MarkdownResume
export abstract class PDFResume {} export abstract class JSONResume {} export abstract class MarkdownResume {}
클래스에는 pdfkit이라는 라이브러리에서 가져온 PDFKit.PDFDocument 유형의 보호된 doc 개체가 있습니다. 라이브러리는 객체 지향 인터페이스를 통해 PDF 문서 작성 및 조작을 단순화합니다.
클래스 정의:
이력서/pdf/PDFResume
export interface ResumeFactory { createPDFResume(): PDFResume createMarkdownResume(): MarkdownResume createJSONResume(): JSONResume }
이제 일반 제품 유형과 추상 팩토리를 정의했으므로 이제 ConcreteFactories 생성을 진행할 차례입니다. 모든 일반 제품 유형의 다양한 변형.
이력서에는 창의적, 미니멀리스트, 현대적의 3가지 변형이 있습니다. 그리고 3가지 유형의 일반 제품: JSON , PDF 및 Markdown.
추상 팩토리( ResumeFactory )는 제품 생성을 담당하는 3가지 팩토리 메소드를 정의합니다.
제품당 다양한 변형을 지원하려면 콘크리트 공장을 3개 만들어야 합니다.
각 콘크리트 공장에서는 고유한 맛을 지닌 3가지 유형의 제품을 제작합니다.
공장/CreativeResumeFactory
export class CreativeResumeFactory implements ResumeFactory { createPDFResume(): CreativePDFResume { return new CreativePDFResume() // CreativePDFResume implements PDFResume } createMarkdownResume(): CreativeMarkdownResume { return new CreativeMarkdownResume() // CreativeMarkdownResume implements MarkdownResume } createJSONResume(): CreativeJSONResume { return new CreativeJSONResume() // CreativeJSONResume implements JSONResume } }
공장/MinimalistResumeFactory
// User inputs... let theme = "minimalist" let format = "pdf" let factory: ResumeFactory switch (theme) { case "minimalist": factory = new MinimalistResumeFactory() break case "modern": factory = new ModernResumeFactory() break case "creative": factory = new CreativeResumeFactory() break default: throw new Error("Invalid theme.") } const userInput = await getUserInput() let resume switch (format) { case "pdf": resume = factory.createPDFResume() break case "markdown": resume = factory.createMarkdownResume() break case "json": resume = factory.createJSONResume() break default: throw new Error("Invalid format.") }
공장/ModernResumeFactory
export abstract class PDFResume {} export abstract class JSONResume {} export abstract class MarkdownResume {}
이제 CreativeResumeFactory에서 반환되는 이전 ConcreteProducts
를 만들어 보겠습니다.PDF 이력서 :
이력서/pdf/CreativePDFResume
export interface ResumeFactory { createPDFResume(): PDFResume createMarkdownResume(): MarkdownResume createJSONResume(): JSONResume }
마크다운 이력서 :
이력서/markdown/CreativeMarkdownResume
export class CreativeResumeFactory implements ResumeFactory { createPDFResume(): CreativePDFResume { return new CreativePDFResume() // CreativePDFResume implements PDFResume } createMarkdownResume(): CreativeMarkdownResume { return new CreativeMarkdownResume() // CreativeMarkdownResume implements MarkdownResume } createJSONResume(): CreativeJSONResume { return new CreativeJSONResume() // CreativeJSONResume implements JSONResume } }
JSON 이력서 :
이력서/json/CreativeJSONResume
// User inputs... let theme = "minimalist" let format = "pdf" let factory: ResumeFactory switch (theme) { case "minimalist": factory = new MinimalistResumeFactory() break case "modern": factory = new ModernResumeFactory() break case "creative": factory = new CreativeResumeFactory() break default: throw new Error("Invalid theme.") } const userInput = await getUserInput() let resume switch (format) { case "pdf": resume = factory.createPDFResume() break case "markdown": resume = factory.createMarkdownResume() break case "json": resume = factory.createJSONResume() break default: throw new Error("Invalid format.") }
다음으로 MinimalistResumeFactory에서 반환되는 이전 ConcreteProducts
를 만들어 보겠습니다.PDF 이력서 :
이력서/pdf/MinimalistPDFResume
npm install npm start
마크다운 이력서 :
이력서/markdown/MinimalistMarkdownResume
export type ResumeData = { name: string email: string phone: string experience: Experience[] } export type Experience = { company: string position: string startDate: string endDate: string description: string }
JSON 이력서 :
이력서/json/MinimalistJSONResume
import { JSONResume } from "../resumes/json/JSONResume" import { MarkdownResume } from "../resumes/markdown/MarkdownResume" import { PDFResume } from "../resumes/pdf/PdfResume" export interface ResumeFactory { createPDFResume(): PDFResume createMarkdownResume(): MarkdownResume createJSONResume(): JSONResume }
마지막으로 ModernResumeFactory에서 반환되는 이전 ConcreteProducts
를 만들어 보겠습니다.PDF 이력서 :
이력서/pdf/ModernPDFResume
import * as fs from "fs/promises" import { ResumeData } from "../../interfaces/Types" export abstract class JSONResume { protected data!: ResumeData & { style: string } abstract generate(data: ResumeData): void async saveToFile(fileName: string): Promise<void> { await fs.writeFile(fileName, JSON.stringify(this.data, null, 2)) } getData(): any { return this.data } }
마크다운 이력서 :
이력서/markdown/ModernMarkdownResume
import * as fs from "fs/promises" import { ResumeData } from "../../interfaces/Types" export abstract class MarkdownResume { protected content: string = "" abstract generate(data: ResumeData): void async saveToFile(fileName: string): Promise<void> { await fs.writeFile(fileName, this.content) } getContent(): string { return this.content } }
JSON 이력서 :
이력서/json/ModernJSONResume
import * as fs from "fs" import PDFDocument from "pdfkit" import { ResumeData } from "../../interfaces/Types" export abstract class PDFResume { protected doc: PDFKit.PDFDocument constructor() { this.doc = new PDFDocument() } abstract generate(data: ResumeData): void async saveToFile(fileName: string): Promise<void> { const stream = fs.createWriteStream(fileName) this.doc.pipe(stream) this.doc.end() await new Promise<void>((resolve, reject) => { stream.on("finish", resolve) stream.on("error", reject) }) } getBuffer(): Buffer { return this.doc.read() as Buffer } }
클라이언트 코드에서 팩토리를 사용하여 이전 작업의 결실을 맺어 보겠습니다.
이제 공장을 사용하여 이력서 작성 라이브러리를 매우 깔끔한 방식으로 사용할 수 있는 방법을 살펴보세요.
사용자는 다음 두 가지 사항만 제공하면 됩니다.
index.ts
import { ResumeFactory } from "../interfaces/ResumeFactory" import { CreativeJSONResume } from "../resumes/json/CreativeJSONResume" import { CreativeMarkdownResume } from "../resumes/markdown/CreativeMarkdownResume" import { CreativePDFResume } from "../resumes/pdf/CreativePDFResume" export class CreativeResumeFactory implements ResumeFactory { createPDFResume(): CreativePDFResume { return new CreativePDFResume() // CreativePDFResume extends PDFResume } createMarkdownResume(): CreativeMarkdownResume { return new CreativeMarkdownResume() // CreativeMarkdownResume extends MarkdownResume } createJSONResume(): CreativeJSONResume { return new CreativeJSONResume() // CreativeJSONResume extends JSONResume } }
위 코드는 세 단계로 작동합니다.
사용자는 제품과 해당 변형이 어떻게 생성되는지에 관심이 없습니다. 테마와 형식만 선택하면, 요청한 대로 해당 제품이 생성됩니다.
이제 클라이언트 코드는 변경 사항에 대해 강력합니다. 새로운 테마나 스타일을 추가하고 싶다면 이를 담당하는 새로운 공장을 만들면 됩니다.
분필 라이브러리를 사용하여 의미론적 의미에 따라 터미널 로그에 색상을 지정했습니다.
CLI 앱 사용자로부터 입력을 얻을 수 있도록 우리는 사용자로부터 다양한 유형의 입력을 얻을 수 있는 매우 매력적이고 사용자 친화적인 방법을 제공하는 inquirer 패키지를 사용했습니다.
유틸리티/userInput
export abstract class PDFResume {} export abstract class JSONResume {} export abstract class MarkdownResume {}
추상 팩토리 패턴은 소프트웨어 디자이너와 개발자에게 강력한 도구입니다. 구체적인 클래스를 지정하지 않고 관련 객체의 패밀리를 생성하는 구조화된 접근 방식을 제공합니다. 이 패턴은 다음과 같은 경우에 특히 유용합니다.
실제 예에서는 Abstract Factory 패턴을 적용하여 유연하고 확장 가능한 이력서 생성 시스템을 만드는 방법을 살펴보았습니다. 이 시스템은 기존 코드를 수정하지 않고도 새로운 이력서 스타일이나 출력 형식을 쉽게 수용할 수 있어 개방/폐쇄 원칙의 강력한 효과를 실제로 보여줍니다.
추상 팩토리 패턴은 많은 이점을 제공하지만 코드베이스에 추가적인 복잡성을 초래할 수 있다는 점에 유의하는 것이 중요합니다. 따라서 특정 사용 사례에 제공되는 유연성이 필요한지 여부를 평가하는 것이 중요합니다.
추상 팩토리와 같은 디자인 패턴을 마스터하면 강력하고 유연하며 유지 관리가 가능한 소프트웨어 시스템을 만들 수 있는 능력이 향상됩니다. 소프트웨어 설계 기술을 향상하려면 프로젝트에서 이러한 패턴을 계속 탐색하고 적용하세요.
질문이 있으시거나 추가 논의를 원하시면 여기로 연락주세요.
즐거운 코딩하세요!
위 내용은 추상 팩토리 패턴 마스터하기: 종합 가이드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!