您的位置:首页 > 编程语言 > Ruby

对照 Ruby 学 Go (Part 6): 结构体,方法与接口

2018-07-03 12:55 302 查看

转载自: http://zonov.me/golang-for-rubyists-part-6-structs-methods-and-interfaces/ 已获原作者授权原标题: Golang for Rubyists. Part 6. Structs, Methods and Interfaces

Let the force be with you, my friends. Today we will dive into some of the features, statical typing brings to us, those are Structs and Interfaces.

Structs

As I aim my series of articles to those, who has mostly scripting languages background, such as Ruby, Python, JS, I would expect that you have a very brief understanding what those things are. In the best case, you remember that words from your Computer Science study in a university. Let’s jump right into the code and take a look at some Struct definition example:
package main

import (
"fmt"
)

type User struct {
name   string
email  string
}

func main() {
u := User{name: "Max", email: "maxmustermann@fake.com"}
fmt.Println("Hello,", u.name)
}
(https://play.golang.org/p/FEQVAtNYig7)

Doesn’t it look similar to:
class User
attr_accessor :name
attr_accessor :email
end

u = User.new(name: "Max", email: "maxmustermann@fake.com")
p "Hello, #{u.name}"
By me, it looks pretty much. But as you may know, Ruby also has structs. Just because it is not statically typed we have two different structs, OpenStruct and Struct. And even the second one is still not as strict as Golang’s one, because it doesn’t have a type check. Anyway, the same snippet using Ruby’s Struct would look like this:
User = Struct.new(:name, :email)
u = User.new(name: "Max", email: "maxmustermann@fake.com")
p "Hello, #{u.name}"
Uh, I love Ruby’s expressiveness and readability.
Go is quite more verbose, but as you may notice, it is pretty readable as well.
What else should we add to understand structs? I think it should be clear enough from the example. Struct is basically a combination of one or more named fields with specified types. Fields can be accessed using dot (.) for both reading and writing.

Methods
Let’s take a look at one more Ruby example, which uses Struct:
User = Struct.new(:first_name, :last_name) do
def full_name
"#{first_name} #{last_name}"
end
end
How to implement something similar in Golang? Easy-peasy!
package main

import (
"fmt"
)

type User struct {
first_name   string
last_name  string
}

func (u *User) full_name() string {
return fmt.Sprintf("%s %s", u.first_name, u.last_name)
}

func main() {
u := User{first_name: "Max", last_name: "Mustermann"}
fmt.Println("Hello,", u.full_name())
}
(https://play.golang.org/p/RTaUjMnUXF6)

Could you ever imagine it? It looks exactly the same as Ruby’s methods. And even the name is exactly the same, Methods! So, the method in Golang is a function, which related to some specific Struct and can be applied only to it (only? hmmmm)


Interfaces
Do you remember, what polymorphism is? Wiki says:
Polymorphism - the provision of a single interface to entities of different types.
And how do we achieve it with Ruby? Eh, actually multiple ways, this Thoughtbot’s article perfectly describes them. But in general case, Ruby has a duck typing system, so let’s take a look on the following example:

class Dog
attr_reader :hungry

def initialize
@hungry = true
end

def feed!
p 'Making loud chewing noise'
@hungry = false
end

def bard
p 'bark'
end
end

class Cat
attr_reader :hungry

def initialize
@hungry = true
end

def feed!
@hungry = false
end

def meow
p 'meow'
end
end

class AutoFeeder
def self.feed_animal(animal)
animal.feed!
end
end

dog = Dog.new
p dog.hungry
AutoFeeder.feed(dog)
p dog.hungry
We have a class AutoFeeder which method expects something, which can be feeden (so, something, which implements interface “feed”). And since we’re in Ruby, we don’t have type checks, so we can just pass any object into this method and it either will perform the feeding operation or will fail with famous NoMethodError. And how can we achieve the same in Golang? As you remember, you should always specify a type for a function, so this snippet will work out:

package main

import (
"fmt"
)

type Dog struct {
hungry bool
}

type Cat struct {
hungry bool
}

func (d *Dog) feed() bool {
fmt.Println("Making loud chewing noise")
d.hungry = false
return d.hungry
}

func (c *Cat) feed() bool {
c.hungry = false
return c.hungry
}

func feed_animal(d *Dog) {
d.feed()
}

func main() {
myFluffy := Dog{hungry: true}
fmt.Println(myFluffy.hungry)
feed_animal(&myFluffy)
fmt.Println(myFluffy.hungry)
}
But how to make it work for my poor hungry kitten as well? Can we just pass my cat into feed_animal function?
func main() {
myHungryKitten := Cat{hungry: true}
fmt.Println(myHungryKitten.hungry)
feed_animal(&myHungryKitten)
fmt.Println(myHungryKitten.hungry)
}
Ah, c’mon, ruthless static typing! prog.go:33:14: cannot use &myHungryKitten (type *Cat) as type *Dog in argument to feed_animal
Thank gods, we have Interfaces! Let’s first take a look onto the example and then I will explain everything.
package main

import (
"fmt"
)

type Dog struct {
hungry bool
}

type Cat struct {
hungry bool
}

func (d *Dog) feed() bool {
fmt.Println("Making loud chewing noise")
d.hungry = false
return d.hungry
}

func (c *Cat) feed() bool {
c.hungry = false
return c.hungry
}

type Animal interface {
feed() bool
}

func feed_animal(a Animal) {
a.feed()
}

func main() {
myHungryKitten := Cat{hungry: true}
fmt.Println("Feeding my kitty")
feed_animal(&myHungryKitten)
fmt.Println(myHungryKitten.hungry)

myFluffy := Dog{hungry: true}
fmt.Println("Feeding my puppy")
feed_animal(&myFluffy)
fmt.Println(myFluffy.hungry)
}
Just like that! Now both my pets are full and happy now, many thanks to Interfaces! So, I changed to things here.
Firstly I added an interface, named Animal. You see the syntax is very similar to the way you describe structs. But instead of defining attributes, you specify, which methods should the particular struct implement, in order to comply with this interface.
The second thing I changed is now feed_animal expects not a Dog pointer, but some struct, which implements the interface Animal.
(https://play.golang.org/p/8NlLiCZDr-R)


Few important things to mention about Interfaces in Golang.
1. Structs don’t have to explicitly specify, that they implement some interface. It is checked implicitly by the interface itself.
2. One struct may implement multiple interfaces.


Actually, interfaces topic is deeper than what I described in this article, but this information should be totally enough to understand them and to start using it.
Also, I hope methods and structs are clear for you now as well. Feel free to ask questions in the comments section or approach me via Twitter. Or even buy me a coffee ^__^


Good luck in your endless but immersive learning path! 阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: