我的設計模式之旅 ⑤ 裝飾模式

来源:https://www.cnblogs.com/linxiaoxu/archive/2022/09/10/16675819.html
-Advertisement-
Play Games

一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現裝飾模式。小明和小王去吃沙縣小吃,各自喜歡不同的搭配,需要考慮每個人飲食喜好不同,隨時可能的變化。 ...


一個菜鳥的設計模式之旅,文章可能會有不對的地方,懇請大佬指出錯誤。

編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。

程式介紹

image-20220910000340764

本程式實現裝飾模式。小明和小王去吃沙縣小吃,各自喜歡不同的搭配,需要考慮每個人飲食喜好不同,隨時可能的變化。

小明想吃不帶湯的面
拿個碗裝了麵條加個炸蛋加牛肉片加點醬汁
小王想吃餛飩,還特別愛吃雞腿
拿個碗裝了餛飩加個雞腿加個雞腿加點湯

程式代碼

decorator.go

package main

import "fmt"

type component interface {
	operation()
}

type noodle struct{}
type huntun struct{}

func (e noodle) operation() {
	fmt.Printf("拿個碗裝了麵條")
}
func (e huntun) operation() {
	fmt.Printf("拿個碗裝了餛飩")
}

type decorator struct {
	component component
}

func (d *decorator) setComponent(c component) {
	d.component = c
}

func (d *decorator) operation() {
	if d.component != nil {
		d.component.operation()
	}
}

type egg struct{ decorator }
type beef struct{ decorator }
type sauce struct{ decorator }
type soup struct{ decorator }
type chicken struct{ decorator }

func (e egg) operation() {
	e.decorator.operation()
	fmt.Printf("加個炸蛋") // 可以是該結構類型獨有的方法
}

func (e beef) operation() {
	e.component.operation()
	fmt.Printf("加牛肉片")
}

func (e sauce) operation() {
	e.component.operation()
	fmt.Printf("加點醬汁")
}

func (e soup) operation() {
	e.component.operation()
	fmt.Printf("加點湯")
}

func (e chicken) operation() {
	e.component.operation()
	fmt.Printf("加個雞腿")
}

main.go

package main

import "fmt"

func main() {
	fmt.Println("小明想吃不帶湯的面")
	noodle := noodle{}
	egg := egg{decorator{noodle}}
	beef := beef{decorator{egg}}
	sauce := sauce{decorator{beef}}
	sauce.operation()
	/* -------------------------------------------------------------------------- */
	fmt.Println("\n小王想吃餛飩,還特別愛吃雞腿")
	huntun := huntun{}
	chicken1 := chicken{}
	chicken2 := chicken{}
	soup := soup{}
	chicken1.setComponent(huntun)
	chicken2.setComponent(chicken1)
	soup.setComponent(chicken2)
	soup.operation()
}

Console

小明想吃不帶湯的面
拿個碗裝了麵條加個炸蛋加牛肉片加點醬汁
小王想吃餛飩,還特別愛吃雞腿
拿個碗裝了餛飩加個雞腿加個雞腿加點湯

思考總結

什麼是裝飾模式

裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作為現有的類的一個包裝。

image-20220909234039141

裝飾模式:動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。

面對變化,如果採用生成子類的方式進行擴充,為支持每一種擴展的組合,會產生大量的子類。事實上,這些子類多半隻是為某個對象增加一些職責,此時通過裝飾模式,可以更加靈活、動態、透明的方式給單個對象添加職責,在不需要時也可以撤銷相應職責。這種模式創建了一個裝飾類,用來包裝原有的類,併在保持類方法簽名完整性的前提下,提供了額外的功能。

主要解決:一般的,我們為了擴展一個類經常使用繼承方式實現,由於繼承為類引入靜態特征,並且隨著擴展功能的增多,子類會很膨脹。

何時使用:在不想增加很多子類的情況下擴展類。

如何解決:將具體功能職責劃分,同時繼承裝飾者模式。

