天天看點

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

簡介: 若想捉大魚,就得潛入深淵。深淵裡的魚更有力,也更純淨。碩大而抽象,且非常美麗。

作者:張建飛

若想捉大魚,就得潛入深淵。深淵裡的魚更有力,也更純淨。碩大而抽象,且非常美麗。——大衛·林奇

前言

抽象思維是我們工程師最重要的思維能力。因為軟體技術 本質上就是一門抽象的藝術。我們的工作是存思維的“遊戲”,雖然我們在使用鍵盤、顯示器,打開電腦可以看到主機闆、硬碟等硬體。但我們即看不到程式如何被執行,也看不到 0101 是如何被 CPU 處理的。

我們工程師每天都要動用抽象思維,對問題域進行分析、歸納、綜合、判斷、推理。進而抽象出各種概念,挖掘概念和概念之間的關系,對問題域進行模組化,然後通過程式設計語言實作業務功能。是以,我們大部分的時間并不是在寫代碼,而是在梳理需求,理清概念。當然,也包括嘗試看懂那些“該死的、别人寫的”代碼。

在我接觸的工程師中,能深入了解抽象概念的并不多,能把抽象和面向對象、架構設計進行有機結合,能用抽象思維進行問題分析、化繁為簡的同學更是鳳毛麟角。

對于我本人而言,每當我對抽象有進一步的了解和認知,我都能切身感受到它給我在編碼和設計上帶來的質的變化。同時,感慨之前對抽象的了解為什麼如此膚淺。如果時間可以倒流的話,我希望我在我職業生涯的早期,就能充分意識到抽象的重要性,能多花時間認真的研究它,深刻的了解它,這樣應該可以少走很多彎路。

什麼是抽象

關于抽象的定義,百度百科是這樣說的:

抽象是從衆多的事物中抽取出共同的、本質性的特征,而舍棄其非本質的特征的過程。具體地說,抽象就是人們在實踐的基礎上,對于豐富的感性材料通過去粗取精、去僞存真、由此及彼、由表及裡的加工制作,形成概念、判斷、推理等思維形式,以反映事物的本質和規律的方法。實際上,抽象是與具體相對應的概念,具體是事物的多種屬性的總和,因而抽象亦可了解為由具體事物的多種屬性中舍棄了若幹屬性而固定了另一些屬性的思維活動。[1]

Wikipedia 的解釋是:

抽象是指為了某種目的,對一個概念或一種現象包含的資訊進行過濾,移除不相關的資訊,隻保留與某種最終目的相關的資訊。例如,一個皮質的足球,我們可以過濾它的質料等資訊,得到更一般性的概念,也就是球。從另外一個角度看,抽象就是簡化事物,抓住事物本質的過程。[2]

簡單而言,“抽”就是抽離,“象”就是具象,字面上了解抽象,抽象的過程就是從“具象”事物中歸納出共同特征,“抽取”得到一般化(Generalization)的概念的過程。英文的抽象——abstract 來自拉丁文 abstractio,它的原意是排除、抽出。

為了更好的友善你了解抽象,讓我們先來看一幅畢加索的畫,如下圖所示,圖的左邊是一頭水牛,是具象的;右邊是畢加索畫,是抽象的。怎麼樣,是不是感覺自己一下子了解了抽象畫的含義。

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

可以看到,抽象牛隻有幾根線條,不過這幾根線條是做了高度抽象之後的線條,過濾了水牛的絕大部分細節,保留了牛最本質特征,比如牛角,牛頭,牛鞭、牛尾巴等等。這種對細節的舍棄使得“抽象牛”具有更好的泛化(Generalization)能力。

可以說,抽象更接近問題的本質,也就是說所有的牛都逃不過這幾根線條。

抽象和語言是一體的

關于抽象思維,我們在百度百科上可以看到如下的定義:

抽象思維,又稱詞(概念)的思維或者邏輯思維,是指用詞(概念)進行判斷、推理并得出結論的過程。抽象思維以詞(概念)為中介來反映現實。這是思維的最本質特征,也是人的思維和動物心理的根本差別。[3]

