ホームページ > ウェブフロントエンド > jsチュートリアル > プロトタイプ設計パターンをマスターする: 包括的なガイド

プロトタイプ設計パターンをマスターする: 包括的なガイド

Barbara Streisand
リリース: 2024-11-17 21:41:02
オリジナル
1042 人が閲覧しました

ライブラリからオブジェクトをインポートしてクローンを作成しようとしたが、クローン作成にはライブラリの内部構造に関する広範な知識が必要なので失敗したという経験はありますか?

あるいは、長期間プロジェクトに取り組んだ後、休憩を取ってコードをリファクタリングし、コードベースのさまざまな部分で多くの複雑なオブジェクトを再複製していることに気づいたのではないでしょうか?

プロトタイプのデザイン パターンで問題は解決しました!

この記事では、完全に機能するジャーナリング テンプレート 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

共通のインターフェースを持つ利点の 1 つは、すべてのプロトタイプを共通の登録サービス クラスに登録できることです。このクラスは、頻繁に使用されるプロトタイプをキャッシュしてユーザーに返す役割を果たします。 clone メソッドが呼び出されるたびにオブジェクトのクローンを作成する必要がなくなります。

これは、特に複雑なオブジェクトのクローンを作成する場合に非常に便利です。

Mastering the Prototype Design Pattern: A Comprehensive Guide

実践的なシナリオ

このセクションでは、ミニジャーナリング テンプレート Nodejs CLI アプリケーションを構築して、この設計パターンをデモします。

前に見たように、プロトタイプ設計パターンは、オブジェクトのクローンをオブジェクト自体に複製する責任を委任します。

しかし、なぜそれがプロトタイプと呼ばれているのについて疑問に思ったことはありますか?つまり、それがクローン作成と何の関係があるのですか?

この実践的な例を通じてその答えを示しますので、読み続けて楽しみにしていてください。
最終的なコードはこのリポジトリにあります。クローンを作成して、次のコマンドを実行するだけです。

プロトタイプの作成: ジャーナリング テンプレート クラス

まず、次の属性を持つ JournalTemplate を作成しましょう:

  1. name : テンプレートを識別するために必要になります。
  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 オブジェクトは、名前が示すとおり、他のテンプレートまたはジャーナリング ファイル エントリを作成するためのテンプレートまたはプロトタイプとして使用することを目的としています。

そのため、clone メソッドを JournalTemplate クラスに追加しました。

クローン作成ビジネス ロジックを処理する責任を、使用するコードではなく 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 > createTemplate

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 >プロンプトセクション詳細

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. まず、レジストリからテンプレート名を取得した後、既存のテンプレートの中からテンプレートを 1 つ選択します。
  2. テンプレート内のすべてのセクションについて、ユーザーは好みのエディタを開いて日記セクションのテキストを入力するように求められます。

TemplateActions.ts > useTemplate

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 >既存のテンプレートから作成

  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 > editTemplateSections

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 は基本的にメニューを表示し、次のようなさまざまな操作を提供することで、必要に応じて既存のセクションをオーバーライドするようユーザーに求めます。

  • セクションを追加
  • セクションを削除
  • セクションを編集

アプリケーションメニュー

最後に、index.ts ファイル内の前述の関数をすべて利用して、CLI アプリをブートストラップし、さまざまなテンプレート操作オプションを含むメニューを表示します。

  • テンプレートを作成します。
  • 既存のテンプレートからテンプレートを作成します。
  • テンプレートを表示します。
  • テンプレートを使用して日記エントリを作成します。
  • プログラムを終了します。

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 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート