I already made calculator that can compute with integers and real numbers(i made it with go). Then I want to make it possible to calculate complex and rational numbers by adding those modules. (it can also calculate when types are mixed) It can be easy if I check types of operands every time(runtime) and take care of each case, but I want to solve it with dynamic binding. Guys can you tell me the idea of how to solve this problem
CodePudding user response:
I think by dynamic typing, you're probably referring to how in eg C and Java, dynamic binding is essentially having reference to a base class that can point to a derived class (because the derived class "is a" base class).
One might say that the base class defines the interface behind which derived classes morph. If the derived classes replace methods of the base class, a reference to the base class can still access those methods in the derived class.
Base class can define some methods to provide some "base" functionality to its derived classes. If those classes don't redefine the method, the base class's method can be called.
In go, both these concepts exist, but they're quite different.
Go has an explicit interface
keyword that defines method signatures but no methods. Any value implicitly satisfies that interface if it has methods of the same name with the same signature.
type LivingBeing interface {
TakeInEnergy()
ExpelWaste()
}
The interface type becomes a valid type in the code. We can pass an interface to a function, and without knowing the type satisfying that interface, can call its methods:
func DoLife(being LivingBeing) {
being.TakeInEnergy()
being.ExpelWaste()
}
This is valid code, but not complete code. Unlike with base classes in other languages, interfaces cannot define functions, only their signatures. It is purely and only an interface definition. We have to define the types that satisfy the interface separately from the interface itself.
type Organism struct{}
func (o *Organism) TakeInEnergy() {}
func (o *Organism) ExpelWaste() {}
We now have a type organism
that satisfies LivingBeing
. It is something like a base class, but if we wanted to build on it, we can't use subclassing because Go doesn't implement it. But Go does provide something similar called embedding types.
In this example I'll define a new organism, Animal, that draws ExpelWaste()
from LivingBeing
, but defines its own TakeInEnergy()
:
type Animal struct {
Organism // the syntax for an embedded type: type but no field name
}
func (a *Animal) TakeInEnergy() {
fmt.Printf("I am an animal")
}
What isn't obvious from that code is that because Animal
's Organism
is not a named field, its fields and methods are accessible directly from Animal
. It's almost as if Animal
"is an" organism.
However it is *not * an organism. It is a different type, and it would be more accurate to think of object embedding as syntactic sugar to automatically promote Organism
's fields and methods to Animal
.
Since go is statically and explicitly typed, DoLife
cannot take an Organism
and then be passed an Animal
: it doesn't have the same type:
/* This does not work. Animal embeds organism, but *is not* an organism */
func DoLife(being *Organism) {
being.TakeInEnergy()
being.ExpelWaste()
}
func main() {
var a = &Animal{Organism{}}
DoLife(&Animal{})
}
cannot use &Animal{} (type *Animal) as type *Organism in argument to DoLife
That's why interface
exists - so that both Organism
and Animal
(and indeed, even Plant
or ChemotrophBacteria
) can be passed as a LivingBeing
.
Putting it all together, here's the code I've been working from:
package main
import "fmt"
type LivingBeing interface {
TakeInEnergy()
ExpelWaste()
}
type Organism struct{}
func (o *Organism) TakeInEnergy() {
}
func (o *Organism) ExpelWaste() {}
type Animal struct {
Organism
}
func DoLife(being LivingBeing) {
being.TakeInEnergy()
being.ExpelWaste()
}
func (a *Animal) TakeInEnergy() {
fmt.Printf("I am an animal")
}
func main() {
var a = &Animal{Organism{}}
DoLife(a)
}
There are a few caveats:
Syntactically, If you want to declare an embedded literal you must explicitly provide its type. In my example
Organism
has no fields so there's nothing to declare, but I still left the explicit type in there to point you in the right direction.DoLife
can call the rightTakeInEnergy
on a LivingBeing, butOrganism
's methods cannot. They see only the embeddedOrganism
.
func (o *Organism) ExpelWaste() {
o.getWaste() //this will always be Organism's getWaste, never Animal's
}
func (o *Organism)getWaste() {}
func (a *Animal)getWaste() {
fmt.Println("Animal waste")
}
Simlarly if you pass only the embedded part, then it's going to call its own TakeInEnergy()
, not that of Animal
; there's no Animal
left!
func main() {
var a = &Animal{Organism{}}
DoLife(&a.Organism)
}
In summary,
Define explict
interface
and use that type wherever you want "polymorphic" behaviorDefine base types and embed them in other types to share base functionality.
Don't expect the "base" type to ever "bind" to functions from the "derived" type.