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