之是以把抽象思維稱為詞思維或者概念思維,是因為語言和抽象是一體的。當我們說“牛”的時候,說的就是“牛”的抽象,他代表了所有牛共有的特征。同樣,當你在程式中建立 Cow 這個類的時候,道理也是一樣。在生活中,我們隻見過一頭一頭具象的牛,“牛”作為抽象的存在,即看不見也摸不着。

這種把抽象概念作為世界本真的看法,也是古希臘哲學家柏拉圖的最重要哲學思想。柏拉圖認為,我們所有用感覺感覺到的事物,都源于相應的理念。他認為具體事物的“名”,也就是他說的“理念世界”才是本真的東西,具體的一頭牛,有大有小,有公有母,顔色、性情、外形各自不同。是以我們不好用個體感覺加以概括,但是這些牛既然都被統稱為“牛”,則說明它們必然都源于同一個“理念”,即所謂“牛的理念”或者“理念的牛”,是以它們可以用“牛”加以概括。尚且不論“理念世界”是否真的存在,這是一個哲學問題,但有一點可以确定,我們的思考和對概念的表達都離不開語言。[4]

這也是為什麼,我在做設計和代碼審查(Code Review)的時候,會特别關注命名是否合理的原因。因為命名的好壞,在很大程度上反映了我們對一個概念的思考是否清晰,我們的抽象是否合理,反應在代碼上就是,代碼的可讀性、可了解性是不是良好,以及我們的設計是不是到位。

有人做過一個調查,問程式員最頭痛的事情是什麼,通過 Quora 和 Ubuntu Forum 的調查結果顯示,程式員最頭疼的事情是命名。如果你曾經為了一個命名而絞盡腦汁,就不會對這個結果感到意外。

就像 Stack Overflow 的創始人 Joel Spolsky 所說的:“起一個好名字應該很難,因為,一個好名字需要把要義濃縮在一到兩個詞。(Creating good names is hard, but it should be hard, because a great name captures essential meaning in just one or two words)。”

是的,這個濃縮的過程就是抽象的過程。我不止一次的發現,當我覺得一個地方的命名有些别扭的時候,往往就意味着要麼這個地方我沒有思考清楚,要麼是我的抽象弄錯了。

關于如何命名,我在《代碼精進之路》裡已經有比較詳盡的闡述,這裡就不贅述了。

我想強調的是,語言是明晰概念的基礎,也是抽象思維的基礎,在建構一個系統時,值得我們花很多時間去斟酌、去推敲語言。在我做過的一個項目中,就曾為一個關鍵實體讨論了兩天,因為那是一個新概念,嘗試了很多名字,始終感覺到别扭、不好了解。随着我們讨論的深入,對問題域了解的深入,我們最終找到了一個相對比較合适的名字,才肯罷休。

這樣的斟酌是有意義的,因為明晰關鍵概念,是我們設計中的重要工作。雖然不合理的命名、不合理的抽象也能實作業務功能。但其代價就是維護系統時需要極高的認知負荷。随着時間的推移,就沒人能搞懂系統的設計了。

抽象的層次性

回到畢加索的抽象畫,如下圖所示,如果映射到面向對象程式設計,抽象牛就是抽象類(Abstract Class),代表了所有牛的抽象。抽象牛可以泛化成更多的牛,比如水牛、奶牛、牦牛等。每一種牛都代表了一類(Class)牛,對于每一類牛,我們可以通過執行個體化,得到一頭具體的牛執行個體(Instance)。

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

從這個簡單的案例中,我們可以到抽象的三個特點:

1. 抽象是忽略細節的。抽象類是最抽象的,忽略的細節也最多,就像抽象牛,隻是幾根線條而已。在代碼中,這種抽象可以是 Abstract Class,也可以是 Interface。

2. 抽象代表了共同性質。類(Class)代表了一組執行個體(Instance)的共同性質,抽象類(Abstract Class)代表了一組類的共同性質。對于我們上面的案例來說,這些共同性質就是抽象牛的那幾根線條。

3. 抽象具有層次性。抽象層次越高,内涵越小,外延越大,也就是說它的涵義越小,泛化能力越強。比如,牛就要比水牛更抽象,因為它可以表達所有的牛,水牛隻是牛的一個種類(Class)。

