>웹 프론트엔드 >JS 튜토리얼 >프로토타입 디자인 패턴 익히기: 종합 가이드

프로토타입 디자인 패턴 익히기: 종합 가이드

Barbara Streisand
Barbara Streisand원래의
2024-11-17 21:41:021053검색

라이브러리에서 개체를 가져와서 복제하려고 했지만 복제하려면 라이브러리 내부에 대한 광범위한 지식이 필요하기 때문에 실패한 적이 있습니까?

아니면 오랫동안 프로젝트를 진행한 후 코드를 리팩토링하기 위해 잠시 휴식을 취했는데 코드베이스의 다양한 부분에서 복잡한 개체가 많이 다시 복제되고 있다는 사실을 발견하셨나요?

자, 프로토타입 디자인 패턴이 적용되었습니다!

이 기사에서는 완전한 기능을 갖춘 저널링 템플릿 Node.js CLI 애플리케이션을 구축하면서 프로토타입 디자인 패턴을 살펴보겠습니다.

자세히 살펴보겠습니다!

개요

프로토타입창의적 디자인 패턴으로, 새로운 기능을 사용하여 객체를 생성하는 기본 방식에서 발생하는 다양한 문제를 다루는 디자인 패턴 카테고리입니다. 키워드 또는 연산자.

문제

공장 설계 패턴은 다음과 같은 생성 문제를 해결합니다.

  1. 구체 클래스에 의존하지 않고 애플리케이션의 기존 객체를 어떻게 복사할 수 있나요?

  2. 일부 복잡한 개체는 사용자가 알지 못하는 특정 비즈니스 논리가 필요한 필드가 많거나 외부에서 액세스할 수 없는 비공개 필드가 많기 때문에 복제하기 어렵습니다. 개체.

socket.io 라이브러리에서 가져온 socket 객체를 예로 들어 보겠습니다. 이를 직접 복제해야 합니까?

라이브러리 내부의 코드를 살펴보고, 소켓이 어떻게 작동하는지 이해해야 하며, 객체에는 복제하기 위해 스스로 처리해야 하는 순환 종속성도 있습니다.

또한 코드는 소켓 클래스나 인터페이스 및 해당 비즈니스 로직에 따라 생성되므로 견고한 종속성 반전 원칙을 위반하고 변경에 대한 코드의 견고성이 떨어집니다.

해결책

프로토타입 디자인 패턴은 객체를 복사하는 책임을 객체 자체에 위임하고 모든 객체의 클래스에 clone 메소드를 선언하여 이러한 문제를 해결합니다. 복제 가능합니다.

class Socket {
  // code........

  clone(): Socket {
    // business logic to instantiate the socket.
    return new Socket(/*...Params*/)
  }
}


const socket1 = new Socket()
const socket2 = socket1.clone()

구조

프로토타입 디자인 패턴을 구현하려면 복제 가능 객체 내에 clone 메소드를 직접 포함할 수 있습니다.

또는 복제 가능한 모든 개체에서 구현할 수 있는 공통 인터페이스 프로토타입을 만드세요. Mastering the Prototype Design Pattern: A Comprehensive Guide

공통 인터페이스의 한 가지 이점은 공통 등록 서비스 클래스에 모든 프로토타입을 등록할 수 있다는 것입니다. 이 클래스는 자주 사용되는 프로토타입을 캐싱하여 사용자에게 반환합니다. clone 메소드가 호출될 때마다 객체를 복제할 필요가 없습니다.

특히 복잡한 개체를 복제할 때 매우 유용할 수 있습니다.

Mastering the Prototype Design Pattern: A Comprehensive Guide

실제 시나리오

이 섹션에서는 미니 저널링 템플릿 Nodejs CLI 애플리케이션을 구축하여 이 디자인 패턴을 시연해 보겠습니다.

앞서 살펴본 것처럼 프로토타입 디자인 패턴은 객체를 객체 자체에 복제하는 책임을 위임합니다.

그런데 왜 프로토타입이라고 불리는지 궁금하신가요? 그게 복제와 무슨 관련이 있나요?

실제 사례를 통해 이에 대한 답변을 드리겠습니다. 계속해서 읽어보시고 지켜봐 주시기 바랍니다.
이 저장소에서 최종 코드를 찾을 수 있습니다. 복제하고 다음 명령을 실행하면 됩니다.

프로토타입 만들기: 저널링 템플릿 클래스

먼저 다음 속성을 가진 JournalTemplate을 만들어 보겠습니다.

  1. 이름 : 템플릿을 식별하는 데 필요합니다.
  2. 섹션 : 섹션은 감사, 도전, 내일의 목표와 같은 특정 주제나 주제에 대해 예약된 저널링 템플릿의 일부입니다....

