Thursday, February 29, 2024
HomeGolangObject Oriented Programming in Go

Object Oriented Programming in Go


Somebody requested a query on the discussion board right this moment on achieve the advantages of inheritance with out embedding. It’s actually vital for everybody to assume by way of Go and never the languages they’re abandoning. I can’t let you know a lot code I faraway from my early Go implementations as a result of it wasn’t essential. The language designers have years of expertise and data. Hindsight helps to create a language that’s quick, lean and actually enjoyable to code in.

I take into account Go to be a lightweight object oriented programming language. Sure it does have encapsulation and sort member capabilities nevertheless it lacks inheritance and subsequently conventional polymorphism. For me, inheritance is ineffective until you wish to implement polymorphism. With the best way interfaces are applied in Go, there is no such thing as a want for inheritance. Go took the perfect components of OOP, unnoticed the remainder and gave us a greater option to write polymorphic code.

Here’s a fast view of OOP in Go. Begin with these three structs:

kind Animal struct {
    Identify string
    imply bool
}

kind Cat struct {
    Fundamentals Animal
    MeowStrength int
}

kind Canine struct {
    Animal
    BarkStrength int
}

Listed below are three structs you’d most likely see in any OOP instance. We’ve a base struct and two different structs which might be particular to the bottom. The Animal construction comprises attributes that every one animals share and the opposite two structs are particular to cats and canines.

The entire member properties (fields) are public aside from imply. The imply area within the Animal struct begins with a lowercase letter. In Go, the case of the primary letter for variables, structs, fields, capabilities, and so forth. decide the entry specification. Use a capital letter and it is public, use a lowercase letter and it is personal.

Observe: The idea of private and non-private entry in Go shouldn’t be precisely true:
https://www.ardanlabs.com/weblog/2014/03/exportedunexported-identifiers-in-go.html

Since there is no such thing as a inheritance in Go, composition is your solely selection. The Cat struct has a area known as Fundamentals which is of kind Animal. The Canine struct is utilizing an un-named struct (embedding) for the Animal kind. It is as much as you to resolve which is best for you and I’ll present you each implementations.

I wish to thank John McLaughlin for his remark about un-named structs!!

To create a member operate (methodology) for each Cat and Canine, the syntax is as follows:

func (canine *Canine) MakeNoise() {
    barkStrength := canine.BarkStrength

    if canine.imply == true {
        barkStrength = barkStrength * 5
    }

    for bark := 0; bark < barkStrength; bark++ {
        fmt.Printf(“BARK “)
    }

    fmt.Println(“”)
}

func (cat *Cat) MakeNoise() {
    meowStrength := cat.MeowStrength

    if cat.Fundamentals.imply == true {
        meowStrength = meowStrength * 5
    }

    for meow := 0; meow < meowStrength; meow++ {
        fmt.Printf(“MEOW “)
    }

    fmt.Println(“”)
}

Earlier than the identify of the strategy we specify a receiver which is a pointer to every kind. Now each Cat and Canine have strategies known as MakeNoise.

Each these strategies do the identical factor. Every animal speaks of their native tongue primarily based on their bark or meow energy and if they’re imply. Foolish, nevertheless it exhibits you entry the referenced object (worth).

When utilizing the Canine reference we entry the Animal fields straight. With the Cat reference we use the named area known as Fundamentals.

Thus far we’ve lined encapsulation, composition, entry specs and member capabilities. All that’s left is create polymorphic habits.

We use interfaces to create polymorphic habits:

kind AnimalSounder interface {
    MakeNoise()
}

func MakeSomeNoise(animalSounder AnimalSounder) {
    animalSounder.MakeNoise()
}

Right here we add an interface and a public operate that takes a worth of the interface kind. Really the operate will take a reference to a worth of a sort that implements this interface. An interface shouldn’t be a sort that may be instantiated. An interface is a declaration of habits that’s applied by different sorts.

There’s a Go conference of naming interfaces with the “er” suffix when the interface solely comprises one methodology.

In Go, any kind that implements an interface, by way of strategies, then represents the interface kind. In our case each Cat and Canine have applied the AnimalSounder interface with pointer receivers and subsequently are thought of to be of kind AnimalSounder.