抽象的這種層次性,是除了抽象概念之外,另一個我們必須要深入了解的概念,因為小到一個方法要怎麼寫,大到 一個系統要如何架構,以及我們後面第三章要介紹的結構化思維,都離不開抽象層次的概念。

在進一步介紹抽象層次之前,我們先來了解一下外延和内涵的意思:

抽象是以概念(詞語)來反映現實的過程,每一個概念都有一定的外延和内涵。概念的外延就是适合這個概念的一切對象的範圍,而概念的内涵就是這個概念所反映的對象的本質屬性的總和。例如“平行四邊形”這個概念,它的外延包含着一切正方形、菱形、矩形以及一般的平行四邊形,而它的内涵包含着一切平行四邊形所共有的“有四條邊,兩組對邊互相平行”這兩個本質屬性。

一個概念的内涵愈廣,則其外延愈狹;反之,内涵愈狹,則其外延愈廣。例如,“平行四邊形”的内涵是“有四條邊,兩組對邊互相平行”,而“菱形”的内涵除了這兩條本質屬性外,還包含着“四邊相等”這一本質屬性。“菱形”的内涵比“平行四邊形”的内涵廣,而“菱形”的外延要比“平行四邊形”的外延狹。

所謂的抽象層次就展現在概念的外延和内涵上,這種層次性,基本可以展現在任何事物上,比如一份報紙就存在多個層次上的抽象,“出版品”最抽象,其内涵最小,但外延最大,“出版品”可以是報紙也可以是期刊雜志等。

  1. 一個出版品
  2. 一份報紙
  3. 《舊金山紀事報》
  4. 5 月 18 日的《舊金山紀事報》

當我要統計美國有多少個出版品,那麼就要用到最上面第一層“出版品”的抽象,如果我要查詢舊金山 5月18日當天的新聞,就要用到最下面第四層的抽象。

每一個抽象層次都有它的用途,對于我們工程師來說,如何拿捏這個抽象層次是對我們設計能力的考驗,抽象層次太高和太低都不行。

比如,現在要寫一個水果程式,我們需要對水果進行抽象,因為水果裡面有紅色的蘋果,我們當然可以建一個 RedApple 的類,但是這個抽象層次有點低,隻能用來表達“紅色的蘋果”。來一個綠色的蘋果,你還得建立一個 GreenApple 類。

為了提升抽象層次,我們可以把 RedApple 類改成 Apple 類,讓顔色變成 Apple 的屬性,這樣紅色和綠色的蘋果就都能表達了。再繼續往上抽象,我們還可以得到水果類、植物類等。再往上抽象就是生物、物質了。

你可以看到,抽象層次越高,内涵越小,外延越大,泛化能力越強。然而,其代價就是業務語義表達能力越弱。

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

具體要抽象到哪個層次,要視具體的情況而定了,比如這個程式是專門研究蘋果的可能到 Apple 就夠了,如果是賣水果的可能需要到 Fruit,如果是植物研究的可能要到 Plant,但很少需要到 Object。

我經常開玩笑說,你把所有的類都叫 Object,把所有的參數都叫 Map 的系統最通用,因為 Object 和 Map 的内涵最小,其延展性最強,可以适配所有的擴充。從原理上來說,這種抽象也是對的,萬物皆對象嘛。但是這種抽象又有什麼意義呢?它沒有表達出任何想表達的東西,隻是一句正确的廢話而已。

越抽象,越通用,可擴充性越強,然而其語義的表達能力越弱。越具體,越不好延展,然而其語義表達能力很強。是以,對于抽象層次的權衡,是我們系統設計的關鍵所在,也是區分普通程式員和優秀程式員的關鍵所在。

軟體中的分層抽象無處不在

越是複雜的問題越需要分層抽象,分層是分而治之,抽象是問題域的合理劃分和概念語義的表達。不同層次提供不同的抽象,下層對上層隐藏實作細節,通過這種層次結構,我們才有可能應對像網絡通信、雲計算等超級複雜的問題。

網絡通信是網際網路最重要的基礎實施,但同時它又是一個很複雜的過程,你要知道把資料包傳給誰——IP協定,你要知道在這個不可靠的網絡上出現狀況要怎麼辦——TCP 協定。有這麼多的事情需要處理,我們可不可以在一個層次中都做掉呢?當然是可以的,但顯然不科學。是以,ISO制定了網絡通信的七層參考模型,每一層隻處理一件事情,低層為上層提供服務,直到應用層把HTTP、FTP等友善了解和使用的協定暴露給使用者。

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

