Decorator pattern add new functionality to an existing implementation by adding a new layer. The responsibility/functionality is added to individual objects.
This article demonstrates Decorator pattern implementations in Go. Check the following examples.
Implementation
Here are the steps for Decorator pattern implementation-
Pre-existing implementations-
- Create an interface for the item/subject class.
- Create subject/item class and implement the interface.
Decorator implementation steps-
- Create new struct for the decorator.
- Create decorator struct and implement the decorator interface.
- Declare a field for storing a reference to the subject/item class object. Set the subject field while creating a new decorator object.
- In the interface implementation (or any other method) use/call methods from the subject, according to requirement.
Here is a simple decorator implementation-
// Simple Decorator pattern implementaion in GoLang
package main
import "fmt"
// Subject
type Subject interface {
OperationOne()
OperationTwo()
}
// Subject struct
type ConcreteSubject struct {
}
func NewConcreteSubject() (concreteSubject *ConcreteSubject) {
concreteSubject = &ConcreteSubject{}
return
}
func (concreteSubject *ConcreteSubject) OperationOne() {
fmt.Println("Performing Operation One(1) in Subject")
}
func (concreteSubject *ConcreteSubject) OperationTwo() {
fmt.Println("Performing Operation Two(2) in Subject")
}
// Decorator
type Decorator interface {
DecoratorOperationOne()
DecoratorOperationTwo()
}
// Concrete decorator
type ConcreteDecorator struct {
subject Subject
}
func NewConcreteDecorator(subject Subject) (concreteDecorator *ConcreteDecorator) {
concreteDecorator = &ConcreteDecorator{}
concreteDecorator.subject = subject
return
}
func (concreteDecorator *ConcreteDecorator) DecoratorOperationOne() {
// Some additional operations here
concreteDecorator.subject.OperationOne()
// Some additional operations here
}
func (concreteDecorator *ConcreteDecorator) DecoratorOperationTwo() {
// Some additional operations here
concreteDecorator.subject.OperationTwo()
// Some additional operations here
}
// Demo
func main() {
someDecorator := NewConcreteDecorator(NewConcreteSubject())
someDecorator.DecoratorOperationOne()
}
This code will generate the following output-
Performing Operation One(1) in Subject
Examples
Here are a few examples-
Example #1: Data Export
In this example we have a simple data exporter. We want to add new functionality to the data exporter.
Here we are implementing 3 decorators, that adds layer on top of the simple exporter.
DataExport Interface [Existing]
- Create file “date_export.go”.
- Define interface “DataExport”.
- Declare methods according to the requirement. Here we have – “ProcessData”.
// data_export.go
package main
type DataExport interface {
ProcessData()
}
SimpleExport Struct [Existing]
- Create file “simple_date_export.go”.
- Define interface “SimpleDataExport”.
- Create method “NewSimpleDataExport” for creating a new “SimpleDataExport” object.
- Implement interface “DataExport” for struct “SimpleDataExport”.
// simple_data_export.go
package main
import "fmt"
type SimpleDataExport struct {
}
func NewSimpleDataExport() (simpleDataExport *SimpleDataExport) {
simpleDataExport = &SimpleDataExport{}
return
}
func (simpleDataExport *SimpleDataExport) ProcessData() {
fmt.Println("SimpleDataExport: Processing Data")
}
Main Decorator Struct [Decorator]
- Create file “data_export_decorator.go”.
- Define struct “DataExportDecorator”.
- Define a field “dataExport” of type “DataExport”. This will be used to store a reference to “DataExport” object.
- Create method “NewDataExportDecorator” for creating a new “DataExportDecorator” object.
// data_export_decorator.go
package main
type DataExportDecorator struct {
dataExport DataExport
}
func NewDataExportDecorator(dataExport DataExport) (dataExportDecorator *DataExportDecorator) {
dataExportDecorator = &DataExportDecorator{}
dataExportDecorator.dataExport = dataExport
return
}
func (dataExportDecorator *DataExportDecorator) ProcessData() {
dataExportDecorator.dataExport.ProcessData()
}
CSV Decorator Struct [Decorator]
- Create file “csv_data_export_decorator.go”.
- Define struct “CsvDataExportDecorator”.
- Define/embed “DataExprtDecorator” into the struct.
- Create method “NewCsvDataExportDecorator” for creating a new “CsvDataExportDecorator” object.
- Declare method “processCsv” for the struct.
- Define method “ProcessData” for the struct. Use “processCsv” of this struct, and “ProcessData” from the decorator, in the method.
// csv_data_export_decorator.go
package main
import "fmt"
type CsvDataExportDecorator struct {
*DataExportDecorator
}
func NewCsvDataExportDecorator(dataExporter DataExport) (csvDataExportDecorator *CsvDataExportDecorator) {
csvDataExportDecorator = &CsvDataExportDecorator{}
csvDataExportDecorator.DataExportDecorator = NewDataExportDecorator(dataExporter)
return
}
func (csvDataExportDecorator *CsvDataExportDecorator) processCsv() {
fmt.Println("Processed data to CSV")
}
func (csvDataExportDecorator *CsvDataExportDecorator) ProcessData() {
csvDataExportDecorator.DataExportDecorator.ProcessData()
csvDataExportDecorator.processCsv()
}
Excel Decorator Struct [Decorator]
- Create file “excel_data_export_decorator.go”.
- Define struct “ExcelDataExportDecorator”.
- Define/embed “DataExportDecorator” into the struct.
- Create method “NewExcelDataExportDecorator” for creating a new “ExcelDataExportDecorator” object.
- Declare method “processExcel” for the struct.
- Define method “ProcessData” for the struct. Use “processExcel” of this struct, and “ProcessData” from the decorator, in the method.
// excel_data_export_decorator.go
package main
import "fmt"
type ExcelDataExportDecorator struct {
*DataExportDecorator
}
func NewExcelDataExportDecorator(dataExporter DataExport) (excelDataExportDecorator *ExcelDataExportDecorator) {
excelDataExportDecorator = &ExcelDataExportDecorator{}
excelDataExportDecorator.DataExportDecorator = NewDataExportDecorator(dataExporter)
return
}
func (excelDataExportDecorator *ExcelDataExportDecorator) ProcessData() {
excelDataExportDecorator.DataExportDecorator.ProcessData()
excelDataExportDecorator.processExcel()
}
func (excelDataExportDecorator *ExcelDataExportDecorator) processExcel() {
fmt.Println("Processed data to Excel")
}
JSON Decorator Struct [Decorator]
- Create file “json_data_export_decorator.go”.
- Define struct “JsonDataExportDecorator”.
- Define/embed “DataExportDecorator” into the struct.
- Create method “NewJsonDataExportDecorator” for creating a new “JsonDataExportDecorator” object.
- Declare method “processJson” for the struct.
- Define method “ProcessData” for the struct. Use “processJson” of this struct, and “ProcessData” from the decorator, in the method.
// json_data_export_decorator.go
package main
import "fmt"
type JsonDataExportDecorator struct {
*DataExportDecorator
}
func NewJsonDataExportDecorator(dataExporter DataExport) (jsonDataExportDecorator *JsonDataExportDecorator) {
jsonDataExportDecorator = &JsonDataExportDecorator{}
jsonDataExportDecorator.DataExportDecorator = NewDataExportDecorator(dataExporter)
return
}
func (jsonDataExportDecorator *JsonDataExportDecorator) ProcessData() {
jsonDataExportDecorator.DataExportDecorator.ProcessData()
jsonDataExportDecorator.processJson()
}
func (jsonDataExportDecorator *JsonDataExportDecorator) processJson() {
fmt.Println("Processed data to JSON")
}
Demo
Now we can pass an object of “SimpleDataExport” to decorator, while creating a new decorator object (Csv/Json/Excel Decorator).
// main.go
package main
func main() {
csvDataExport := NewCsvDataExportDecorator(NewSimpleDataExport())
csvDataExport.ProcessData()
excelDataExport := NewExcelDataExportDecorator(NewSimpleDataExport())
excelDataExport.ProcessData()
jsonDataExport := NewJsonDataExportDecorator(NewSimpleDataExport())
jsonDataExport.ProcessData()
}
Output
The output of the demo above will be like below.
SimpleDataExport: Processing Data
Processed data to CSV
SimpleDataExport: Processing Data
Processed data to Excel
SimpleDataExport: Processing Data
Processed data to JSON
Example #2: UI Elements
In this example we have some UI elements like Buttons, Input files, Tables, etc. We want to decorate these elements with border and/or background and/or margin.
We can do this without any change in the UI element implementation, by using Decorator pattern.
UI Element Interface
- Create file “ui_element.go”.
- Define interface “UIElement”.
- Declare method “Draw” for the interface.
// ui_element.go
package main
type UIElement interface {
Draw()
}
Button UI Element Struct
- Create file “button.go”.
- Define struct “Button”.
- Define method “NewButton” which is responsible for creating a new “Button” object.
- Define method “Draw” for the struct, as part of “UIElement” interface implementation.
// button.go
package main
import "fmt"
type Button struct {
}
func NewButton() (button *Button) {
button = &Button{}
return
}
func (button *Button) Draw() {
fmt.Println("Drawing Button")
}
Input Box UI Element Struct
- Create file “input_box.go”.
- Define the struct “InputBox”.
- Define method “NewInputBox” which is responsible for creating a new “InputBox” object.
- Implement interface “UIElement” for the struct. Define the method “Draw” for the struct, as part of the implementation.
// input_box.go
package main
import "fmt"
type InputBox struct {
}
func NewInputBox() (inputBox *InputBox) {
inputBox = &InputBox{}
return
}
func (inputBox *InputBox) Draw() {
fmt.Println("Drawing Input Box")
}
Table UI Element Struct
- Create file “table.go”.
- Define the struct “Table”.
- Define method “NewTable” which is responsible for creating a new “Table” object.
- Implement interface “UIElement” for the struct. Define the method “Draw” for the struct, as part of the “UIElement” interface implementation.
// table.go
package main
import "fmt"
type Table struct {
}
func NewTable() (table *Table) {
table = &Table{}
return
}
func (table *Table) Draw() {
fmt.Println("Drawing Table")
}
UI Decorator [Decorator]
- Create file “ui_decorator.go”.
- Define the struct “UIDecorator”.
- Define field “uiElement” of type “UIElement” to store a reference of “UIElement”.
- Define method “NewUIDecorator” for creating a new “UIDecorator” object.
- Define method “Draw” and use/call the “Draw” method from “uiElement” in the method implementation.
// ui_decorator.go
package main
type UIDecorator struct {
uiElement UIElement
}
func NewUIDecorator(uiElement UIElement) (uiDecorator *UIDecorator) {
uiDecorator = &UIDecorator{}
uiDecorator.uiElement = uiElement
return
}
func (uiDecorator *UIDecorator) Draw() {
uiDecorator.uiElement.Draw()
}
Border Decorator Struct [Decorator]
- Create file “border_decorator.go”.
- Define the struct “BorderDecorator”.
- Embed “UIDecorator” into this struct.
- Define method “NewBorderDecorator” for creating a new “BorderDecorator” object.
- Define method “Draw” and use/call the “Draw” method from “uiElement”. Add the implementation for adding border, in the method.
// border_decorator.go
package main
import "fmt"
type BorderDecorator struct {
*UIDecorator
}
func NewBorderDecorator(uiElement UIElement) (borderDecorator *BorderDecorator) {
borderDecorator = &BorderDecorator{}
borderDecorator.UIDecorator = NewUIDecorator(uiElement)
return
}
func (borderDecorator *BorderDecorator) Draw() {
// Can perform any additional task anywhere in the method
borderDecorator.UIDecorator.Draw()
// Write code to add border to the element
fmt.Println("Adding Border to the element")
}
Background Decorator Struct [Decorator]
- Create file “background_decorator.go”.
- Define the struct “BackgroundDecorator”.
- Embed “UIDecorator” into this struct.
- Define method “NewBackgroundDecorator” for creating a new “BackgroundDecorator” object.
- Implement method “Draw” and call the “Draw” method from “uiElement” for drawing the element. Add functionality for adding background, in the method.
// background_decorator.go
package main
import "fmt"
type BackgroundDecorator struct {
*UIDecorator
}
func NewBackgroundDecorator(uiElement UIElement) (backgroundDecorator *BackgroundDecorator) {
backgroundDecorator = &BackgroundDecorator{}
backgroundDecorator.UIDecorator = NewUIDecorator(uiElement)
return
}
func (backgroundDecorator *BackgroundDecorator) Draw() {
// Can perform any additional task anywhere in the method
backgroundDecorator.UIDecorator.Draw()
// Write code to add background to the element
fmt.Println("Adding Background to the element")
}
Margin Decorator Struct [Decorator]
- Create file “margin_decorator.go”.
- Define the struct “MarginDecorator”.
- Embed “UIDecorator” into this struct.
- Define method “NewMarginDecorator”, which creates a new “MarginDecorator” object.
- Define method “Draw” and use/call the “Draw” method from “uiElement”. Add margin in the method.
// margin_decorator.go
package main
import "fmt"
type MarginDecorator struct {
*UIDecorator
}
func NewMarginDecorator(uiElement UIElement) (marginDecorator *MarginDecorator) {
marginDecorator = &MarginDecorator{}
marginDecorator.UIDecorator = NewUIDecorator(uiElement)
return
}
func (marginDecorator *MarginDecorator) Draw() {
// Can perform any additional task anywhere in the method
marginDecorator.UIDecorator.Draw()
// Write code to add margin to the element
fmt.Println("Adding margin to the element")
}
Demo
The following code demonstrates how to use multiple decorators on a single UI Element.
We can pass the UI object to the decorator, to add additional functionality. We can also chain the call and use multiple decorators for an element.
// main.go
package main
func main() {
tableWithBorder := NewBorderDecorator(NewTable())
tableWithBorder.Draw()
inputWithBorderAndBackground := NewBackgroundDecorator(NewBorderDecorator(NewInputBox()))
inputWithBorderAndBackground.Draw()
buttonWithAllDecorator := NewMarginDecorator(NewBackgroundDecorator(NewBorderDecorator(NewButton())))
buttonWithAllDecorator.Draw()
}
Output
The following output will be generated-
Drawing Table
Adding Border to the element
Drawing Input Box
Adding Border to the element
Adding Background to the element
Drawing Button
Adding Border to the element
Adding Background to the element
Adding margin to the element
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check Decorator pattern implementation in other programming languages.