ホームページ >ウェブフロントエンド >jsチュートリアル >プロトタイプ設計パターンをマスターする: 包括的なガイド
ライブラリからオブジェクトをインポートしてクローンを作成しようとしたが、クローン作成にはライブラリの内部構造に関する広範な知識が必要なので失敗したという経験はありますか?
あるいは、長期間プロジェクトに取り組んだ後、休憩を取ってコードをリファクタリングし、コードベースのさまざまな部分で多くの複雑なオブジェクトを再複製していることに気づいたのではないでしょうか?
プロトタイプのデザイン パターンで問題は解決しました!
この記事では、完全に機能するジャーナリング テンプレート Node.js CLI アプリケーションを構築しながら、プロトタイプの設計パターンを検討します。
早速、詳しく見ていきましょう!
プロトタイプ は 創造的なデザイン パターン であり、新しいオブジェクトを使用してオブジェクトを作成するネイティブな方法に伴うさまざまな問題に対処するデザイン パターンのカテゴリです。 キーワードまたは演算子。
ファクトリ デザイン パターンは、次の作成上の問題を解決します。
具象クラスに依存せずにアプリケーション内の既存のオブジェクトをコピーするにはどうすればよいですか?
一部の複雑なオブジェクトは、ユーザーが知らない特定のビジネス ロジックを必要とするフィールドが多数あるか、外部からアクセスできないプライベート フィールドが多数あるため、複製が困難です。オブジェクト。
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 メソッドを直接含めることができます。
または、すべての複製可能なオブジェクトで実装できる共通インターフェイス プロトタイプ を作成します。
共通のインターフェースを持つ利点の 1 つは、すべてのプロトタイプを共通の登録サービス クラスに登録できることです。このクラスは、頻繁に使用されるプロトタイプをキャッシュしてユーザーに返す役割を果たします。 clone メソッドが呼び出されるたびにオブジェクトのクローンを作成する必要がなくなります。
これは、特に複雑なオブジェクトのクローンを作成する場合に非常に便利です。
このセクションでは、ミニジャーナリング テンプレート Nodejs CLI アプリケーションを構築して、この設計パターンをデモします。
前に見たように、プロトタイプ設計パターンは、オブジェクトのクローンをオブジェクト自体に複製する責任を委任します。
しかし、なぜそれがプロトタイプと呼ばれているのについて疑問に思ったことはありますか?つまり、それがクローン作成と何の関係があるのですか?
この実践的な例を通じてその答えを示しますので、読み続けて楽しみにしていてください。
最終的なコードはこのリポジトリにあります。クローンを作成して、次のコマンドを実行するだけです。
まず、次の属性を持つ JournalTemplate を作成しましょう:
各セクションは次の属性で構成されます:
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)。
テンプレートを作成します :
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 関数は次のように動作します:
テンプレートを使用して日記エントリを作成します : 日記テンプレートを作成する理由は、さまざまな種類の日記を書くときに作業を楽にするためです。空のページに直面するのではなく、連続したセクションのタイトルやプロンプトが大量に表示された場合は、日記を記入してください。
useTemplate 関数を詳しく見てみましょう:
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()
既存のテンプレートからテンプレートを作成する :
最後に、プロトタイプのデザインパターンが実際に動作している様子を見ていきます。
既存のテンプレートをオーバーライドして、新しいタイプのテンプレートを動的に作成する方法を見てみましょう。
以下のコードからわかるように、JournalTemplate クラスの詳細を知る必要も、インポートしてコードを汚染する必要もありません。
TemplateActions.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 } }
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 サイトの他の関連記事を参照してください。