Go Design Patterns - The Decorator Pattern - Part One

Tensor Programming · Beginner ·🛠️ AI Tools & Apps ·7y ago

Key Takeaways

The Decorator Pattern in Go is used to build composite functions and create middleware, demonstrated through logging and caching functionalities. The tutorial covers defining decorator functions, chaining multiple decorators, and applying them to functions using Go and its libraries.

Full Transcript

hi guys my name is tensor welcome to the go design pattern tutorial series in today's video we're going to talk about the decorator pattern some of you may have heard this term decorator before and you may have even used it in other programming languages such as Python Java and typescript and of course you can use decorators in go now decorators essentially allow you to wrap existing functionality and then inject your own custom functionality on top of that functionality and we're able to get away with this because functions in go are first-class which means that we can pass functions to variables we can pass functions to other functions and we can of course return functions from functions to demonstrate the decorator pattern I've created a function here called pi this function just takes in a value and then it uses that value to approximate the number of pi so we create a channel of float64 here and then we iterate from 0 to n which is the number that we're passing in here and the higher this number is the better our approximation will be for each iteration will spawn a new go routine on this anonymous function here and the anonymous function will take in our channel and it will take in our K value as a float64 and it will run this calculation and then pass it into the channel now down here we have our result variable which will start at 0 0 then we'll iterate again from 0 all the way up to N and we'll take all of the values from our channel and then just increment our result with them after we're done cleaning out all of the values from the channel we'll go ahead and return this result from this function so I'm going to call fmt println pi with 1000 inside of it and then I'm going to call it also with 50000 inside of it and we'll see the approximations of pi so here you can see that our top approximate gave us three point one four to five and then our bottom approximation is a bit more accurate giving us three point one four one now let's say we want to take this function and wrap it inside of a lager in such a way that the lager will tell us how long it took to execute the function well to accomplish this we can go ahead and use what's called a decorator function now to start out we can create a function called wrap lager and this will take in a function that takes in an integer and a float 64 which is the type of our PI function down here and then it will return that same type of function and actually to make this easier to read we can alias this function type as pi func and then just replace these declarations so this wrap lager function will take in a PI funk which is just a function that takes in an int and returns a float 64 and then it will return a PI func type while we're at it let's also add a lager variable here which will take in a reference to log lager which will allow us to specify how we want our lager to actually function so then inside of this function will return an anonymous function that takes in an integer and returns a float 64 and in the body of this function we can then create our lager and also execute the PI function and notice that we can just take the function that we're passing in to wrap logger and just call it on N and this in essence would just give us our PI function back except it would also have the logger scoped to it as well the body of this function is going to get a tad more complicated when we add the actual logger because we want this logger to execute as soon as our function starts and also when the function ends and we can accomplish this by using the defer keyword and then creating yet another as function our inner anonymous function is where we're getting the timestamp for when the function execution starts this takes in a value of time time and the value is being passed in here so this is being executed immediately with time dot now as the argument but because it's being deferred this will not complete execution until the end of this outer function body because it's being called down here inside of it we're calling lager print F we're putting in that it took a time sense value which is just a time comparison between time now and then the current time so this will give us the amount of time that it took to execute the function then we can see the value n that we passed in and then we can also see the result and this result value is why we have this second anonymous function being wrapped around the defer call so you can see here that the second anonymous function takes in a value and int and then it also returns a float 64 but because we want to access that return value we named it as a result and what that does is it allows us to gain access to the return value of our function in this closure and then we can use it in our logger here in this FN function that we're creating we'll call fun which is the argument function that we're passing in here which will be our PI function so this will start the execution of our PI function and then outside of the FN declaration we call f n with n once we define this decorator function we can now go down to our main function and just call it and we call it on our PI function and then we pass in the augur that we want to add and this is just a call to log new with OS standard out because we want it to print to our console and then we want it to start with the word test this will give us back a function which we can invoke with the value that we want to pass into our PI function so here I'm passing in 100,000 and then this will go through our lager and our PI function and we'll have all of this behavior attached to this new composite function we can see when we go ahead and run this that we get that it took 660 milliseconds with an 100000 and the result was three point one four one six zero two etc this decorator pattern is especially useful when you want to create middleware for like a server or a client or a sort of like restful api and in fact the HTTP library ingo uses the decorator pattern quite a lot now let's say we want to add a second decorator currently we have the logger decorator and this gives us a new function and we can chain it together with another decorator to add more functionality to our PI function if we wanted to say call PI 100,000 and then call it again we would have to go through both executions and so it makes sense to create a cache decorator function so to create the cache function we'll create a function called wrap cache this will take in a PI func type and then our cache which will be a sync map type and this is just a map primitive that is thread safe and the reason why we want to use this primitive is because our PI function is using a lot of go routines and then like our wrap logger function our wrap cache function will also return a PI func type inside of our cast function will again return a function that takes in an integer and a float 64 and will again create another anonymous function that also takes in an integer in a float at 64 will first define the key that we want to add to our map so this will be the key that were and we can use fmt s printf pass in the value end and put it into a string of N equals with the N number inside of it then we can call to our cash and we can call load on our key to check to see if that key is already populated in our sync map and this will return the value and then a boolean and if this boolean comes back as true then we want to return this value as a float64 otherwise we want to continue call our function our PI func on n get the result and then store that result inside of our cache map with the key and then return the result from this function and then of course because we need to return a float 64 from this anonymous function will call this FN function on n every turn it and now we can come down to our main function call wrap cash on PI and then pass in a reference to the sync map that we want to use as our cash and then take that function f and call wrap logger on it which will give us a function G which we can then call and this function G will have both our cash and or logger behavior on top of our PI function first when we run it with 100,000 the time it takes is 706 milliseconds then we run it on say 20,000 and it takes 66 milliseconds and then again when we run it on 100,000 it takes 0 seconds because it gets the value from the cache one thing that you want to keep in mind with this current implementation is that you want to wrap the cache on top of PI first and then wrap the logger on top of both of those functions if you don't do that then the cache function if it returns a value will not actually put that value into the logger and to kind of explain this let's take a look at the actual structure of our functions so we have PI in the middle then we have logger and then we have cat so when we call rap cash the fun function here is a combination of the lager and the pie function when we come to this if statement we return the value with type float64 but we never actually execute the fun function which means that we don't actually execute the lager either and so this return value just gets returned from our function but it doesn't actually go into our lager if however we have the lager on the outside then it doesn't really matter if we get a value returned in that way or if we get a value return by invoking the pie function one of the cooler features of the decorator pattern is that you can reuse all of the decorators many times over many different objects so long as those objects follow the same pattern so this divided function which I've created here which is pretty simple it just takes a number which is an integer and then divides it by two and then cast that value as a float 64 follows the same type declaration as our PI function and so this divide function can also be wrapped inside of the cash function and our logger function so instead of passing in PI we pass in divide and then we wrap the value here F in the wrap logger which then gives us this function G and we can of course call it with different values and now you can see that we get back the calculations of Pi and then we also get back in the division calculations so so long as the function follows this type declaration we can attach all of these decorators to it and this is a very flexible idea because you can really create a ton of different functions which can follow these types of patterns and also because of the way that we're composing all of these functions together the main functions do not actually care about how the decorators are structured and vice versa so our rap cache decorator just deals with the cash and our rap logger decorator just deals with the logger and only they know how the implementation of the cache are the logger actually is whereas the divide and the pie functions don't really care what that implementation is and so even if I radically changed rap cache and rap logger so long as the general pattern is held they should still work on these functions alright guys well I hope you enjoyed this tutorial if you did feel free to like and subscribe if you have any questions or comments feel free to leave them in the box below and if you dislike this video then by all means download it as much as you like if you want to catch more videos on go design patterns then I recommend you click on the notification bell have a good night