程式設計語言的發展史也是一個典型的分層抽象的演化史。

機器能了解的隻有機器語言,即各種二進制的 01 指令。如果我們采用 01 的輸入方式,其程式設計效率極低(學過數字電路的同學,體會下用開關實作加減法)。是以我們用彙編語言抽象了二進制指令。

然而彙編還是很底層,于是我們用 C 語言抽象了彙編語言。而進階語言 Java 是類似于 C 這樣低級語言的進一步抽象,這種逐層抽象極大的提升了我們的程式設計效率。

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

重複代碼是抽象的缺失

如果說抽象的本質是共性的話,那麼我們代碼中的重複代碼,是不是就意味着抽象的缺失呢?

//取預設搜尋條件
List<String> defaultConditions = searchConditionCacheTunnel.getJsonQueryByLabelKey(labelKey);
for(String jsonQuery : defaultConditions){
  jsonQuery = jsonQuery.replaceAll(SearchConstants.SEARCH_DEFAULT_PUBLICSEA_ENABLE_TIME, String.valueOf(System.currentTimeMillis() / 1000));
  jsonQueryList.add(jsonQuery);
}
//取主搜尋框的搜尋條件
if(StringUtils.isNotEmpty(cmd.getContent())){
    List<String> jsonValues = searchConditionCacheTunnel.getJsonQueryByLabelKey(SearchConstants.ICBU_SALES_MAIN_SEARCH);
    for (String value : jsonValues) {
    String content = StringUtil.transferQuotation(cmd.getContent());
    value = StringUtil.replaceAll(value, SearchConstants.SEARCH_DEFAULT_MAIN, content);
      jsonQueryList.add(value);
  }
}      

是這樣的,重複代碼是典型的代碼壞味道,其本質問題就是抽象的缺失。因為我們 Ctrl+C 加 Ctrl+V 的工作習慣,導緻沒有對共性代碼進行抽取;或者雖然抽取了,隻是簡單的用了一個 Util 名字,沒有給到一個合适的名字,沒有正确的反應這段代碼所展現的抽象概念,都屬于抽象不到位。

有一次,我在 Review 團隊代碼的時候,發現有一段組裝搜尋條件的代碼,在幾十個地方都有重複。這個搜尋條件還比較複雜,是以中繼資料的形式存在資料庫中,是以組裝的過程是這樣的:

  • 首先,我們要從緩存中把搜尋條件清單取出來;
  • 然後,周遊這些條件,将搜尋的值填充進去;

簡單的重構無外乎就是把這段代碼提取出來,放到一個Util類裡面給大家複用。然而我認為這樣的重構隻是完成了工作的一半,我們隻是做了簡單的歸類,并沒有做抽象提煉。

簡單分析,不難發現,此處我們是缺失了兩個概念:一個是用來表達搜尋條件的類——SearchCondition;另一個是用來組裝搜尋條件的類——SearchConditionAssembler。隻有配合命名,顯性化的将這兩個概念表達出來,才是一個完整的重構。

重構後,搜尋條件的組裝會變成一種非常簡潔的形式,幾十處的複用隻需要引用SearchConditionAssembler就好了。

public class SearchConditionAssembler {
    public static SearchCondition assemble(String labelKey){
        String jsonSearchCondition =  getJsonSearchConditionFromCache(labelKey);
        SearchCondition sc = assembleSearchCondition(jsonSearchCondition);
        return sc;
    }
}      

由此可見,提取重複代碼隻是我們重構工作的第一步。對重複代碼進行概念抽象,尋找有意義的命名才是我們工作的重點。

是以,每一次遇到重複代碼的時候,你都應該感到興奮,想着這是一次鍛煉抽象能力的絕佳機會,當然,測試代碼除外。

強制類型轉換是抽象層次有問題

面向對象設計裡面有一個著名的 SOLID 原則是由 Bob 大叔(Robert Martin)提出來的,其中的 L 代表 LSP,就是 Liskov Substitution Principle(裡氏替換原則)。簡單來說,裡氏替換原則就是子類應該可以替換任何父類能夠出現的地方,并且經過替換以後,代碼還能正常工作。