Which means pointers of each Cat and Canine might be handed as parameters to the MakeSomeNoise operate. The MakeSomeNoise operate implements polymorphic habits by the AnimalSounder interface.

Should you needed to scale back the duplication of code within the MakeNoise strategies of Cat and Canine, you might create a technique for the Animal kind to deal with it:

func (animal *Animal) PerformNoise(energy int, sound string) {
    if animal.imply == true {
        energy = energy * 5
    }

    for voice := 0; voice < energy; voice++ {
        fmt.Printf(“%s “, sound)
    }

    fmt.Println(“”)
}

func (canine *Canine) MakeNoise() {
    canine.PerformNoise(canine.BarkStrength, “BARK”)
}

func (cat *Cat) MakeNoise() {
    cat.Fundamentals.PerformNoise(cat.MeowStrength, “MEOW”)
}

Now the Animal kind has a technique with the enterprise logic for making noise. The enterprise logic stays inside the scope of the kind it belongs to. The opposite cool profit is we need not move the imply area in as a parameter as a result of it already belongs to the Animal kind.

Right here is the whole working pattern program:

bundle important

import (
    “fmt”
)

kind Animal struct {
    Identify string
    imply bool
}

kind AnimalSounder interface {
    MakeNoise()
}

kind Canine struct {
    Animal
    BarkStrength int
}

kind Cat struct {
    Fundamentals Animal
    MeowStrength int
}

func important() {
    myDog := &Canine{
        Animal{
           “Rover”, // Identify
           false,   // imply
        },
        2, // BarkStrength
    }

    myCat := &Cat{
        Fundamentals: Animal{
            Identify: “Julius”,
            imply: true,
        },
        MeowStrength: 3,
    }

    MakeSomeNoise(myDog)
    MakeSomeNoise(myCat)
}

func (animal *Animal) PerformNoise(energy int, sound string) {
    if animal.imply == true {
        energy = energy * 5
    }

    for voice := 0; voice < energy; voice++ {
        fmt.Printf(“%s “, sound)
    }

    fmt.Println(“”)
}

func (canine *Canine) MakeNoise() {
    canine.PerformNoise(canine.BarkStrength, “BARK”)
}

func (cat *Cat) MakeNoise() {
    cat.Fundamentals.PerformNoise(cat.MeowStrength, “MEOW”)
}

func MakeSomeNoise(animalSounder AnimalSounder) {
    animalSounder.MakeNoise()
}

Right here is the output:

BARK BARK
MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW

Somebody posted an instance on the board about embedding an interface within a struct. Right here is an instance:

bundle important

import (
    “fmt”
)

kind HornSounder interface {
    SoundHorn()
}

kind Car struct {
    Listing [2]HornSounder
}

kind Automotive struct {
    Sound string
}

kind Bike struct {
   Sound string
}

func important() {
    car := new(Car)
    car.Listing[0] = &Automotive{“BEEP”}
    car.Listing[1] = &Bike{“RING”}

    for _, hornSounder := vary car.Listing {
        hornSounder.SoundHorn()
    }
}

func (automobile *Automotive) SoundHorn() {
    fmt.Println(automobile.Sound)
}

func (bike *Bike) SoundHorn() {
    fmt.Println(bike.Sound)
}

func PressHorn(hornSounder HornSounder) {
    hornSounder.SoundHorn()
}

On this instance the Car struct maintains an inventory of values that implement the HornSounder interface. In important we create a brand new car and assign a Automotive and Bike pointer to the listing. This task is feasible as a result of Automotive and Bike each implement the interface. Then utilizing a easy loop, we use the interface to sound the horn.

The whole lot you have to implement OOP in your utility is there in Go. As I stated earlier than, Go took the perfect components of OOP, unnoticed the remainder and gave us a greater option to write polymorphic code.

To be taught extra on associated matters try these posts:

https://www.ardanlabs.com/weblog/2014/05/methods-interfaces-and-embedded-types.html
https://www.ardanlabs.com/weblog/2013/07/how-packages-work-in-go-language.html
https://www.ardanlabs.com/weblog/2013/07/singleton-design-pattern-in-go.html

I hope this small instance helps you in your future Go programming.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments