Interface embedding and calling interface methods from another package in Go (Golang)

In Go (Golang), interface embedding is a concept where you can include one interface inside another, effectively enabling the outer interface to inherit the methods declared by the embedded interface. Interface embedding helps in organizing and reusing code by building more complex interfaces out of simpler ones.
Directory structure:
example2/
|-- main.go
|-- go.mod
|-- monitor/
    |-- monitor.go
|-- validator/
    |-- validator.go

Module Declaration:

This declares the module name example2 and specifies that this module is written in Go version 1.20.

Example

module example2

go 1.20

Monitor Package:
  • This package imports the validator package.
  • It declares a struct named Monitor which embeds the Validator interface from the validator package. The field is named dynamicValidator.
  • There's a constructor function named NewMonitor which takes a deviceID and an instance of Validator. It returns an instance of Monitor.
  • The Monitor method on the Monitor struct simply calls the ValidateLocation method on the embedded Validator.

Example

package monitor

import validator "example2/validator"

type Monitor struct {
	deviceID         string
	dynamicValidator validator.Validator
}

func NewMonitor(deviceID string, dynamicValidator validator.Validator) (*Monitor, error) {
	return &Monitor{
		deviceID:         deviceID,
		dynamicValidator: dynamicValidator,
	}, nil
}

func (mon *Monitor) Monitor() error {
	mon.dynamicValidator.ValidateLocation("whatever")
	return nil
}

Validator Package:
  • This package declares an interface named Validator with a method signature ValidateLocation(location string) error.
  • It also declares a private struct validation that implements this Validator interface. This struct has a field appID which seems to be used for validation purposes, though the current implementation of ValidateLocation only prints the location.
  • There's a constructor function named NewValidator which takes appID as a parameter and returns an instance of validation as a Validator.

Example

package validator

import "fmt"

type Validator interface {
	ValidateLocation(location string) error
}

type validation struct {
	appID string
}

func NewValidator(
	appID string,
) Validator {
	return &validation{
		appID: appID,
	}
}

func (v *validation) ValidateLocation(location string) error {
	fmt.Println(location)
	return nil
}

Main Package:
  • Both validator and monitor packages are imported with aliases d and m respectively.
  • In the main function:
    1. An instance of Validator is created using the NewValidator function from the validator package.
    2. This Validator instance is then passed to the NewMonitor function from the monitor package to create a Monitor instance.
    3. Finally, the Monitor method is called on the Monitor instance.

This program demonstrates the power of interfaces in Go:

  • Decoupling: The monitor package doesn't need to know the exact implementation of the Validator it uses. It only cares about the methods described in the Validator interface. This makes it easy to swap out validation implementations without changing the monitor package.

  • Composition: The Monitor struct in the monitor package composes its behavior using the embedded Validator interface. This is a powerful way to build complex types by composing simpler ones.

  • Package Organization: Each package has its responsibility: validator handles validation, and monitor monitors something (in this example, it just calls the validation). This separation of concerns makes the codebase modular and maintainable.

Example

package main

import (
	"fmt"

	d "example2/validator"

	m "example2/monitor"
)

func main() {
	// Create a validator instance
	validatorInstance := d.NewValidator("your_app_id")

	// Create a monitor instance with the validator validator
	monitorInstance, _ := m.NewMonitor("your_device_id", validatorInstance)

	// Call the Monitor method
	err := monitorInstance.Monitor()
	if err != nil {
		fmt.Println("Error:", err)
	}
}

When you run this program, it will print:

whatever

This output comes from the ValidateLocation method in the validator package, which is called via the Monitor method of the monitor package.


Most Helpful This Week