각 섹션은 다음 속성으로 구성됩니다.

  • 제목 섹션의 주제 또는 테마: 감사, 도전, 내일의 목표...
  • 프롬프트 사용자가 섹션 저널링 텍스트를 작성하려고 할 때 사용자에게 표시되는 메시지입니다.

JournalTemplate.ts

class Socket {
  // code........

  clone(): Socket {
    // business logic to instantiate the socket.
    return new Socket(/*...Params*/)
  }
}


const socket1 = new Socket()
const socket2 = socket1.clone()

JournalTemplate 클래스에는 다양한 속성을 설정하기 위한 다양한 유틸리티 메서드가 있습니다.

display 메소드는 나중에 터미널에 색상이 잘 지정된 출력을 표시하는 데 사용됩니다.

분필 패키지는 출력되는 터미널 텍스트의 일부 조각을 색칠하는 데 사용됩니다.

저희 JournalTemplate 개체는 이름에서 알 수 있듯이 다른 템플릿을 만들거나 파일 항목을 저널링하기 위한 템플릿이나 프로토타입으로 사용하도록 만들어졌습니다.

그래서 JournalTemplate 클래스에 clone 메소드를 추가했습니다.

복제 비즈니스 로직을 처리하는 책임을 소비 코드가 아닌 JournalTemplate 개체 자체에 부여하기 위해 추가했습니다.

저널링 템플릿 등록 선언

이제 JournalTemplate 클래스 프로토타입 인스턴스를 저장하는 역할을 담당하는 TemplateRegistry 클래스를 만들어 보겠습니다. 해당 인스턴스를 조작하는 방법을 제공합니다.

TemplateRegistry.ts

class Socket {
  // code........

  clone(): Socket {
    // business logic to instantiate the socket.
    return new Socket(/*...Params*/)
  }
}


const socket1 = new Socket()
const socket2 = socket1.clone()

레지스트리는 이름으로 빠르게 검색할 수 있도록 해당 클래스를 Map 객체에 저장하고 템플릿 인스턴스를 추가하거나 폐기하기 위한 다양한 유틸리티 메서드를 제공합니다.

저널링 템플릿 레지스터 인스턴스화

이제 템플릿 레지스터를 인스턴스화한 다음 일부 초기 템플릿을 시드해 보겠습니다.

registry.ts

import chalk from "chalk"

import { TemplateSection } from "./types"

export interface TemplateSection {
  title: string
  prompt: string
}

export class JournalTemplate {
  constructor(
    public name: string,
    public sections: TemplateSection[]
  ) {}
  clone(): JournalTemplate {
    return new JournalTemplate(
      this.name,
      this.sections.map((s) => ({ ...s }))
    )
  }

  display(): void {
    console.log(chalk.cyan(`\nTemplate: ${this.name}`))
    this.sections.forEach((section, index) => {
      console.log(chalk.yellow(`${index + 1}. ${section.title}`))
      console.log(chalk.gray(` Prompt: ${section.prompt}`))
    })
  }

  addSection(section: TemplateSection): void {
    this.sections.push(section)
  }

  removeSection(index: number): void {
    if (index >= 0 && index < this.sections.length) {
      this.sections.splice(index, 1)
    } else {
      throw new Error("Invalid section index")
    }
  }

  editSection(index: number, newSection: TemplateSection): void {
    if (index >= 0 && index < this.sections.length) {
      this.sections[index] = newSection
    } else {
      throw new Error("Invalid section index")
    }
  }

  getSectionCount(): number {
    return this.sections.length
  }

  getSection(index: number): TemplateSection | undefined {
    return this.sections[index]
  }

  setName(newName: string): void {
    this.name = newName
  }
}

템플릿 작업 방법 정의

이 섹션에서는 다음과 같은 다양한 작업을 실행하기 위해 애플리케이션 메뉴에서 사용할 여러 기능을 정의합니다.

  1. 사용자에게 템플릿 이름을 입력하라는 메시지를 표시한 다음 반복적으로 다시 메시지를 표시하여 원하는 만큼 섹션을 생성합니다.
  2. 기존 또는 생성된 템플릿을 모두 봅니다.
  3. 템플릿을 사용하여 저널링 파일 항목을 만듭니다.
  4. 기존 템플릿에서 새 템플릿 만들기: 사용자에게 기존 템플릿을 선택하라는 메시지가 표시되고 해당 템플릿을 직접 사용하거나 이름과 섹션을 재정의할 수 있습니다.

새로 생성된 템플릿을 사용하여 새 저널 항목(1)을 생성할 수 있습니다.

템플릿 만들기 :

TemplateActions.ts > 템플릿 만들기

import { JournalTemplate } from "./JournalTemplate"

export class TemplateRegistry {
  private templates: Map<string, JournalTemplate> = new Map()

