How to Simulate Inheritance in Go with Generics, Composition, and Interfaces
This article demonstrates how to achieve class‑inheritance‑like behavior in Go by combining generics, struct embedding, and interfaces, walking through a BaseJob generic base class, a ProductJob implementation, runtime polymorphism, and a unified task manager for scalable scheduling.
1. Generic BaseJob Implementation
In lib/basejob.go a generic BaseJob[T model.JobConfig] struct is defined, providing common fields such as JobConfig, JobName, Description, RetryCount and an EasyFunc placeholder.
type BaseJob[T model.JobConfig] struct {
JobConfig T
JobName string
Description string
RetryCount int
EasyFunc interface{}
}The type parameter T is constrained to model.JobConfig, ensuring every task configuration implements a specific interface. Common fields enable reuse across tasks, and several generic methods (error handling, config retrieval, state saving) are provided:
func (job *BaseJob[T]) DoError(err error, description string) {
job.RetryCount++
log.Println(description, err)
// update task status
}
func (job *BaseJob[T]) GetSyncConfig() error {
// generic implementation to fetch task configuration
}2. "Inheritance" via ProductJob
The file jobs/productjob/easyjob.go shows how composition is used to mimic inheritance. An EasyJob struct embeds the generic BaseJob:
type EasyJob[T model.ByIdConfig] struct {
easylib.BaseJob[model.ByIdConfig]
}Key design points:
Embedding BaseJob : EasyJob inherits all base functionality.
Method overriding : Specific business logic is supplied by redefining methods.
Method‑overriding example – data retrieval
func (job *EasyJob[T]) GetDataPageList() ([]interface{}, []interface{}, interface{}, error) {
jobConfig := job.JobConfig
maxPid := easylib.GetMaxPid()
if jobConfig.Lastid >= maxPid {
return nil, nil, maxPid, nil
}
// call business query
products, delProducts, lastId, err := QueryProduct(jobConfig.Lastid, jobConfig.Limit)
data := easylib.MapperFromProducts(products)
deleteData := easylib.MapperFromProducts(delProducts)
return data, deleteData, lastId, nil
}Method‑overriding example – ES update
func (job *EasyJob[T]) UpdateEs(data []interface{}) error {
ps := easylib.MapperToProducts(data)
err := products.BulkInsert(ps)
return err
}3. Polymorphism with Interfaces and Runtime Binding
The Run method of BaseJob demonstrates runtime polymorphism using a type assertion on the EasyFunc interface:
func (job *BaseJob[T]) Run() {
// ...
data, delData, last, err := job.EasyFunc.(EasyFunc).GetDataPageList()
// ...
err = job.EasyFunc.(EasyFunc).UpdateEs(data)
// ...
}Key points:
EasyFunc interface : defines the methods a task must implement.
Runtime binding : the concrete implementation is invoked via job.EasyFunc.(EasyFunc).
4. Task Manager Design
A centralized manager in lib/jobmanager.go tracks all tasks:
type EasyJobParam struct {
JobName string
Status int
Description string
RetryCount int
Interval time.Duration
}
var EasyJobManager = struct {
Jobs map[string]*EasyJobParam
Lock sync.RWMutex
}{
Jobs: make(map[string]*EasyJobParam),
}Conclusion
The design achieves:
Generic configuration : type parameters allow different task configs.
Code reuse : BaseJob supplies common logic, reducing duplication.
Flexible extension : new tasks only need to embed BaseJob and override key methods.
Unified management : the task manager centrally controls task states.
Although Go lacks traditional class inheritance, combining composition, interfaces, and method overriding provides powerful abstraction suitable for highly extensible systems such as a task scheduling platform.
The full source code will be published on GitHub soon, with further performance‑tuning examples to follow.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Wrench
Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
