Home > OS >  Extend calculator with complex and rational module(using dynamic binding)
Extend calculator with complex and rational module(using dynamic binding)

Time:11-26

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:

  1. 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.

  2. DoLife can call the right TakeInEnergy on a LivingBeing, but Organism's methods cannot. They see only the embedded Organism.

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" behavior

  • Define 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.

  • Related