  addTemplate(name: string, template: JournalTemplate): void {
    this.templates.set(name, template)
  }

  getTemplate(name: string): JournalTemplate | undefined {
    const template = this.templates.get(name)
    return template ? template.clone() : undefined
  }

  getTemplateNames(): string[] {
    return Array.from(this.templates.keys())
  }
}

  • 템플릿을 만들려면 먼저 사용자에게 템플릿 이름을 입력하라는 메시지를 표시합니다.
  • 그런 다음 이름과 섹션의 빈 배열을 사용하여 새 템플릿 개체를 인스턴스화합니다.
  • 그런 다음 사용자에게 섹션의 세부 정보를 입력하라는 메시지가 표시되며, 모든 섹션의 정보를 입력한 후 사용자는 중지하거나 추가 섹션을 입력할 수 있습니다.

utils.ts > 프롬프트ForSectionDetails

import { JournalTemplate } from "./JournalTemplate"
import { TemplateRegistry } from "./TemplateRegistry"

export const registry = new TemplateRegistry()

registry.addTemplate(
  "Daily Reflection",
  new JournalTemplate("Daily Reflection", [
    {
      title: "Gratitude",
      prompt: "List three things you're grateful for today.",
    },
    { title: "Accomplishments", prompt: "What did you accomplish today?" },
    {
      title: "Challenges",
      prompt: "What challenges did you face and how did you overcome them?",
    },
    {
      title: "Tomorrow's Goals",
      prompt: "What are your top 3 priorities for tomorrow?",
    },
  ])
)

registry.addTemplate(
  "Weekly Review",
  new JournalTemplate("Weekly Review", [
    { title: "Highlights", prompt: "What were the highlights of your week?" },
    {
      title: "Lessons Learned",
      prompt: "What important lessons did you learn this week?",
    },
    {
      title: "Progress on Goals",
      prompt: "How did you progress towards your goals this week?",
    },
    {
      title: "Next Week's Focus",
      prompt: "What's your main focus for next week?",
    },
  ])
)

promptForSectionDetails 기능은 inquirer 패키지를 사용하여 제목을 요청한 다음 사용자에게 순차적으로 프롬프트를 표시합니다.

템플릿 보기 :

TemplateActions.ts > 뷰템플릿

import chalk from "chalk"
import inquirer from "inquirer"

import { JournalTemplate } from "./JournalTemplate"
import { registry } from "./registry"
import { editTemplateSections } from "./templateSectionsActions"
import { promptForSectionDetails } from "./utils"

export async function createTemplate(): Promise<void> {
  const { name } = await inquirer.prompt<{ name: string }>([
    {
      type: "input",
      name: "name",
      message: "Enter a name for the new template:",
    },
  ])

  const newTemplate = new JournalTemplate(name, [])
  let addMore = true
  while (addMore) {
    const newSection = await promptForSectionDetails()
    newTemplate.addSection(newSection)
    const { more } = await inquirer.prompt<{ more: boolean }>([
      {
        type: "confirm",
        name: "more",
        message: "Add another section?",
        default: false,
      },
    ])
    addMore = more
  }

  registry.addTemplate(name, newTemplate)
  console.log(chalk.green(`Template "${name}" created successfully!`))
}

viewTemplates 기능은 다음과 같이 작동합니다.

  1. 먼저 registry에서 모든 템플릿을 가져온 다음 반환된 템플릿 배열을 반복하고 이전에 JournalTemplate에서 정의한 display 메서드를 사용합니다. 수업.

템플릿을 사용하여 저널링 항목 만들기 : 저널링 템플릿을 만드는 이유는 다양한 유형의 저널을 작성할 때 빈 페이지를 마주하는 것보다 더 쉽게 작성하기 위한 것입니다. 일련의 섹션 제목과 프롬프트가 나타나면 일지를 채우세요.

useTemplate 기능을 자세히 살펴보겠습니다.

  1. 먼저 등록에서 템플릿 이름을 가져온 후 기존 템플릿 중 하나의 템플릿을 선택합니다.
  2. 템플릿의 모든 섹션에 대해 사용자는 저널 섹션 텍스트를 채우기 위해 선호하는 편집기를 열라는 요청을 받게 됩니다.

TemplateActions.ts > 템플릿 사용

class Socket {
  // code........

  clone(): Socket {
    // business logic to instantiate the socket.
    return new Socket(/*...Params*/)
  }
}


const socket1 = new Socket()
const socket2 = socket1.clone()

기존 템플릿에서 템플릿 만들기 :

마지막으로 프로토타입 디자인 패턴이 실제로 작동하는 모습을 살펴보겠습니다.

