This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). You can find a copy of the license at https://www.gnu.org/licenses/agpl-3.0.en.html
For commercial use, please use my contact page to obtain a commercial license.
This package provides an implementation of Object-Oriented Programming (OOP) concepts in Go, focusing on creating a runtime type system that supports features typically found in languages with more traditional OOP support.
Go is not a traditional object-oriented language. While it has structs and interfaces, it lacks features like inheritance, method overriding, and runtime type information that are common in languages like C++, Java, or C#. This package provides a framework to simulate these OOP features in Go.
import "project/pkg/oop"The implementation provides runtime type information through the TypeInfo struct, which stores:
- Type name
- Type ID (unique identifier)
This allows for type checking and identification at runtime, similar to C++'s typeid or Java's reflection.
The implementation simulates vtables, which are used in languages like C++ to support dynamic dispatch:
VtableInfoassociates a type ID with a pointer to its vtable- This enables method overriding and polymorphic behavior
Several casting functions are provided:
Cast: Converts an object to a different type, handling interface and type conversionsAs: Performs a dynamic cast and returns an optional pointerAsPtr: Returns a pointer to the object's data
These functions allow for safe type conversions, similar to C++'s dynamic_cast or C#'s as operator.
The implementation maintains class metadata through:
ClassInfo: Holds runtime information about a classKlassHeader: Contains metadata for a class instanceKlass: Represents a class instance with metadata
This metadata enables features like inheritance, initialization/deinitialization, and type checking.
The IObject struct represents an interface object with:
- A pointer to the underlying data
- A pointer to the vtable
This allows for interface-based programming with dynamic dispatch.
The implementation provides utilities for working with nil interfaces:
Nilstruct and its methods for creating nil interface valuesNilInterfacefunction to create a nil instance of a specified interface typeIsNilfunction to check if an interface instance is nil
The example below demonstrates how to use the user-friendly API provided by this package:
package main
import (
"fmt"
"reflect"
"project/pkg/oop"
)
// Define an interface
type IAnimal interface {
MakeSound()
}
// Define implementing structs
type Dog struct {
Name string
}
func (d *Dog) MakeSound() {
fmt.Printf("%s: Woof!\n", d.Name)
}
type Cat struct {
Name string
}
func (c *Cat) MakeSound() {
fmt.Printf("%s: Meow!\n", c.Name)
}
func main() {
// Create an object factory
factory := oop.NewObjectFactory()
// Create Dog and Cat instances using the user-friendly API
dogObj := factory.CreateObject(&Dog{Name: "Buddy"})
catObj := factory.CreateObject(&Cat{Name: "Whiskers"})
// Cast the objects to the IAnimal interface using the simplified API
animalDog := dogObj.As((*IAnimal)(nil))
animalCat := catObj.As((*IAnimal)(nil))
// Polymorphic method calls
animalDog.(IAnimal).MakeSound() // Outputs: Buddy: Woof!
animalCat.(IAnimal).MakeSound() // Outputs: Whiskers: Meow!
// Clean up resources
dogObj.Destroy()
catObj.Destroy()
}For comparison, here's how to use the low-level API directly:
// Create Dog and Cat instances
dog := oop.New(allocator, reflect.TypeOf(Dog{}), &Dog{Name: "Rex"})
cat := oop.New(allocator, reflect.TypeOf(Cat{}), &Cat{Name: "Felix"})
// Cast to the IAnimal interface
animalDog := oop.Cast(dog.Class, reflect.TypeOf((*IAnimal)(nil)).Elem())
animalCat := oop.Cast(cat.Class, reflect.TypeOf((*IAnimal)(nil)).Elem())
// Polymorphic method calls
animalDog.(IAnimal).MakeSound() // Outputs: Rex: Woof!
animalCat.(IAnimal).MakeSound() // Outputs: Felix: Meow!
// Clean up resources
dog.Deinit()
cat.Deinit()The implementation carefully manages memory layout to support features like:
- Finding a class instance from an object pointer
- Accessing vtables for dynamic dispatch
- Maintaining class hierarchies
The implementation leverages Go's reflection package to:
- Get type information at runtime
- Create and manipulate objects dynamically
- Perform type checking and conversions
The implementation uses the unsafe package for low-level memory operations:
- Converting between different pointer types
- Calculating memory offsets
- Accessing memory directly
The package includes a user-friendly API layer that simplifies working with the OOP implementation:
The ObjectFactory class provides a simplified way to create objects:
// Create a factory
factory := oop.NewObjectFactory()
// Create an object (much simpler than using New directly)
dogObj := factory.CreateObject(&Dog{Name: "Buddy"})Benefits:
- Hides the complexity of the underlying OOP implementation
- No need to manually specify types using reflection
- Provides a more intuitive object creation process
The ObjectWrapper class wraps a Klass object and provides simplified methods:
// Cast to an interface (much simpler than using Cast directly)
animalDog := dogObj.As((*IAnimal)(nil))
// Clean up resources
dogObj.Destroy()Benefits:
- Simplifies casting operations
- Provides a cleaner API for resource management
- Hides the internal details of the OOP implementation
This OOP implementation is useful for:
- Complex Object Hierarchies: When you need to model complex inheritance relationships
- Plugin Systems: For dynamically loading and using components with polymorphic behavior
- Language Interoperability: When interfacing with C++ or other OOP languages
- Legacy Code Migration: When porting code from OOP languages to Go
While this implementation provides many OOP features, it has some limitations:
- Performance Overhead: The use of reflection and dynamic dispatch adds runtime overhead
- Type Safety: Some operations use
unsafeand may bypass Go's type system - Complexity: The implementation adds complexity compared to idiomatic Go code
- Maintenance: Code using this framework may be harder to maintain than standard Go code
This OOP package demonstrates how to extend Go with features from traditional object-oriented languages. The addition of a user-friendly API layer shows how complex functionality can be wrapped in a more intuitive interface. While this approach is not recommended for most Go projects (which should follow Go's idiomatic patterns), it provides valuable insights into language design and API development, and can be useful for specific use cases where traditional OOP features are required.