掌握原型設計模式:綜合指南
您是否曾經從庫中導入一個物件並嘗試克隆它,但卻失敗了,因為克隆它需要對庫的內部結構有廣泛的了解?
或者,在一個專案工作了很長時間之後,您休息一下重構程式碼,並注意到您正在程式碼庫的各個部分重新克隆許多複雜的物件?
嗯,原型設計模式已經滿足你了!
在本文中,我們將探索原型設計模式,同時建立功能齊全的日記範本 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 函數的工作原理如下:
- 我們首先從 registry 取得所有模板,然後循環返回的模板數組並使用我們之前在 JournalTemplate 中定義的 display 方法 上課。
使用範本建立日記條目:建立日記範本的原因,是為了讓我們在撰寫不同類型的日記時變得更輕鬆,而不是面對空白頁,更好的是更容易當面對一堆連續的章節標題和提示時,填寫日記。
讓我們深入了解 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()
從現有範本建立範本 :
最後,我們將看到原型設計模式的實際應用。
讓我們探索如何透過覆寫現有模板來動態建立新類型的模板。
- 首先,我們提示使用者從現有範本中選擇他想要覆蓋的範本。
- 然後我們再次提示它輸入新建立的範本的名稱。
- 我們使用登錄來取得給定使用者選擇的範本名稱的範本。
- 我們使用clone方法來取得與所選模板相符的複製物件。
從下面的程式碼中可以看到,我們甚至不需要了解 JournalTemplate 類別的詳細信息,也不需要透過導入它來污染我們的程式碼。
TemplateActions.ts >從現有範本建立
- 最後,我們將使用者指定的範本名稱設為新建立的對象,然後提示使用者使用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 >編輯範本部分
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中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。