기존 템플릿을 재정의하여 새로운 유형의 템플릿을 동적으로 만드는 방법을 살펴보겠습니다.

  1. 먼저 사용자에게 기존 템플릿 중에서 재정의하고 싶은 템플릿을 선택하라는 메시지를 표시합니다.
  2. 그런 다음 새로 생성된 템플릿의 이름을 입력하라는 메시지를 다시 표시합니다.
  3. 저희는 레지스트리를 사용하여 사용자가 선택한 템플릿 이름이 지정된 템플릿을 가져옵니다.
  4. 선택한 템플릿과 일치하는 복제 개체를 가져오기 위해 clone 메서드를 사용합니다.

아래 코드에서 볼 수 있듯이 JournalTemplate 클래스의 세부 사항을 알 필요도 없고 코드를 가져와서 오염시킬 필요도 없습니다.

TemplateActions.ts > createFromExistingTemplate

  1. 마지막으로 사용자가 지정한 템플릿 이름을 새로 생성된 객체로 설정한 다음 editTemplateSections 메소드를 사용하여 기존 템플릿 섹션에 대한 모든 조잡한 작업을 수행하라는 메시지를 사용자에게 표시합니다. 코드 블록 바로 뒤에 설명합니다.
import chalk from "chalk"

import { TemplateSection } from "./types"

export interface TemplateSection {
  title: string
  prompt: string
}

export class JournalTemplate {
  constructor(
    public name: string,
    public sections: TemplateSection[]
  ) {}
  clone(): JournalTemplate {
    return new JournalTemplate(
      this.name,
      this.sections.map((s) => ({ ...s }))
    )
  }

  display(): void {
    console.log(chalk.cyan(`\nTemplate: ${this.name}`))
    this.sections.forEach((section, index) => {
      console.log(chalk.yellow(`${index + 1}. ${section.title}`))
      console.log(chalk.gray(` Prompt: ${section.prompt}`))
    })
  }

  addSection(section: TemplateSection): void {
    this.sections.push(section)
  }

  removeSection(index: number): void {
    if (index >= 0 && index < this.sections.length) {
      this.sections.splice(index, 1)
    } else {
      throw new Error("Invalid section index")
    }
  }

  editSection(index: number, newSection: TemplateSection): void {
    if (index >= 0 && index < this.sections.length) {
      this.sections[index] = newSection
    } else {
      throw new Error("Invalid section index")
    }
  }

  getSectionCount(): number {
    return this.sections.length
  }

  getSection(index: number): TemplateSection | undefined {
    return this.sections[index]
  }

  setName(newName: string): void {
    this.name = newName
  }
}

templateSectionsAction > editTemplateSection

import { JournalTemplate } from "./JournalTemplate"

export class TemplateRegistry {
  private templates: Map<string, JournalTemplate> = new Map()

  addTemplate(name: string, template: JournalTemplate): void {
    this.templates.set(name, template)
  }

  getTemplate(name: string): JournalTemplate | undefined {
    const template = this.templates.get(name)
    return template ? template.clone() : undefined
  }

  getTemplateNames(): string[] {
    return Array.from(this.templates.keys())
  }
}

아래에 정의된 editTemplateSections는 기본적으로 다음과 같은 다양한 작업을 제공하여 필요에 따라 기존 섹션을 재정의하도록 사용자에게 요청하는 메뉴를 표시합니다.

  • 섹션 추가
  • 섹션 제거
  • 섹션 편집

애플리케이션 메뉴

마지막으로 cli 앱을 부트스트랩하고 다양한 템플릿 조작 옵션이 있는 메뉴를 표시하는 index.ts 파일의 이전 기능을 모두 활용합니다.

  • 템플릿을 만듭니다.
  • 기존 템플릿에서 템플릿을 만듭니다.
  • 템플릿 보기.
  • 템플릿을 사용하여 일지 항목을 만듭니다.
  • 프로그램을 종료합니다.

index.ts

class Socket {
  // code........

  clone(): Socket {
    // business logic to instantiate the socket.
    return new Socket(/*...Params*/)
  }
}


const socket1 = new Socket()
const socket2 = socket1.clone()

결론

프로토타입 디자인 패턴은 기존 객체를 복제하여 새로운 객체를 생성하는 강력한 방법을 제공합니다. 저널링 템플릿 애플리케이션에서 이 패턴을 통해 어떻게 기존 템플릿을 기반으로 새 템플릿을 생성하여 프로토타입 패턴의 유연성과 효율성을 입증할 수 있는지 확인했습니다.

이 패턴을 사용하여 확장 및 수정이 쉬운 시스템을 만들었으며 실제 애플리케이션에서 객체 지향 디자인 패턴의 진정한 힘을 보여주었습니다.

연락하다

질문이 있으시거나 추가 논의를 원하시면 여기로 연락주세요.

즐거운 코딩하세요!

위 내용은 프로토타입 디자인 패턴 익히기: 종합 가이드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.