在上篇文章中,咱們編撰了一則簡短的小故事用於講解了什麼是面向對象的繼承特性以及 Go
語言是如何實現這種繼承語義的,這一節咱們將繼續探討新的場景,但願能順便講解面向對象的接口概念.java
爲了照顧到沒有看過上一節文章的讀取,這裏再簡述一下上節文章關於買寵物的故事,如需詳細瞭解,請自行翻閱歷史文章進行查看.編程
A: 貓是一種寵物,淘氣可愛會賣萌,看家本領抓老鼠,偶爾還會喵喵喵. B: 狗是一種寵物,忠實聽話能看家,嗅覺靈敏會破案,一言不合汪汪汪. C: 我想要買一個寵物,文能賣萌,武可退敵,明個一早給我送來吧!框架
因而,次日,A和B各自帶着本身的寵物來拜見C,並附上各自的理由,說的頭頭是道,C總以爲有些哪裏不對,可一時間又無言反駁,只能悻悻收下了貓和狗,白白多花了一份錢!dom
這則故事很簡單,但同時也暴露出一個問題,那就是在這場交易中,賣家實際上虧了,明明只是想買一個寵物,結果卻買了兩個!編程語言
固然,在上篇文中最後也給出瞭解決方案,那就是將貓和狗進行抽象封裝,共性的部分提取成寵物,個性的部分纔是貓和狗.ide
如此一來,顧客買寵物時要麼買的是貓,要麼面對是狗,具體買的是什麼寵物是由顧客本身根據各自寵物的個性決定的,必定程度上解決了交易不公平的問題.工具
讓咱們再簡單回憶一下繼承的實現過程,回憶的過程當中不妨思考一下繼承有沒有沒能解決的問題?oop
type Pet struct {
}
func (p *Pet) Skill() {
fmt.Println("能文能武的寵物")
}
複製代碼
type Cat struct {
p *Pet
}
func (c *Cat) Catch() {
fmt.Println("老鼠天敵喵喵喵")
}
複製代碼
type Dog struct {
p *Pet
}
func (d *Dog) Navigate() {
fmt.Println("自帶導航汪汪汪")
}
複製代碼
某一天,C要能文能武的寵物,最好還能夠順便抓個老鼠,因而C選擇了喵喵喵!ui
func TestExtendInstance(t *testing.T) {
p := new(Pet)
c := new(Cat)
c.p = p
// 老鼠天敵喵喵喵
c.Catch()
// 能文能武的寵物
c.p.Skill()
}
複製代碼
過了一陣子,C以爲貓除了抓老鼠別的什麼都不會,別人遛狗,我遛貓?spa
因而,想要一種能自帶導航功能的寵物,毫無疑問的是,選擇了狗.
func TestExtendInstance(t *testing.T) {
p := new(Pet)
d := new(Dog)
d.p = p
// 自帶導航汪汪汪
d.Navigate()
// 能文能武的寵物
d.p.Skill()
}
複製代碼
上述示例,簡而言之就是經過組合的方式實現了面向對象中的繼承特性,解決了貓和狗除了是寵物仍是本身的問題.
面對貓和狗兩種寵物,顧客犯了選擇困難症,因而第一次全盤照收買下了兩種寵物,吃了一次啞吧虧.
後來在市場監督的介入下,利用面向對象的繼承特性,用 Go
語言實現了貓和狗的個性化與寵物的共性化,今後像C同樣的顧客不再會面臨選擇困難症,每一次都要根據獨特的需求,最終選擇某一種寵物,要麼是貓,要麼是狗.
不知過了多久,這種相安無事的場景最終被一羣急性子的顧客所打破,這一天寵物市場一大早就來一大批人,一上來就吵吵嚷嚷說快給咱們一批寵物,咱們要做爲抽獎活動的獎品,必定要快一點!
誰知道銷售人員不緊不慢地說: "彆着急,咱們這裏的寵物有不少種,有貓,有狗,有兔子,有金魚,有烏龜,有蝸牛..."
"別整那些虛頭巴腦的,我只要寵物,趕忙給我寵物就行,別盡扯沒用的",顧客吵吵說.
果真是一羣急性子的顧客,還沒等銷售人員介紹完各個寵物的差別性亮點直接被打斷了.
寵物市場吵吵鬧鬧引來了市場監督人員的注意,顧客和商家均向官方訴苦,指望能給出一個解決辦法!
市場監督人員心想: 商家和顧客本來和諧相處的,今天怎麼會吵鬧起來?
仔細聽了事情前因後果,雙方都沒有過錯,看來還真的是市場趕不上實際需求的變化,真得儘快研究新的解決辦法才行啊!
"冷靜一下,大家的意見咱們這邊已經知道了,這樣吧,給咱們三天的時間,咱們必定會想出一個萬全之策,到時候再公佈新的交易規則,如今我宣佈暫時關閉交易,省的再惹出沒必要要的爭端!"
本來吵吵嚷嚷的市場頓時冷卻了很多,畢竟誰也不敢違抗市場老大的命令,衆人只得悻悻而去,期待三天後的從新開市.
視角切換到市場監督大會上,主席首先開始發言:"各位,如今市場面臨的問題想必你們都有所耳聞吧,咱們已經鄭重承諾,三天後必須給市場一個答覆,時間緊,任務重,你們要集思廣益,一塊兒解決這個難題!"
"如今的顧客究竟是怎麼了,連本身到底想要什麼都搞不清楚,還急衝衝地跑來買寵物,本身都不知道要買啥,鬼才知道呢!",資歷老練的繼承經理抱怨道.
"經理說得對,他們本身都不知道到底想要啥寵物,怎麼能埋怨商家太羅裏吧嗦呢?人家那麼賣力介紹寵物的特色,不也是幫助顧客更好的選擇嘛!",發言的是繼承經理的小弟.
"..."
"咳咳,我理解你們的心情,繼承項目組確實在解決寵物問題上立下了很大功勞,你們爲他們抱不平也是情理之中的事情,過去的就讓他過去吧!當務之急,仍是要解決現實問題!",主席首先安撫前幾位激動情緒,又挑出重點提醒在場的各位迴歸到主題的討論上,不要再揪住過去的功勞簿.
"我以爲,心急顧客的真正需求只是想要一種寵物,而再也不關注寵物的種類,管他是貓是狗,只要是寵物就行.因此咱們應該提供一種新的機制,對外宣傳時只說這是寵物,至於這種寵物究竟是貓仍是狗,均可以!"
"貓和狗明明已是寵物了啊,難道不能夠直接賣給顧客嗎?爲啥還要提供新機制?"
"貓和狗雖然是寵物,但對於用戶來講,這種寵物有點浪費了,用戶實際使用到可能只是寵物的功能,並不會用到也不能用到具體寵物的功能,因此對用戶來講,這就是一種浪費."
"哦哦,明白了,這就像是顧客須要的寵物是能賣萌的,是要送給女友做爲禮物的,並不關心這個寵物能不能抓老鼠.因此對於抓老鼠的技能就是沒用的,而買家卻要爲抓老鼠的技能額外買單,這對於買家來講並不公平!"
通過一番激烈的討論,你們基本上達成一致,先前存在的繼承模型確實有些不足,不能適應快速變化的市場,過於強調差別性而非共性.
這樣就致使沒法知足急性子顧客批量購買的需求,因此須要提供相似於繼承那種抽象的概念來表達某種約定,只要知足這種約定的動物就是寵,無論是貓仍是狗,哪怕是玩具也行!
透過現象看本質,從紛繁冗雜的事務中抽象出精簡的模型是各個編程語言都必不可少的一個環節,Go
語言固然也不例外.
面向對象編程中的繼承概念表達是一種上下級的抽象關係,也就是說某一個封裝對象是從屬於特定上級的封裝對象,默認擁有該上級的行爲方法,這裏的上級概念就是父類就是對全部子類共性的抽象實現.
當研究的問題就是具體的子類實現時,此時使用繼承的概念,語義上比較清晰,子類只須要關注本身的特性,共性部分由父類去完成,這種思路也是很是天然的,貓是貓的同時也是一種寵物.
可是當咱們研究的問題再也不關注具體的子類實現而是着眼於父類的共性時,此時若是再提供具體的子類實現固然也能用,可是殺雞焉用牛刀?
明明我僅僅須要一滴水,你卻給了我整個海洋!
原本,真正須要的可能只是父類的某一個方法,你卻提供給我一個具體的子類實現,這個子類不但擁有目標方法還有不少的其餘方法.
既然有這麼多的附加價值,你說浪費不浪費,銷售時可不得漲價嗎,這樣不至關於捆綁銷售了嘛!
因此,咱們須要對繼承的概念進一步抽象,使這種抽象達到一種極致狀態以致於只存在很是少許的行爲方法,凡是繼承自這種極致抽象的子類都是它的子民.
爲了以後討論方便,業界將這種抽象到極致的繼承關係稱之爲接口,雖然看似只是稱呼的改變,但實際上思惟方式上已經發生了翻天覆地的變化.
繼承的概念是描述子類和父類的關係,子類繼承自父類,關注點在子類,共性部分徹底由父類實現,子類天然擁有這些行爲能力.
而接口的概念衍生於繼承,只不過是這種抽象程度已經達到了一種不能再抽象的地步,全部子類都要有一個最終的父類,這個父類擁有最公共性的行爲方法,因此這種極致的抽象也就沒法體現出子類的共性行爲的具體表現.此時這種極致的抽象沒有太大的意義,是一種很是很是寬泛的概念,等於什麼都沒說,因此也適合絕大部分封裝對象.
因此,乾脆取消了極致抽象中對於行爲共性的實現,轉而僅僅定義共性的行爲,具體這種行爲到底如何表現,徹底由具體子類自行決定.
這樣作有兩個顯而易見的好處,一是解決了太寬泛概念等於沒說的尷尬,同時保留了對共性行爲的描述.二是將控制權轉移到具體的子類實現,實現了體制內的個性化!
因此這種專業名詞的轉變背後是思惟方式的轉變,而接口更是很好地描述了這種轉變的語義.
回憶一下生活總隨處可見的 USB
數據線,對於計算機來講,對外暴露的是 USB
插口,行爲描述是隻要插入就能鏈接到電腦,可以同電腦進行溝通交流,這種交流多是傳遞數據,也多是鏈接電源等等不一樣的行爲表現.
基於接口設計,USB
數據線提供了訪問電腦的能力,一端連着電腦,另外一端連着手機,雙方進行數據交換. 有線鼠標的數據線也提供了訪問電腦的能力,實現鼠標的左擊仍是又擊都能反饋到電腦.
諸如此類的案例不勝枚舉,生活中不缺乏計算機哲學,缺乏的只是咱們的思考.
因此,若是讓我來給這種機制進行命名的話,我可能會將其稱呼爲插口,意思是隻要能適配指定的插口,那麼就說知足插口要求,對外暴露的抽象概念是插口,真正的實現多是數據線或者工具等.
固然,這只是個人一廂情願,由於面向對象中這種機制叫作接口,知足接口的規範叫作實現了接口.
接口這種概念顯得比較專業,提出這個概念的人估計也是厲害人物,基本上全部的面嚮對象語言中都採用了接口的概念,即便不是面嚮對象語言但支持面向對象編程風格的 Go
語言也採用了接口概念.
因而可知,接口的概念應該是通俗易懂,可移值性比較強的,得到了至關高的承認度.
除了面向對象編程風格外,與接口相關的編程風格中還有一種叫作面向接口編程,這個會在之後的文章中繼續分享這封面的內容.
我的理解封裝和繼承的概念,講的就是面向對象編程,關注點在於具體的對象以及對象之間的層次關係.
而接口的出現則是另一種維度的思考,當關注點再也不是具體的子類而是抽象的父類,這種狀況下則根據實際狀況抽象出了接口的概念,由此看出,面向對象編程中高內聚部分說的是封裝和繼承,低耦合則是接口和多態.
因此面向接口編程在應用而生,因而可知,不一樣的應用場景關注點不一樣,面向對象和麪向接口也並非互斥關係,是互補關係.
在將來的某種需求繼續發生改變時,可能還會產生新的概念,進而提出新的一套理論,到時候是面向需求編程仍是面向思惟編程亦或是面向搜索編程,那就就不得而知了.
聰明的讀者,大家有什麼見解呢?
市場監督大會散會後,繼承小組接受了設計接口的任務挑戰.大會之因此推舉繼承小組領頭,是由於與會人員一致認爲繼承小組在處理抽象概念上十分擅長,相信設計出接口這種機制也是能夠的.
繼承小組深感這次任務責任重大,任重而道遠,必定要設計出接口概念才能不辜負參會人員的承認和領導的厚愛.
因而,繼承小組內部在一塊兒開了個會,會上你們暢所欲言談談本身的見解.
小王: "我以爲這種接口的概念是抽象的終極狀態,咱們可能沒辦法一會兒到達終點,可是按照現有的理論應該能夠逐步逼近終點." 小李: "我也是有相似的感受,抽象到什麼程度纔是終點呢?拿什麼斷定這個抽象程度呢?貓和狗到寵物的過程是一種抽象過程,咱們先前也是基於此過程提出了繼承的概念,解決了重複定義的問題.如今應該沿着這種思路繼續抽象,直到小王說的那種接口概念." 小張: "從貓和狗抽象到寵物,是封裝對象的演進過程,顧客須要的不是具體的貓和狗,而是寵物.可是這個寵物直覺上感受和原來繼承中實現的寵物仍是有點不同啊?" 小王: "我也有同感,此次的寵物必須具有某種能力,只要是知足這種能力的,管他是貓仍是狗或者是別的什麼蜥蜴蟑螂的都是顧客眼中的寵物.因此這種寵物更加單一化,並不在意有沒有其餘能力."
...
你們你一言我一語的討論了好長時間,最終在項目經理的引導整理下有了有了初步的思路.
"等一下,我有疑問?你怎麼一會說須要,一會又說不須要,這不自相矛盾了嗎?",你們幾乎不約而同舉手示意經理.彷佛早就料到這幫小子搞不懂其中原因,經理故弄玄虛地迴應說:"嗯嗯,我就知道大家會有疑惑,下面容我談一下個人見解,大家聽聽看.」
若是站在接口的定義者角度上看問題,一旦發佈了接口規範,子類確定會屁顛屁顛知足接口約束,因而對外暴露時都是接口那一套理論,忽略了本身的特點.
統一了接口規範這種狀況對於接口設計者最爲方便,全部的控制權所有掌握在本身手中,一道命令便可號令羣雄,莫敢不從,如若不從,輕則千夫所指,重則驅逐出境!
對於接口設計者來講,這些實現了接口的對象並無什麼不一樣,地球離了誰照樣自轉,隨時隨地想換就換.
可是對於接口的實現類來講,只要一收到天子詔令,立馬無條件停下手上的活,熬夜加班也要知足新的接口規範,敢怒不敢言,除非是不想混了,哪怕怠慢了一步也會引起巨大的動盪!
因此說接口更改時,具體的實現類必需要隨之改變以實現新的接口規範約束.
若是站在接口的使用者角度上看問題,是否實現接口應該是個人地盤我作主,是自主決定的事情,管你接口是否更改,老子愛實現就實現,不樂意實現就不實現!你奈我何?個人王國我當家,尊你敬你你纔是國王,把咱們惹惱了,所謂的聯合王國到時候只剩你這麼一個孤家寡人去吧!
因此說接口更改時,具體的實現類不須要隨之更改,想不想知足新的接口規範徹底在於本身,並非強迫的,沒必要當即實現新的接口規範.
真的是公說公有理婆說婆有理,既然如此,那麼問題來了,Go
語言選擇是哪種?其餘主流的編程語言又是選擇哪種的呢?
先說其餘主流的編程語言,這類編程語言大可能是站在接口設計者角度出發,控制慾特別強,一言不合就報錯,接口更新了實現類必須同步更新,違令者殺無赦!
這樣有優勢也有缺點,優勢是皇帝一聲令下,天下臣民莫敢不從,屢教不敢者,千夫所指,王國崩潰也不是沒有可能! 正是這種優勢,換另一種角度看就是缺點了,俗話說天高皇帝遠,聖旨雖下但還沒傳達到邊境要塞,那邊監察御史就上奏你一本,說你怠政目無尊上,引起帝國動盪,罪大惡極,理應凌遲處死!
你說冤不冤,無論是朝令夕改仍是面目一新的改革,凡是曾經實現過接口的類都要實時更新,不然後果不堪設想.
真的是成也蕭何敗蕭何,控制慾太強有利有弊.
因此,Go
不同凡響,選擇了另外一種思路來解決問題,放棄中央集權轉向分封制,將權力下放給地方.
名義上仍是由國王制定統一標準,由地方負責自主實施,具體如何實現標準徹底是諸侯國本身的事情,萬一哪天國王須要使用統一標準時,實現了該標準的諸侯王國均可以無障礙使用.
即便之後接口規範有變,舊的接口再也不適合新時代要求,國王只須要制定了一套新的標準,昭告天下後,當詔令傳到地方時,地方能夠根據新的規範更新本身的實現類,萬一消息閉塞或者不肯意當即更新,也不要緊,王國不會崩潰,只不過須要使用新規範時,沒有實現接口的地方天然是不能使用的.
所以,不管是集中制仍是民主制,接口的規範都是自頂向下實施的,不一樣之處在於底下的人因各類緣由沒有實現新的接口規範時,集中制會直接崩潰而民主制依舊正常運行,僅此而已.
下面就演示一下兩種思路的實現方式.
java
等傳統的面嚮對象語言賣家首先定義到底什麼是寵物這種接口.
public interface Pet {
void actingCute();
}
複製代碼
喵喵喵,人家能賣萌,就是寵物嘛,爲啥還非得證實一下啊!
public static class Cat implements Pet {
@Override
public void actingCute() {
System.out.println("喵星人喵喵喵來賣萌");
}
}
複製代碼
這裏說的證實一下貓是寵物,指的是必須使用關鍵字
implements
聲明實現了寵物的接口,頒發了合格證纔算是寵物,不然就不是.
汪星人說,這年頭自帶賣萌天賦的貓咪都要經過專業認證纔算是寵物,我也乖乖去認證寵物吧!
public static class Dog implements Pet {
@Override
public void actingCute() {
System.out.println("汪星人汪汪汪來賣萌");
}
}
複製代碼
次日,市場上又來了一羣急性子的買家,一上來就要買寵物,管他是貓仍是狗,並不在意,只要是寵物就行.
public static void main(String[] args) {
Pet p;
p = new Cat();
// 喵星人喵喵喵來賣萌
p.actingCute();
p = new Dog();
// 汪星人汪汪汪來賣萌
p.actingCute();
}
複製代碼
終於送走了這批顧客,賣家也舒了一口氣,默默唸叨着,市場監督那幫人真牛逼,居然設計出接口的方案,只要是寵物,別管是貓仍是狗,隨便給一個都行,給這幫人點個贊!
go
等非傳統非面嚮對象語言首先定義接口規範,寵物必定要能賣萌,否則怎麼討得女神歡心?
type Pet interface {
ActingCute()
}
複製代碼
喵喵喵說我會賣萌啊,那我就是寵物啦!
type Cat struct {
}
func (c *Cat) ActingCute() {
fmt.Println("喵星人喵喵喵來賣萌")
}
複製代碼
汪汪汪說我也會賣萌,我也要給女神當寵物!
type Dog struct {
}
func (d *Dog) ActingCute() {
fmt.Println("汪星人汪汪汪來賣萌")
}
複製代碼
既然大家都會賣萌,對於直男來講這就夠了,隨便拿一個就好了,快點準備送禮物啦!
func SendGift(p Pet) {
p.ActingCute()
}
複製代碼
因而乎,既然買家並不在意究竟是貓仍是狗,那就賣給他一個貓好了,因而小夥子打包了寵物準備送給女神.
func TestActingCute(t *testing.T) {
var p Pet
p = new(Cat)
// 喵星人喵喵喵來賣萌
SendGift(p)
}
複製代碼
次日,女神打來電話說,你不知道對貓毛過敏的嗎,送的啥破禮物!哼!
可憐的小夥子跑去寵物店找賣家算帳,氣沖沖地質問賣家,賣家一臉滿不在乎的樣子,笑嘻嘻的說,小夥子想不想將功補過啊,這一次保準你能得到女神青睞.
只見,賣家這一次找來了一條寵物狗,打打包還放到原來的包裝盒遞給你小夥子.
func TestActingCute(t *testing.T) {
var p Pet
p = new(Dog)
// 汪星人汪汪汪來賣萌
SendGift(p)
}
複製代碼
我擦,仍是原來的配方,有點擔憂,同樣的包裝這一次真的能討得女神歡心,原諒本身嗎?
親愛的讀者,大家說呢,一樣的配方不同的味道,女神會原諒本身嗎?
不管是站在設計者角度上解決抽象問題仍是站在使用者角度思考,二者的解決方案沒有高低優劣之分,選用好恰當的應用場景就是最好的解決方案.
只不過這種選擇每每不是開發者能左右的事情,由於這種底層的語言級別框架設計屬於締造者的工做,他們一旦以爲了一種模式,語言使用者很難改變,咱們惟一能作的就是理解並使用罷了!
當站在接口設計者角度上時,接口的定義和具體實現類的關係就比如是集中制,皇帝一聲令下,無論身處何處,天下臣民皆唯命是從,若有懶政懈怠者,千夫所指,立馬崩潰.
當站在接口實現者角度上時,此時接口的設計者和具體實現者的關係是鬆耦合的,猶如分封制,國王一聲令下,諸侯國能夠遵從差遣也能夠抗旨不遵,對於整個王國而言並不會形成顛覆性混亂,諸侯國和國王更像是一種契約精神而不是隸屬服從關係.
Go
語言中的接口採用的就是後一種鬆耦合的關係,接口設計者和接口實現者是鬆耦合的,實現的關係也是隱式的,這也是另外一種理論"鴨子模型"的體現.
好了,本文主要介紹了爲何要有接口設計的需求以及接口設計是怎麼思考的,並簡單介紹了 Go
是如何實現這種模型的.
下一節咱們將真正開始介紹 Go
語言關於接口的設計,順便講解面向對象最後一個知識點---多態.
若是本文對你理解面向對象有所幫助,歡迎你的的轉發分享,若是文章有描述不當之處,但願你能留言告訴我,謝謝你的閱讀.