> ## Content Index
> Fetch the complete content index at: https://huizhou92.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Go program pattern 03: Inversion of Control
- URL: https://huizhou92.com/go-program-pattern-03-inversion-of-control-2/
- Published: 2024-01-24T12:05:08.000Z
- Updated: 2026-09-08T02:39:32.000Z
- Description: Go program pattern 03: Inversion of Control. Hello everyone, I’m hxzhouh In the previous article, I briefly introduced the composite pattern in Go, which w。
- Author: huizhou92
- Tags: #Migrated-1788833207488, #Import 2026-09-08 02:07

Hello everyone, I’m hxzhouh

In the previous article, I briefly introduced the composite pattern in Go, which was explained in a simple manner. We understood that Go can achieve polymorphism in object-oriented programming through composition.

> Go programming pattern series articles

[Go program pattern 01: Functional Options PatternHello everyone, I am hxzhouh.![](https://huizhou92.com/content/images/2026/09/1-dk1_ric7jta_fjv6bcmxjg-3.png)](https://levelup.gitconnected.com/go-program-pattern-01-functional-options-pattern-cbcee4979f42?ref=huizhou92.com)

[Go program pattern 02： Implementing Class Inheritance and Method Overriding through CompositionHello everyone, I’m hxzhouh.![](https://huizhou92.com/content/images/2026/09/0-8yrd9qawsrvmcy61-4.png)](https://levelup.gitconnected.com/go-program-pattern-02-implementing-class-inheritance-and-method-overriding-through-composition-aa8b03f0f4d5?ref=huizhou92.com)

[Go program pattern 03: Inversion of ControlHello everyone, I’m hxzhouh![](https://huizhou92.com/content/images/2026/09/0-xu7a_pbif-an-k_s-4.png)](https://medium.hxzhouh.com/go-program-pattern-03-inversion-of-control-ee15f0544d14?ref=huizhou92.com)

[Go Program pattern 04: Map-ReduceHello everyone, I’m hxzhouh.![](https://huizhou92.com/content/images/2026/09/0-nt7b2vhakgf6qmnt-3.png)](https://levelup.gitconnected.com/go-program-pattern-04-map-reduce-ff3bff69e4e8?ref=huizhou92.com)

[Go program pattern 05: decorationsHow to simplify your code with a higher-order function![](https://cdn-images-1.medium.com/fit/c/160/160/0*PZOIhD2jWDkR8gde)](https://levelup.gitconnected.com/go-program-pattern-05-decorations-d108b493145d?ref=huizhou92.com)

In this article, let’s learn about Inversion of Control (IoC). [Inversion of Control](https://en.wikipedia.org/wiki/Inversion%5Fof%5Fcontrol?ref=huizhou92.com) is a software design method that involves separating control logic from business logic. Instead of writing control logic within the business logic, which creates a dependency of control logic on business logic, IoC reverses this relationship and makes the business logic dependent on the control logic.

### Inversion of Control

Let’s consider an example where we want to implement a functionality to record the existence of numbers. We can easily implement the following code:

```go
type IntSet struct {   
     data map[int]struct{}   
 }   
    
 func NewIntSet() IntSet {   
     return IntSet{make(map[int]struct{})}   
 }   
 func (set *IntSet) Add(x int) {   
     set.data[x] = struct{}{}   
 }   
 func (set *IntSet) Delete(x int) {   
     delete(set.data, x)   
 }   
 func (set *IntSet) Contains(x int) bool {   
     _, ok := set.data[x]   
     return ok   
 }
```

The above code uses a map to store numbers and provides functionalities for adding, deleting, and checking the existence of numbers. Everything seems perfect.

Now, suppose we want to add an undo feature to this functionality. How can we do that? With a little thought, we can write clear code by wrapping `IntSet` into `UndoableIntSet`. Here's the code:

```go
type UndoableIntSet struct { // Poor style 
     IntSet    // Embedding (delegation) 
     functions []func() 
 } 
   
 func NewUndoableIntSet() UndoableIntSet { 
     return UndoableIntSet{NewIntSet(), nil} 
 } 
   
 ​ 
 func (set *UndoableIntSet) Add(x int) { // Override 
     if !set.Contains(x) { 
         set.data[x] = true 
         set.functions = append(set.functions, func() { set.Delete(x) }) 
     } else { 
         set.functions = append(set.functions, nil) 
     } 
 } 
 ​ 
 ​ 
 func (set *UndoableIntSet) Delete(x int) { // Override 
     if set.Contains(x) { 
         delete(set.data, x) 
         set.functions = append(set.functions, func() { set.Add(x) }) 
     } else { 
         set.functions = append(set.functions, nil) 
     } 
 } 
 ​ 
 func (set *UndoableIntSet) Undo() error { 
     if len(set.functions) == 0 { 
         return errors.New("No functions to undo") 
     } 
     // invert the order of calls 
     index := len(set.functions) - 1 
     if function := set.functions[index]; function != nil { 
         function() 
     } 
     set.functions = set.functions[:index] 
     return nil 
 }
```

This approach is a good choice for extending existing code with new functionalities. It allows for a balance between reusing the existing code and adding new features. However, the main issue with this approach is that the Undo operation is actually a form of control logic, not business logic. The Undo feature cannot be reused because it contains a lot of business logic related to `IntSet`.

### Dependency Inversion

Let’s explore another implementation approach where we extract the undo feature and make `IntSet` depend on it:

```go
type Undo []func() 
 func (undo *Undo) Add(u func()) {   
     *undo = append(*undo, u)   
 }   
 func (undo *Undo) Undo() {   
     if len(*undo) == 0 {   
        return   
     }   
     index := len(*undo) - 1   
     (*undo)[index]()   
     *undo = (*undo)[:index]   
 }
```

Next, we embed `Undo` in `IntSet`:

```go
type IntSet struct {   
     data map[int]struct{}   
     undo Undo   
 }   
    
 func NewIntSet() IntSet {   
     return IntSet{make(map[int]struct{}), make(Undo, 0)}   
 }   
 func (set *IntSet) Undo() {   
     set.undo.Undo()   
 }   
 func (set *IntSet) Add(x int) {   
     if set.Contains(x) {   
        return   
     } else {   
        set.undo.Add(func() {   
           set.Delete(x)   
        })   
        set.data[x] = struct{}{}   
     }   
 }   
 func (set *IntSet) Delete(x int) {   
     if !set.Contains(x) {   
        return   
     } else {   
        set.undo.Add(func() {   
           set.Add(x)   
        })   
        delete(set.data, x)   
     }   
 }   
 func (set *IntSet) Contains(x int) bool {   
     _, ok := set.data[x]   
     return ok   
 }
```

In our application, we can use it as follows:

```css
func main() {   
     set := NewIntSet()   
     set.Add(1)   
     set.Add(2)   
     fmt.Println(set.Contains(2))   
     set.Undo()   
     fmt.Println(set.Contains(2))   
     set.Delete(1)   
     fmt.Println(set.Contains(1))   
     set.Undo()   
     fmt.Println(set.Contains(1))   
 }
```

Output:

```bash
/Users/hxzhouh/Library/Caches/JetBrains/GoLand2023.3/tmp/GoLand/___go_build_github_com_hxzhouh_go_example_pattern_ioc 
 true 
 false 
 false 
 true
```

This is Inversion of Control, where the control logic `Undo` no longer depends on the business logic `IntSet`, but rather the business logic `IntSet` depends on `Undo`. Now, the Undo feature can be easily used by other business logics.

[文章索引](/article-index/)