Inheritance and interfaces - go

I want to express the extends behavior many languages possess.
In my Go Code I have a few structs that look like so:
type Base struct {
// Some fields
}
type BaseExtender struct {
Base
// Unique fields
}
type AnotherBaseExtender struct {
Base
// Unique fields
}
Now, I want to write a function that takes any Base since I only need it's "similar fields".
func UseTheBase(b Base) {
testVal := b.thingICareAbout
}
However, this doesn't work. I've done some digging into interface and thought I could do something like:
type Base interface {
// Some fields
}
Except it appears that Go automatically infers interfaces by method implementation. Is there a way to mimic this behavior so I can pass any Base into my function, and not have to implement some nop method on the Base struct and all of it's extenders?
Thanks!

Base implies you want inheritance in Go, Go deliberately eschews inheritance, don't try to recreate it. You can embed types but think of this as embedding behaviour, not just data (as you might be tempted to in an inheriting language).
You're on the right lines with your solution but need public methods and yes interfaces are defined in terms of methods. Just define the interface where you call it as:
type Doer interface {
DoSomething()
}
...
func doit(d Doer) {
d.DoSomething()
}
doit doesn't care what its argument is as long as it has a DoSomething method. Obviously this is a trivial example and there's no point in it, but if you need to override something in all extenders, ask yourself why Base exists, if it is just to add some fields, that's probably not enough reason for a separate type, just add the fields where you need them.
Try to avoid the vast taxonomies of types that you might construct in other languages.

To add on, a common pattern I use is:
type Base interface {
SomeFunction() int
}
type SimpleBaseImpl struct {
// Unique fields
}
func (s SimpleBaseImpl) SomeFunction() int {
return 0
}
type SomethingMoreComplicated struct {
SimpleBaseImpl
// Unique fields
}
Then you could treat SomethingMoreComplicated as "type" Base – but again, its important to note the golang preference for composition (shown here) over inheritance.

Related

Golang: How to embed interface with different type parameters?

The code gives me error: DB redeclared.
Is there any idiomatic way to solve it? Or any work-around?
TIA
type a struct {
DB[int64]
DB[string]
}
type b interface {
DB[int64]
DB[string]
}
type DB[T any] interface {
GetList(query string) ([]T, error)
}
You can't embed the same interface, even with different type parameters. Regardless of how it is instantiated, you are trying to promote into the b interface two methods with the same name GetList and different signatures — given by the different instantiations of DB.
The situation is similar, although not technically the same, for embedding into a struct. In structs, the name of the embedded field is the name of the type — DB —, and a struct can't have two non-blank fields with the same name.
About how to solve this issue, it depends what you want to accomplish.
If you want to convey that "a implements DB with either type parameter" you can embed DB[T] and make a itself generic, and restrict a's type parameters:
type a[T int64 | string] struct {
DB[T]
}
// illustrative implementation of the DB[T] interface
// if you embed DB[T] you likely won't use `a` itself as receiver
func (r *a[T]) GetList(query string) ([]T, error) {
// also generic method
}
This is okay because DB's type parameter is constrained to any, and a's type parameter is more restrictive. This allows you to use a in other generic methods, or choose a specific type upon instantiation, but the implementation of GetList has to be parametrized too.
Otherwise if you need a to have separated methods that return int64 or string, you must give it different names.
Finally, you can embed instances of DB into two interfaces with different names, and then embed those into a instead.
type a struct {
DBStr
DBInt
}
type DBStr interface {
DB[string]
}
type DBInt interface {
DB[int64]
}
This way though the top-level selector isn't available because the method names are still the same. The program compiles, but you'll have to explicitly choose which field to call the method on:
myA := a{ /* init the two fields */ }
res, err = myA.DBStr.GetList(query)
// res is type []string
// or
res, err = myA.DBInt.GetList(query)
// res is type []int64

field in interface[golang]?

