YouTube Summaries | How to Compose Interfaces in Golang

December 5th, 2023

Introduction:

The video introduces the concept of composability with interfaces in Golang, emphasizing its importance in real-life scenarios. As with all other video summaries, this is to help cement my learning after having watched the video, and I hope brings some learnings to you as well.

Use Case - Hash and Broadcast:

A common use case involves hash and broadcast with an IO reader. The following highlights the incorrect approach of using read all directly on the reader.

// Incorrect approach
func hashAndBroadcast(reader io.Reader) {
    data, err := ioutil.ReadAll(reader)
    if err != nil {
        return
    }

    hash := computeHash(data)
    fmt.Println("Hash:", hash)

    // Broadcasting logic
}

Interface Composability Solution:

Instead, lets look at this problem using interface composability, creating a new type, HashReader, which embeds the io.Reader interface and adds a hash function.

// Interface composability solution
type HashReader struct {
    io.Reader
}

// Adding a hash function to HashReader
func (hr *HashReader) Hash() string {
    data, _ := ioutil.ReadAll(hr.Reader)
    return computeHash(data)
}

// Creating a HashReader instance
func NewHashReader(reader io.Reader) *HashReader {
    return &HashReader{Reader: reader}
}

Implementation:

We then use the HashReader in the hash and broadcast scenario, where the benefits of composability can be seen for testing and maintainability.

// Using HashReader in hash and broadcast scenario
func hashAndBroadcast(reader io.Reader) {
    hashReader := NewHashReader(reader)
    hash := hashReader.Hash()
    fmt.Println("Hash:", hash)

    // Broadcasting logic
}

Refinement with Interface Composability:

The video refines the interface composability by introducing an interface, HashReader, with io.Reader embedding and a hash function.

// Improved interface composability
type HashReader interface {
    io.Reader
    Hash() string
}

// Creating a HashReader instance
func NewHashReader(reader io.Reader) HashReader {
    return &hashReaderImpl{Reader: reader}
}

// Implementation of HashReader interface
type hashReaderImpl struct {
    io.Reader
}

func (hr *hashReaderImpl) Hash() string {
    data, _ := ioutil.ReadAll(hr.Reader)
    return computeHash(data)
}