思考一下,我們在寫代碼的過程中,什麼時候會用到強制類型轉換呢?當然是 LSP 不能被滿足的時候,也就是說子類的方法超出了父類的類型定義範圍,為了能使用到子類的方法,隻能使用類型強制轉換将類型轉成子類類型。

舉個例子,在蘋果(Apple)類上,有一個 isSweet() 方法是用來判斷水果甜不甜的;西瓜(Watermelon)類上,有一個 isJuicy() 是來判斷水分是否充足的;同時,它們都共同繼承一個水果(Fruit)類。

此時,我們需要挑選出甜的水果和有水分的習慣,我們會寫一個如下的程式:

public class FruitPicker {
​
    public List<Fruit> pickGood(List<Fruit> fruits){
        return fruits.stream().filter(e -> check(e)).
                collect(Collectors.toList());
    }
​
    private boolean check(Fruit e) {
        if(e instanceof Apple){
            if(((Apple) e).isSweet()){
                return true;
            }
        }
        if(e instanceof Watermelon){
            if(((Watermelon) e).isJuicy()){
                return true;
            }
        }
        return false;
    }
}      

因為pick方法的入參的類型是 Fruit,是以為了獲得 Apple 和 Watermelon 上的特有方法,我們不得不使用 instanceof 做一個類型判斷,然後使用強制類型轉換轉成子類類型,以便獲得他們的專有方法,很顯然,這是違背了裡式替換原則的。

這裡問題出在哪裡?對于這樣的代碼我們要如何去優化呢?仔細分析一下,我們可以發現,根本原因是因為 isSweet 和 isJuicy 的抽象層次不夠,站在更高抽象層次也就是 Fruit 的視角看,我們挑選的就是可口的水果,隻是具體到蘋果我們看甜度,具體到西瓜我們看水分而已。

是以,解決方法就是對 isSweet 和 isJuicy 進行抽象,并提升一個層次,在 Fruit 上建立一個 isTasty() 的抽象方法,然後讓蘋果和西瓜類分别去實作這個抽象方法就好了。

當程式員具備了抽象思維前言什麼是抽象抽象和語言是一體的抽象的層次性軟體中的分層抽象無處不在重複代碼是抽象的缺失強制類型轉換是抽象層次有問題如何提升抽象思維能力小結

下面是重構後的代碼,通過抽象層次的提升我們消除了 instanceof 判斷和強制類型轉換,讓代碼重新滿足了裡式替換原則。抽象層次的提升使得代碼重新變得優雅了。

public class FruitPicker {    
public List<Fruit> pickGood(List<Fruit> fruits){        
return fruits.stream().filter(e -> check(e)).                collect(Collectors.toList());    }    
//不再需要instanceof和強制類型轉換    p
rivate boolean check(Fruit e) {        
return e.isTasty();    }}      

是以,每當我們在程式中準備使用 instanceof 做類型判斷,或者用 cast 做強制類型轉換的時候。每當我們的程式不滿足 LSP 的時候。你都應該警醒一下,好家夥,這又是一次鍛煉抽象能力的絕佳機會。

如何提升抽象思維能力

抽象思維能力是我們人類特有的、與生俱來的能力,除了上面說的在編碼過程中可以鍛煉抽象能力之外,我們還可以通過一些其他的練習,不斷的提升我們的抽象能力。

多閱讀

為什麼閱讀書籍比看電視更好呢?因為圖像比文字更加具象,閱讀的過程可以鍛煉我們的抽象能力、想象能力,而看畫面的時候會将你的大腦鋪滿,較少需要抽象和想象。

這也是為什麼我們不提倡讓小孩子過多的暴露在電視或手機螢幕前的原因,因為這樣不利于他抽象思維的鍛煉。

抽象思維的差别讓孩子們的學習成績從國中開始分化,許多不能适應這種抽象層面訓練的,就去讀技校了,因為技校比大學會更加具象:車銑刨磨、零部件都能看得見摸得着。體力勞動要比腦力勞動來的簡單。

多總結沉澱