is there any solution to force struct have specific property(defined in interface)?
or define a property(attribute,field) in interface?.
as i see interface is always accept method not property?. (https://gobyexample.com/interfaces)
type geo interface {
PrintType()
typ string//not function , but field
}
type circle struct {
typ string
}
func (c circle) PrintType() {
fmt.Println(c.typ)
}
thanks
Interfaces are about methods only, not properties, because interfaces are about behavior. So there is no way to force a struct to have something. Rather the preferred method is to have an accessor to that member.

how to write a function to process two types of input data in golang

I have multiple structs share some fields. For example,
type A struct {
Color string
Mass float
// ... other properties
}
type B struct {
Color string
Mass float
// ... other properties
}
I also have a function that only deals with the shared fields, say
func f(x){
x.Color
x.Mass
}
How to deal with such situations? I know we can turn the color and mass into functions, then we can use interface and pass that interface to the function f. But what if the types of A and B cannot be changed. Do I have to define two functions with essentially the same implementation?
In Go you don't the traditional polymorphism like in Java, c#, etc. Most things are done using composition and type embedding. A way of doing this simply is by changing your design and group the common field in a separate struct. It's just a different of thinking.
type Common struct {
Color string
Mass float32
}
type A struct {
Common
// ... other properties
}
type B struct {
Common
// ... other properties
}
func f(x Common){
print(x.Color)
print(x.Mass)
}
//example calls
func main() {
f(Common{})
f(A{}.Common)
f(B{}.Common)
}
There are other ways too by using interfaces and getters mentioned here but IMO this is the simplest way

How to modify a field in a struct of an unknown type?

I have multiple structs that have one common field; let's call it common here
type Struct1 struct {
foo string
bar string
common string
}
type Struct2 struct {
baz int
qux string
common string
}
I want to create a function that takes an Interface as input and nullifies common. The available struct types won't be known at compile time, so I can't create a separate function for each type, and I can't use a switch statement.
P.S: In my use-case, I want to nullify common because it holds the creation time of each struct, and I want to track the history of the struct, so I will know if it changes. Having the creation time inside the struct will mess this up because the creation time will be different every time a new struct is generated even though its actual data may be the same.
Define a struct with the common fields and make it implement an interface which says that it is able to nullify the common fields. Then embed this struct into your other struct types that should be able to nullify the fields.
// CommonNullifier is able to nullify its common field(s)
type CommonNullifier interface {
NullifyCommon()
}
// StructCommon contains the common struct fields
type StructCommon struct {
Common string
}
func (sc *StructCommon) NullifyCommon() {
sc.Common = ""
}
// Struct1 embeds common fields, thus implements CommonNullifier
type Struct1 struct {
StructCommon
Foo string
}
// Struct2 also embeds common fields, thus also implements CommonNullifier
type Struct2 struct {
StructCommon
Bar string
}
// NullifyCommon nullfies the 'common' fields in the argument
func NullifyCommon(s CommonNullifier) {
s.NullifyCommon()
}
(See it on the Go Playground)
You could also use reflection, but using an interface is generally more readable.

Private embedded struct when importing a struct from another package

I have a project which relies on a struct imported from another package, which I will call TheirEntity.
In the example below, I (ahem) embed TheirEntity in MyEntity, which is an extension of TheirEntity, with added functionality.
However, I don't want to export TheirEntity in the MyEntity structure, as I would rather the consumer not access TheirEntity directly.
I know that Go embedding is not the same as inheritance in classical OOP, so maybe this is not the correct approach, but is it possible to specify embedded structs as "private", even if they are imported from another package? How might one achieve the same thing in a more idiomatic fashion?
// TheirEntity contains functionality I would like to use...
type TheirEntity struct {
name string
}
func (t TheirEntity) PrintName() {
fmt.Println(t.name)
}
func NewTheirEntity(name string) *TheirEntity {
return &TheirEntity{name: name}
}
// ... by embedding in MyEntity
type MyEntity struct {
*TheirEntity // However, I don't want to expose
// TheirEntity directly. How to embed this
// without exporting and not changing this
// to a named field?
color string
}
func (m MyEntity) PrintFavoriteColor() {
fmt.Println(m.color)
}
func NewMyEntity(name string, color string) *MyEntity {
return &MyEntity{
TheirEntity: NewTheirEntity(name),
color: color,
}
}
Since the question was asked, Go saw the addition of type aliases to the language with the 1.9 release in 2017. It turns out that, through an unconventional use of type aliases, you can have your cake and eat it too!
First, declare an unexported alias for the third-party type you wish to embed in your struct:
type theirEntity = TheirEntity
Then, simply embed that alias instead of the original type:
type MyEntity struct {
*theirEntity
color string
}
(Playground)
[I]s it possible to specify embedded structs as "private", even if they are imported from another package?
No.
How might one achieve the same thing in a more idiomatic fashion?
By not-embedding but making it a unexported named field.
Like this:
type MyEntity struct {
*privateTheirEntity
}
type privateTheirEntity struct {
*TheirEntity
}

Resources