Design Patterns in Golang: Decorator

综合编程 2016-04-18

Sun, Apr 17, 2016

programming languages design patterns


The Decorator pattern adds new functionality to an existing object without altering its structure. It is a structural pattern as this pattern acts as a wrapper to existing class.

The instanciate a decorator struct which decorates (wraps) the original object and provides additional functionality keeping its methods signature intact.


  • Attach additional responsibilities to an object dynamically.
  • Decorators provide a flexible alternative to inheritance for extending functionality.
  • Wrapping a present, putting it in a box, and wrapping the box.

Design Pattern Diagram

The Decorator Pattern has the following entities:

  • Component defines the interface for objects that can have responsibilities added to them dynamically.
  • ConcreteComponent defines an object to which additional responsibilities can be attached.
  • Decorator maintains a reference to a Component object and defines an interface that conforms to Component’s interface.
  • ConcreteDecorator adds responsibilities to the component.


We are explore the use of decorator pattern via following example in which we will extend an existing object that fetches a data from web service. We will decorate it by adding circuit breaker capabilities without changing the struct interface.

Lets have a Fetcher interface that defines a contract for fetching some data from different sources.

// Args of fetching function
type Args map[string]string

// Data returned by fetch
type Data map[string]string

// Fetcher fetches a data from remote endpoint
type Fetcher interface {
	// Fetch fetches the data
	Fetch(args Args) (Data, error)

A concrete implementation of the Fetcher interface is the Repository struct which provides some dummy data if the provided arguments are not empty, otherwise returns an error. The Repository struct is a concrete component in the context of The Decorator Pattern.

// Repository of data
type Repository struct{}

// Fetch fetches data
func (r *Repository) Fetch(args Args) (Data, error) {
	if len(args) == 0 {
		return Data{}, fmt.Errorf("No arguments are provided")

	data := Data{
		"user":     "root",
		"password": "swordfish",
	fmt.Printf("Repository fetched data successfully: %vn", data)
	return data, nil

A Retrier struct is the decorator that adds circuit breaker capabilities to any component that implements the Fetcher interface. The Retrier has a few properties that allow that. The RetryCount property defines the number of times that the retrier should try to fetch if there is an error. The WaitInterval property defines the interval between every retry. The Fetcher property is points to the object that is decorated. The Retrier calls the Fetch function of the decorated object until it succeeds or exceed the retry policy.

// Retrier retries multiple times
type Retrier struct {
	RetryCount   int
	WaitInterval time.Duration
	Fetcher      Fetcher

// Fetch fetches data
func (r *Retrier) Fetch(args Args) (Data, error) {
	for retry := 1; retry <= r.retrycount;="" retry++="" {="" fmt.printf("retrier="" retries="" to="" fetch="" for="" %dn",="" retry)="" if="" data,="" err="" :="r.Fetcher.Fetch(args);" nil="" fetched="" return="" }="" else="" retry="=" r.retrycount="" failed="" %d="" timesn",="" data{},="" is="" waiting="" after="" error="" %vn",="" r.waitinterval)="" time.sleep(r.waitinterval)="" 

Then we can add the new retry capabilities by wrapping the Repository instance with the Retrier :

repository := &cbreaker.Repository{}
retrier := &cbreaker.Retrier{
	RetryCount:   5,
	WaitInterval: time.Second,
	Fetcher:      repository,

data, err := repository.Fetch(cbreaker.Args{"id": "1"})
fmt.Printf("#1 repository.Fetch: %vn", data)

data, err = retrier.Fetch(cbreaker.Args{})
fmt.Printf("#2 retrier.Fetch error: %vn", err)

data, err = retrier.Fetch(cbreaker.Args{"id": "1"})
fmt.Printf("#3 retrier.Fetch: %vn", data)


The Decorator Pattern is more convenient for adding functionalities to objects instead of entire structs at runtime. With decoration it is also possible to remove the added functionalities dynamically.

责编内容by:Software adventures and thoughts (源链)。感谢您的支持!


深入浅出golang errgroup.Group 原生支持并发是Go语言最强大的特性,比如channels和goroutines。但是对于Go语言初学者尝试并接受并发概念并不是很轻松。 Go团队发布的第...
Golang教程:(十)switch 语句 原文: 这是本Golang系列教程的第十篇。 switch 是一个条...
Using the Service Object Pattern in Go This article was original posted on my website at NOTE: ...
Teleport 2.0 一个 Golang TCP Socket 的全新框架... Teleport2.0 实现了一个全新的Golang TCP Socket框架,它通用、高效、灵活!可被用于Peer-Peer对等通信、RPC、长连接网关、微服...
CodeTengu Weekly 碼天狗週刊 – Issue 74 只有兩種產業的人會把... Hello World CodeTengu Weekly 碼天狗週刊 CodeTengu Weekl...