小時候不了解,國文老師為什麼總是要求我們總結段落大意、中心思想什麼的。現在回想起來,這種思維訓練在基礎教育中是非常必要的,其實質就是幫助學生提升抽象思維能力。

記錄也是很好的總結習慣。就拿讀書筆記來說,最好不要原文摘錄書中的内容,而是要用自己的話總結歸納書中的内容,這樣不僅可以加深了解,而且還可以提升自己的抽象思維能力。

我從四年前開始系統的記錄筆記,做總結沉澱,建構自己的知識體系。這種思維訓練的好處顯而易見,可以說我之前寫的《從碼農到工匠》和現在正在寫的《程式員必備的思維能力》都離不開我總結沉澱的習慣。

命名訓練

每一次的變量命名、方法命名、類命名都是一次難得的抽象思維訓練機會,前面已經說過了,語言和抽象是一體的,命名的好壞直接反應了我們的問題域思考的是否清晰,反映了我們抽象的是否合理。

現實情況是,我們很多的工程師常常忽略了命名的重要性,隻要能實作業務功能,名字從來就不是重點。

實際上,這是對系統的不負責任,也是對自己的不負責任,更是對後期維護系統的人不負責任。寫程式和寫文章有很大的相似性,本質上都是在用語言闡述一件事情。試想下,如果文章中用的都是些詞不達意的句子,這樣的文章誰能看得懂,誰又願意去看呢。

同樣,我一直強調代碼要顯性化的表達業務語義,其中命名在這個過程中扮演了極其重要的角色。為了代碼的可讀性,為了系統的長期可維護性,為了我們自身抽象思維的訓練,我們都不應該放過任何一個帶有歧義、表達模糊、意不清的命名。

領域模組化訓練

對于技術同學,我們還有一個非常好的提升抽象能力的手段——領域模組化。當我們對問題域進行分析、整理和抽象的時候,當我們對領域進行劃分和模組化的時候,實際上也是在鍛煉我們的抽象能力。

我們可以對自己工作中的問題域進行模組化,當然也可以通過閱讀一些優秀源碼背後的模型設計來學習如何抽象、如何模組化。比如,我們知道 Spring 的核心功能是 Bean 容器,那麼在看Spring源碼的時候,我們可以着重去看它是如何進行Bean管理的?它使用的核心抽象是什麼?不難發現,Spring 是使用了 BeanDefinition、BeanFactory、BeanDefinitionRegistry、BeanDefinitionReader 等核心抽象實作了 Bean 的定義、擷取和建立。抓住了這些核心抽象,我們就抓住了 Spring 設計主脈。

除此之外,我們還可以進一步深入思考,它為什麼要這麼抽象?這樣抽象的好處是什麼?以及它是如何支援 XML 和 Annotation(注解)這兩種關于 Bean 的定義的。

這樣的抽象思維鍛煉和思考,對提升我們的抽象能力和模組化能力非常重要。關于這一點,我深有感觸,初入職場的時候,當我嘗試對問題域進行抽象和模組化的時候,會覺得無從下手,建出來的模型也感覺很别扭。

然而,經過長期的、刻意的學習和鍛煉之後,很明顯可以感覺到我的模組化能力和抽象能力都有很大的提升。不但分析問題的速度更快了,而且建出來的模型也更加優雅了。

小結

  • 抽象思維是程式員最重要的思維能力,抽象的過程就是尋找共性、歸納總結、綜合分析,提煉出相關概念的過程。
  • 語言和抽象是一體的,抽象思維也叫詞思維,因為抽象的概念隻能通過語言才能表達出來。
  • 抽象是有層次性的,抽象層次越高,内涵越小,外延越大,擴充性越好;反之,抽象層次越低,内涵越大,外延越小,擴充性越差,但語義表達能力越強。
  • 對抽象層次的拿捏,展現了我們的設計功力,視具體情況而定,抽象層次既不能太高,也不能太低。
  • 重複代碼意味着抽象缺失,強制類型轉換意味着抽象層次有問題,我們可以利用這些信号來重構代碼,讓代碼重新變的優雅。
  • 我們可以通過刻意練習來提升抽象能力,這些練習包括閱讀、總結、命名訓練、模組化訓練等。

原文連結

本文為阿裡雲原創内容,未經允許不得轉載。

繼續閱讀