Original Description

#tensorprogramming #golang #designpatterns In this tutorial, we take a look at the decorator pattern in Go. We use it to build composite functions and create middleware that can be plugged into any like function or type. Source Code: https://github.com/tensor-programming/pattern-tutorial/blob/master/decorator/main.go Request Form: https://goo.gl/forms/rFjHcZMRJ3bYPEC03 Cloudways Web App Hosting: https://www.cloudways.com/en/?id=507366 Support the Channel and Join Patreon: Patreon: https://www.patreon.com/tensor_programming Dontate: ETH: 0x03247265dd5242605bD2FA3c40fb3b70d9e3D685 Cardano: addr1q9auccwrr9ws8qdyv45f4qwsx76pfmld4zapks89sakq94ay0xmle73y0r8ruwd0zslls4eglf98lghru7ywv56cedysk7ftjt Check out our Twitter: https://twitter.com/TensorProgram Check out our Facebook: https://www.facebook.com/Tensor-Programming-1197847143611799/ Check out our Steemit: https://steemit.com/@tensor
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from Tensor Programming · Tensor Programming · 0 of 60

← Previous Next →
1 NodeJs, Text editors and IDEs
NodeJs, Text editors and IDEs
Tensor Programming
2 Vanilla JS todo App
Vanilla JS todo App
Tensor Programming
3 Elm Tutorial part 1
Elm Tutorial part 1
Tensor Programming
4 Elm Lang Tutorial, Part 2
Elm Lang Tutorial, Part 2
Tensor Programming
5 Elm Tutorial Part 3
Elm Tutorial Part 3
Tensor Programming
6 Elm Tutorial Part 4 -- Analog Clock App
Elm Tutorial Part 4 -- Analog Clock App
Tensor Programming
7 Elm Tutorial part 5 -- Snake Game
Elm Tutorial part 5 -- Snake Game
Tensor Programming
8 Elm Tutorial part 6 -- Calculator
Elm Tutorial part 6 -- Calculator
Tensor Programming
9 Go Tutorial part 1 -- Hello World and Static File Server
Go Tutorial part 1 -- Hello World and Static File Server
Tensor Programming
10 Go Tutorial part 2 -- Web Crawler
Go Tutorial part 2 -- Web Crawler
Tensor Programming
11 Go Tutorial Part 3 (Web App part 1)
Go Tutorial Part 3 (Web App part 1)
Tensor Programming
12 Go tutorial Part 4 (Web tutorial part 2) - Using templates
Go tutorial Part 4 (Web tutorial part 2) - Using templates
Tensor Programming
13 Go tutorial part 5 (web app part 3)
Go tutorial part 5 (web app part 3)
Tensor Programming
14 Go tutorial part 6 (webapp part 4)
Go tutorial part 6 (webapp part 4)
Tensor Programming
15 Go tutorial part 7 (web app part 5)
Go tutorial part 7 (web app part 5)
Tensor Programming
16 Go tutorial part 8 (Web app part 6)
Go tutorial part 8 (Web app part 6)
Tensor Programming
17 Go tutorial Part 9 (web tutorial part 7)
Go tutorial Part 9 (web tutorial part 7)
Tensor Programming
18 Go tutorial Part 10 (web app part 8)
Go tutorial Part 10 (web app part 8)
Tensor Programming
19 Go tutorial Part 11 (Web app Part 9)
Go tutorial Part 11 (Web app Part 9)
Tensor Programming
20 Go Tutorial Part 12 (Web app Part 10)
Go Tutorial Part 12 (Web app Part 10)
Tensor Programming
21 Go Tutorial Part 13 (Web app Part 11)
Go Tutorial Part 13 (Web app Part 11)
Tensor Programming
22 Looking at Elm 0.18
Looking at Elm 0.18
Tensor Programming
23 Go tutorial Part 14 (Web tutorial part 12)
Go tutorial Part 14 (Web tutorial part 12)
Tensor Programming
24 Go tutorial Part 15 (Web tutorial part 13)
Go tutorial Part 15 (Web tutorial part 13)
Tensor Programming
25 Go tutorial part 16 (web app part 14)
Go tutorial part 16 (web app part 14)
Tensor Programming
26 Elm Tutorial Part 7 (SPA part 1)
Elm Tutorial Part 7 (SPA part 1)
Tensor Programming
27 Elm Tutorial Part 8 (SPA Part 2)
Elm Tutorial Part 8 (SPA Part 2)
Tensor Programming
28 Electron Elm Tutorial
Electron Elm Tutorial
Tensor Programming
29 Go tutorial part 17 (web app part 15)
Go tutorial part 17 (web app part 15)
Tensor Programming
30 Up and Coming Programming Languages and Technologies for 2017
Up and Coming Programming Languages and Technologies for 2017
Tensor Programming
31 elixir tutorial part 1
elixir tutorial part 1
Tensor Programming
32 elixir tutorial part 2
elixir tutorial part 2
Tensor Programming
33 Elixir tutorial Part 3 (GenServer and Supervisor)
Elixir tutorial Part 3 (GenServer and Supervisor)
Tensor Programming
34 Elixir Tutorial Part 4 (GenStage)
Elixir Tutorial Part 4 (GenStage)
Tensor Programming
35 Elixir Tutorial Part 5 (Plug and Cowboy)
Elixir Tutorial Part 5 (Plug and Cowboy)
Tensor Programming
36 Phoenix Framework Tutorial Part 1 (elixir part 6)
Phoenix Framework Tutorial Part 1 (elixir part 6)
Tensor Programming
37 Phoenix Framework Tutorial Part 2  (elixir part 7)
Phoenix Framework Tutorial Part 2 (elixir part 7)
Tensor Programming
38 Phoenix Framework Tutorial Part 3 (elixir part 8)
Phoenix Framework Tutorial Part 3 (elixir part 8)
Tensor Programming
39 A Intro to Clojure and Clojure Syntax
A Intro to Clojure and Clojure Syntax
Tensor Programming
40 An Update about the channel
An Update about the channel
Tensor Programming
41 Intro to Rustlang (Setup and Primitives)
Intro to Rustlang (Setup and Primitives)
Tensor Programming
42 Intro to Rustlang (Strings, Tuples, Arrays, Slices and Pretty Printing)
Intro to Rustlang (Strings, Tuples, Arrays, Slices and Pretty Printing)
Tensor Programming
43 Intro to Rustlang (Ownership and Borrowing)
Intro to Rustlang (Ownership and Borrowing)
Tensor Programming
44 Intro to Rustlang (Structs, Methods, Functions, Related Functions and the Display/Debug Traits)
Intro to Rustlang (Structs, Methods, Functions, Related Functions and the Display/Debug Traits)
Tensor Programming
45 Intro to Rustlang (Control Flow, Conditionals and Pattern Matching)
Intro to Rustlang (Control Flow, Conditionals and Pattern Matching)
Tensor Programming
46 Intro to RustLang (Enums and Options)
Intro to RustLang (Enums and Options)
Tensor Programming
47 Intro to Rustlang (Vectors, HashMaps, Casting, If-Let, While-Let, and the Result Enum)
Intro to Rustlang (Vectors, HashMaps, Casting, If-Let, While-Let, and the Result Enum)
Tensor Programming
48 Rustlang Project: Snake Game
Rustlang Project: Snake Game
Tensor Programming
49 Intro to Rustlang (Traits and Generic Types)
Intro to Rustlang (Traits and Generic Types)
Tensor Programming
50 Intro to Rust-lang (Closures, the Box Pointer and Iterators)
Intro to Rust-lang (Closures, the Box Pointer and Iterators)
Tensor Programming
51 Intro to Rust-lang (Modules and Lifetimes)
Intro to Rust-lang (Modules and Lifetimes)
Tensor Programming
52 Intro to Rust-lang (Macros and Metaprogramming)
Intro to Rust-lang (Macros and Metaprogramming)
Tensor Programming
53 Intro to Rust-lang (Error Handling)
Intro to Rust-lang (Error Handling)
Tensor Programming
54 Intro to Rust-lang (Concurrency, Threads, Channels, Mutex and Arc)
Intro to Rust-lang (Concurrency, Threads, Channels, Mutex and Arc)
Tensor Programming
55 Intro to Rust-lang (Tests, Attributes, Configuration and Conditional compilation)
Intro to Rust-lang (Tests, Attributes, Configuration and Conditional compilation)
Tensor Programming
56 Rustlang Project: Port Sniffer CLI
Rustlang Project: Port Sniffer CLI
Tensor Programming
57 Rustlang Project: Chat Application
Rustlang Project: Chat Application
Tensor Programming
58 Rustlang Project: CLI Toy Blockchain
Rustlang Project: CLI Toy Blockchain
Tensor Programming
59 Intro to Rust-lang (Setting up a Development Environment)
Intro to Rust-lang (Setting up a Development Environment)
Tensor Programming
60 Intro to Rust-lang (Building a Web API with Iron)
Intro to Rust-lang (Building a Web API with Iron)
Tensor Programming

