您是否曾經從庫中導入一個物件並嘗試克隆它,但卻失敗了,因為克隆它需要對庫的內部結構有廣泛的了解?
或者,在一個專案工作了很長時間之後,您休息一下重構程式碼,並注意到您正在程式碼庫的各個部分重新克隆許多複雜的物件?
嗯,原型設計模式已經滿足你了!
在本文中,我們將探索原型設計模式,同時建立功能齊全的日記範本 Node.js CLI 應用程式。
話不多說,讓我們開始吧!
原型是一種創意設計模式,它是一類設計模式,用於處理使用new 創建物件的本機方式所帶來的不同問題。 關鍵字或運算子。
工廠設計模式解決了以下創建問題:
如何複製應用程式中的現有物件而不依賴其具體類別?
一些複雜的物件很難克隆,因為它們要么有很多需要您不知道的特定業務邏輯的字段,要么有很多無法從外部訪問的私有字段物體。
讓我們以從 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 方法。
或建立一個通用介面原型,它可以由所有可複製物件實現。
擁有公共介面的一個好處是能夠在公共註冊服務類別中註冊所有原型,該服務類別將負責快取常用的原型並將其傳回給使用者。不必每次呼叫 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 方法稍後將用於向終端顯示彩色格式良好的輸出。
chalk 套件用於為輸出的終端文字的一些片段著色。
我們的 JournalTemplate 物件顧名思義,用作建立其他範本或日記檔案條目的範本或原型。
這就是為什麼我們將 clone 方法加入 JournalTemplate 類別中。
我們加入它是為了將處理複製業務邏輯的責任交給 JournalTemplate 物件本身,而不是消費程式碼。
現在讓我們建立 TemplateRegistry 類,它將負責儲存 JournalTemplate 類原型實例。同時提供操作這些實例的方法。
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 >建立範本
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 >使用模板
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 >編輯範本部分
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中文網其他相關文章!