關鍵代碼: 1、Component 類充當抽象角色,不應該具體實現。 2、修飾類引用和繼承 Component 類,具體擴展類重寫父類方法。

應用實例: 1、孫悟空有 72 變,當他變成"廟宇"後,他的根本還是一隻猴子,但是他又有了廟宇的功能。 2、不論一幅畫有沒有畫框都可以掛在牆上,但是通常都是有畫框的,並且實際上是畫框被掛在牆上。在掛在牆上之前,畫可以被蒙上玻璃,裝到框子里;這時畫、玻璃和畫框形成了一個物體。

優點:裝飾類和被裝飾類可以獨立發展(類的核心職責和裝飾功能相互分開),不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。把類中的裝飾功能從類中搬移去除,這樣簡化原來的類。

缺點:多層裝飾比較複雜。

使用場景: 1、擴展一個類的功能。 2、動態增加功能,動態撤銷。

註意事項:可代替繼承。當系統需要新功能的時候,是向舊的類中添加新的代碼。這些新加的代碼通常裝飾了原有類的核心職責或主要行為。由於在主類或子類中加入了新的欄位、新的方法、新的邏輯,從而增加了主類的複雜度。而加入這些東西僅僅是為了滿足一些只在某種特定情況下才會執行的特殊行為。

靈活變通:如果只有一個ConcreteComponent類沒有抽象的Component類,那麼Decorator類可以是ConreteComponent的一個子類。如果只有一個ConcreteDecorator類,那麼就沒有必要單獨建立一個Decorator類,可以把DecoratorConcreateDecorator的責任合併成一個類。

與建造者模式不同點:建造者模式要求建造過程必須是穩定的,而裝飾模式可以是不固定過程,可以組合出無數種方案。

參考資料

  • 《Go語言核心編程》李文塔
  • 《Go語言高級編程》柴樹彬、曹春輝
  • 《大話設計模式》程傑
  • 單例模式 | 菜鳥教程

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近我在 CodePen 上看到了這樣一個有意思的動畫: 整個動畫效果是在一個標簽內,藉助了 SVG PATH 實現。其核心在於對漸變(Gradient)的究極利用。 完整的代碼你可以看看這裡 -- CodePen DEMO -- to the future 🍻 By Jane Ori] 源代碼還 ...
  • getDate(excelTimestamp, format) { // 使用JavaScript轉換unix時間戳需要將時間戳再*1000 const old = excelTimestamp - 1 const t = Math.round((old - Math.floor(old)) * 2 ...
  • 明天就是中秋節了,就想著用CSS畫一個月亮送給園友們吧。但是就畫一個月亮也太簡單了些,於是便加了一些星星點綴以及流星墜落的效果。這篇文章就用純CSS為大家實現一個“流星趕月”的效果。 實現效果 點擊運行查看 畫個月亮 首先我們先讓全屏背景變成黑色,然後實現一個大月亮🌕,並加點"漸變",“光暈"等效 ...
  • JS使用parseInt()和正則截取字元串中數字 點擊打開視頻講解更加詳細 parseInt() 函數 定義和用法 parseInt() 函數可解析一個字元串,並返回一個整數。 當參數 radix 的值為 0,或沒有設置該參數時,parseInt() 會根據 string 來判斷數字的基數 當忽略 ...
  • Hello,大家好,我是你們的新朋友小烤鴨,我們的設計模式系列中斷了幾天,今天我們繼續給它續上,那麼我們下麵繼續來說一種結構型設計模式,那就是大名鼎鼎的“橋接模式”。 定義:橋接模式的官方定義是將抽象部分與它的實現部分分離,使得他們都可以獨立變化,是一種結構型對象設計模式;上面這個定義比較拗口,且晦 ...
  • 橋接(Bridge)是用於把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬於結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。 ...
  • 一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現策略模式與簡單工廠。編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去。 ...
  • 一個菜鳥的設計模式之旅,使用 Golang 實現。本節實現代理模式。小明很喜歡同班的雪怡,但是過於靦腆的他不敢當面說。打算通過手機(代理)跟雪怡表白! ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...