This tutorial teaches the Decorator Pattern in Go, focusing on building composite functions and creating middleware for logging and caching. It demonstrates how to define and chain decorator functions, apply them to functions, and utilize Go's libraries for effective implementation. The Decorator Pattern is key to compatibility and radical changes in functions without affecting other parts of the code.

Key Takeaways
  1. Define a logger decorator function to add logging functionality to a function
  2. Create a cache decorator function to add caching functionality to a function
  3. Chain multiple decorators together to add multiple functionalities to a function
  4. Use the Decorator Pattern to add logging and caching functionality to a function
  5. Invoke the decorated function with a value to test its behavior
  6. Wrap the cache around the function using a decorator
  7. Wrap the logger around the function using another decorator
  8. Call the wrapped function with different values
💡 The Decorator Pattern allows for radical changes in functions without affecting other parts of the code, making it a powerful tool for building composite functions and creating middleware.

Related Reads

📰
InsForge vs Firebase: AI-Native Postgres Alternative
Learn how InsForge and Firebase compare as alternatives for backend infrastructure, and why InsForge's AI-native Postgres approach matters
Dev.to AI
📰
InsForge vs Supabase: AI-Native Backend Alternative
Compare InsForge and Supabase as AI-native backend alternatives for serverless applications
Dev.to AI
📰
A model rated a visibly wrong red 0.97 on-brand. That number changed what I built.
Learn how a single model rating can impact design decisions and why it's essential to critically evaluate AI outputs in branding and design
Medium · AI
📰
Best AI Appointment Setter Software in 2026: 11 Tools Compared
Discover the top 11 AI appointment setter software tools in 2026 and learn how to choose the best one for your business needs
Medium · AI
Up next
15 NotebookLM Hacks That Change How You Work
SCALER
Watch →