天天看點

6.2 如何了解Go語言中的接口

作者:老孫正經胡說

Go語言中的接口

接口有點像是定義,描述了必須具備的方法集合。通過觀察接口的代碼樣例中,Go語言中的接口并不是非常複雜,但是為代碼重用提供了高效的實作方式。

不同于Java中顯式的接口定義,Go中的接口是隐式定義,另外Java中的接口可以定義屬性,而Go中的接口則隻包含了方法的定義

// Interface
interface Animal {
  public String name = "animal";
  public void animalSound(); // interface method (does not have a body)
  public void sleep(); // interface method (does not have a body)
}


// Pig "implements" the Animal interface
class Pig implements Animal {
  public void animalSound() {
    // The body of animalSound() is provided here
    System.out.println("The pig says: wee wee");
  }
  public void sleep() {
    // The body of sleep() is provided here
    System.out.println("Zzz");
  }
}


class Main {
  public static void main(String[] args) {
    Pig myPig = new Pig();  // Create a Pig object
    myPig.animalSound();
    myPig.sleep();
  }
}           

如果修改為Go語言實作,則為

type Animal interface {
    animalSound();
    sleep();
}


type Pig struct {
    name string
}


func (p Pig) animalSound() {
    fmt.Println("The pig says: wee wee")
}


func (p Pig) sleep() {
    fmt.Println("Zzz")
}


func main() {
    p := Pig{"pig"}
    p.animalSound()
    p.sleep()
}           

與類、繼承之間的關系

stuct和inteface建構了Go語言中的面向對象

struct相當于其他語言的類,但是并不完全等同,是以不要完全套用傳統類的思想來進行開發。在結構體的學習中,我們知道Go語言中并沒有像其他語言提供類(Class)的定義方式,而是通過結構體及函數的Reciever間接的使用。本質上,Go中的類就是屬性和方法的組合。interface相當于實作了動态語言中的多态(Duck Typing)

Duck Typing是動态類型中常用的一種多态方式,為了便于了解,這裡重溫了Python Duck Typing的實作方法。

多态的引入提高了代碼的靈活度,有些變量類型可以在編譯或運作時決定

Go語言裡并沒有設計諸如虛函數、純虛函數、繼承、多重繼承的概念,但是仍然通過struct和interface實作類似繼承和多态的特性,使代碼能夠可重用且優雅,降低傳統面向對象語言開發的複雜程度。

Python中的Duck Typing

“當看到一隻鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。” 鴨子類型(英語:duck typing)在程式設計中是動态類型的一種風格。在這種風格中,一個對象有效的語義,不是由繼承自特定的類或實作特定的接口,而是由"目前方法和屬性的集合"決定。這個概念的名字來源于由詹姆斯·惠特科姆·萊利提出的鴨子測試。 簡單來說,不關注鴨子,而關注是否會呱呱叫。 在正常類型中,我們能否在一個特定場景中使用某個對象取決于這個對象的類型,而在鴨子類型中,則取決于這個對象是否具有某種屬性或者方法——即隻要具備特定的屬性或方法,能通過鴨子測試,就可以使用。

這不禁讓我讓我想到兩句諺語:狗拿耗子多管閑事和黑貓白貓能抓到耗子就是好貓。顯然,這裡要表達的意思和Duck Typing類似:隻要夠能抓住耗子,狗也可以是隻貓。

不同于強類型語言,在實際使用Duck Typing時,隻需要對象擁有這個方法即可調用,而不需要關心這些對象是否為同一類或繼承類,比如Python内置的len()方法,本質上是在類中實作了len方法。

class DuckTypingDemo:


    def __len__(self):
        return 4096


duck_typing = DuckTypingDemo()
print("DuckTypingDemo len =", len(duck_typing))
# DuckTypingDemo len = 4096


my_str = "hello, world"
print("String len =", len(my_str))
# String len = 12


my_list = [1, 2, 3, 4, 5, 6]
print("List len =", len(my_list))
# List len = 6


my_dict = {"one" : 1, "two" : 2}
print("Dict len =", len(my_dict))
# Dict len = 2


try:
    my_int = 6
    print("int class =", my_int.__class__.__name__)
    print("int len =", len(my_int))
except Exception as e:
    print("Failed Reason:", e)
# int class = int
# Failed Reason: object of type 'int' has no len()           

當我們為自定義類DuckTypingDemo定義了len方法時,就可以調用len方法,同理在内置函數中,對于string, list和dict也可以通過len擷取相應類型的長度。

而對于沒有定義len的int類型來說,就無法使用len方法擷取其長度了。當然我們可以通過繼承int,實作其len後,就可以使用len()方法了。

class IntWithLen(int):


    def __len__(self):
        return 1024


i = IntWithLen()
print("Int with length:", len(i))
# Int with length: 1024           

命名規範

根據慣例,接口名稱字尾一般為er,像Reader, Writer, Formatter等

避免混淆,例如:Read、Write、Close、Flush、String

如果你定義的内容确實與這些類型相同,需要使用相同的名稱和簽名,例如字元轉換方法是String而不是ToString