<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          當(dāng)程序員具備了抽象思維

          共 10561字,需瀏覽 22分鐘

           ·

          2021-03-16 14:28


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

          可以看到,抽象牛只有幾根線條,不過這幾根線條是做了高度抽象之后的線條,過濾了水牛的絕大部分細(xì)節(jié),保留了牛最本質(zhì)特征,比如牛角,牛頭,牛鞭、牛尾巴等等。這種對細(xì)節(jié)的舍棄使得“抽象?!本哂懈玫姆夯℅eneralization)能力。

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

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

          我們工程師每天都要動用抽象思維,對問題域進(jìn)行分析、歸納、綜合、判斷、推理。從而抽象出各種概念,挖掘概念和概念之間的關(guān)系,對問題域進(jìn)行建模,然后通過編程語言實現(xiàn)業(yè)務(wù)功能。所以,我們大部分的時間并不是在寫代碼,而是在梳理需求,理清概念。當(dāng)然,也包括嘗試看懂那些“該死的、別人寫的”代碼。

          在我接觸的工程師中,能深入理解抽象概念的并不多,能把抽象和面向?qū)ο?、架?gòu)設(shè)計進(jìn)行有機(jī)結(jié)合,能用抽象思維進(jìn)行問題分析、化繁為簡的同學(xué)更是鳳毛麟角。

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


          什么是抽象



          關(guān)于抽象的定義,百度百科是這樣說的:
          抽象是從眾多的事物中抽取出共同的、本質(zhì)性的特征,而舍棄其非本質(zhì)的特征的過程。具體地說,抽象就是人們在實踐的基礎(chǔ)上,對于豐富的感性材料通過去粗取精、去偽存真、由此及彼、由表及里的加工制作,形成概念、判斷、推理等思維形式,以反映事物的本質(zhì)和規(guī)律的方法。

          實際上,抽象是與具體相對應(yīng)的概念,具體是事物的多種屬性的總和,因而抽象亦可理解為由具體事物的多種屬性中舍棄了若干屬性而固定了另一些屬性的思維活動。[1] 

          Wikipedia 的解釋是:
          抽象是指為了某種目的,對一個概念或一種現(xiàn)象包含的信息進(jìn)行過濾,移除不相關(guān)的信息,只保留與某種最終目的相關(guān)的信息。例如,一個皮質(zhì)的足球,我們可以過濾它的質(zhì)料等信息,得到更一般性的概念,也就是球。從另外一個角度看,抽象就是簡化事物,抓住事物本質(zhì)的過程。[2]

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




          抽象和語言是一體的



          關(guān)于抽象思維,我們在百度百科上可以看到如下的定義:
          抽象思維,又稱詞(概念)的思維或者邏輯思維,是指用詞(概念)進(jìn)行判斷、推理并得出結(jié)論的過程。抽象思維以詞(概念)為中介來反映現(xiàn)實。這是思維的最本質(zhì)特征,也是人的思維和動物心理的根本區(qū)別。[3]

          之所以把抽象思維稱為詞思維或者概念思維,是因為語言和抽象是一體的。當(dāng)我們說“牛”的時候,說的就是“?!钡某橄?,他代表了所有牛共有的特征。同樣,當(dāng)你在程序中創(chuàng)建 Cow 這個類的時候,道理也是一樣。在生活中,我們只見過一頭一頭具象的牛,“?!弊鳛槌橄蟮拇嬖?,即看不見也摸不著。

          這種把抽象概念作為世界本真的看法,也是古希臘哲學(xué)家柏拉圖的最重要哲學(xué)思想。柏拉圖認(rèn)為,我們所有用感覺感知到的事物,都源于相應(yīng)的理念。他認(rèn)為具體事物的“名”,也就是他說的“理念世界”才是本真的東西,具體的一頭牛,有大有小,有公有母,顏色、性情、外形各自不同。因此我們不好用個體感覺加以概括,但是這些牛既然都被統(tǒng)稱為“?!?,則說明它們必然都源于同一個“理念”,即所謂“牛的理念”或者“理念的?!?,所以它們可以用“?!奔右愿爬āI星也徽摗袄砟钍澜纭笔欠裾娴拇嬖?,這是一個哲學(xué)問題,但有一點可以確定,我們的思考和對概念的表達(dá)都離不開語言。[4]

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

          有人做過一個調(diào)查,問程序員最頭痛的事情是什么,通過 Quora 和 Ubuntu Forum 的調(diào)查結(jié)果顯示,程序員最頭疼的事情是命名。如果你曾經(jīng)為了一個命名而絞盡腦汁,就不會對這個結(jié)果感到意外。

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

          是的,這個濃縮的過程就是抽象的過程。我不止一次的發(fā)現(xiàn),當(dāng)我覺得一個地方的命名有些別扭的時候,往往就意味著要么這個地方我沒有思考清楚,要么是我的抽象弄錯了。

          關(guān)于如何命名,我在《代碼精進(jìn)之路》里已經(jīng)有比較詳盡的闡述,這里就不贅述了。

          我想強(qiáng)調(diào)的是,語言是明晰概念的基礎(chǔ),也是抽象思維的基礎(chǔ),在構(gòu)建一個系統(tǒng)時,值得我們花很多時間去斟酌、去推敲語言。在我做過的一個項目中,就曾為一個關(guān)鍵實體討論了兩天,因為那是一個新概念,嘗試了很多名字,始終感覺到別扭、不好理解。隨著我們討論的深入,對問題域理解的深入,我們最終找到了一個相對比較合適的名字,才肯罷休。

          這樣的斟酌是有意義的,因為明晰關(guān)鍵概念,是我們設(shè)計中的重要工作。雖然不合理的命名、不合理的抽象也能實現(xiàn)業(yè)務(wù)功能。但其代價就是維護(hù)系統(tǒng)時需要極高的認(rèn)知負(fù)荷。隨著時間的推移,就沒人能搞懂系統(tǒng)的設(shè)計了。


          抽象的層次性



          回到畢加索的抽象畫,如下圖所示,如果映射到面向?qū)ο缶幊蹋橄笈>褪浅橄箢悾ˋbstract Class),代表了所有牛的抽象。抽象??梢苑夯筛嗟呐?,比如水牛、奶牛、牦牛等。每一種牛都代表了一類(Class)牛,對于每一類牛,我們可以通過實例化,得到一頭具體的牛實例(Instance)。
           

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

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

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

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

          抽象的這種層次性,是除了抽象概念之外,另一個我們必須要深入理解的概念,因為小到一個方法要怎么寫,大到 一個系統(tǒng)要如何架構(gòu),以及我們后面第三章要介紹的結(jié)構(gòu)化思維,都離不開抽象層次的概念。

          在進(jìn)一步介紹抽象層次之前,我們先來理解一下外延和內(nèi)涵的意思:

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

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

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

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

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

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

          比如,現(xiàn)在要寫一個水果程序,我們需要對水果進(jìn)行抽象,因為水果里面有紅色的蘋果,我們當(dāng)然可以建一個 RedApple 的類,但是這個抽象層次有點低,只能用來表達(dá)“紅色的蘋果”。來一個綠色的蘋果,你還得新建一個 GreenApple 類。

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

          你可以看到,抽象層次越高,內(nèi)涵越小,外延越大,泛化能力越強(qiáng)。然而,其代價就是業(yè)務(wù)語義表達(dá)能力越弱。
           

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

          我經(jīng)常開玩笑說,你把所有的類都叫 Object,把所有的參數(shù)都叫 Map 的系統(tǒng)最通用,因為 Object 和 Map 的內(nèi)涵最小,其延展性最強(qiáng),可以適配所有的擴(kuò)展。從原理上來說,這種抽象也是對的,萬物皆對象嘛。但是這種抽象又有什么意義呢?它沒有表達(dá)出任何想表達(dá)的東西,只是一句正確的廢話而已。

          越抽象,越通用,可擴(kuò)展性越強(qiáng),然而其語義的表達(dá)能力越弱。越具體,越不好延展,然而其語義表達(dá)能力很強(qiáng)。所以,對于抽象層次的權(quán)衡,是我們系統(tǒng)設(shè)計的關(guān)鍵所在,也是區(qū)分普通程序員和優(yōu)秀程序員的關(guān)鍵所在。


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



          越是復(fù)雜的問題越需要分層抽象,分層是分而治之,抽象是問題域的合理劃分和概念語義的表達(dá)。不同層次提供不同的抽象,下層對上層隱藏實現(xiàn)細(xì)節(jié),通過這種層次結(jié)構(gòu),我們才有可能應(yīng)對像網(wǎng)絡(luò)通信、云計算等超級復(fù)雜的問題。

          網(wǎng)絡(luò)通信是互聯(lián)網(wǎng)最重要的基礎(chǔ)實施,但同時它又是一個很復(fù)雜的過程,你要知道把數(shù)據(jù)包傳給誰——IP協(xié)議,你要知道在這個不可靠的網(wǎng)絡(luò)上出現(xiàn)狀況要怎么辦——TCP 協(xié)議。有這么多的事情需要處理,我們可不可以在一個層次中都做掉呢?當(dāng)然是可以的,但顯然不科學(xué)。因此,ISO制定了網(wǎng)絡(luò)通信的七層參考模型,每一層只處理一件事情,低層為上層提供服務(wù),直到應(yīng)用層把HTTP、FTP等方便理解和使用的協(xié)議暴露給用戶。
           

          編程語言的發(fā)展史也是一個典型的分層抽象的演化史。

          機(jī)器能理解的只有機(jī)器語言,即各種二進(jìn)制的 01 指令。如果我們采用 01 的輸入方式,其編程效率極低(學(xué)過數(shù)字電路的同學(xué),體會下用開關(guān)實現(xiàn)加減法)。所以我們用匯編語言抽象了二進(jìn)制指令。

          然而匯編還是很底層,于是我們用 C 語言抽象了匯編語言。而高級語言 Java 是類似于 C 這樣低級語言的進(jìn)一步抽象,這種逐層抽象極大的提升了我們的編程效率。

           



          重復(fù)代碼是抽象的缺失



          如果說抽象的本質(zhì)是共性的話,那么我們代碼中的重復(fù)代碼,是不是就意味著抽象的缺失呢?

          是這樣的,重復(fù)代碼是典型的代碼壞味道,其本質(zhì)問題就是抽象的缺失。因為我們 Ctrl+C 加 Ctrl+V 的工作習(xí)慣,導(dǎo)致沒有對共性代碼進(jìn)行抽??;或者雖然抽取了,只是簡單的用了一個 Util 名字,沒有給到一個合適的名字,沒有正確的反應(yīng)這段代碼所體現(xiàn)的抽象概念,都屬于抽象不到位。

          有一次,我在 Review 團(tuán)隊代碼的時候,發(fā)現(xiàn)有一段組裝搜索條件的代碼,在幾十個地方都有重復(fù)。這個搜索條件還比較復(fù)雜,是以元數(shù)據(jù)的形式存在數(shù)據(jù)庫中,因此組裝的過程是這樣的:

          • 首先,我們要從緩存中把搜索條件列表取出來;
          • 然后,遍歷這些條件,將搜索的值填充進(jìn)去;

          //取默認(rèn)搜索條件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);  }}

          簡單的重構(gòu)無外乎就是把這段代碼提取出來,放到一個Util類里面給大家復(fù)用。然而我認(rèn)為這樣的重構(gòu)只是完成了工作的一半,我們只是做了簡單的歸類,并沒有做抽象提煉。

          簡單分析,不難發(fā)現(xiàn),此處我們是缺失了兩個概念:一個是用來表達(dá)搜索條件的類——SearchCondition;另一個是用來組裝搜索條件的類——SearchConditionAssembler。只有配合命名,顯性化的將這兩個概念表達(dá)出來,才是一個完整的重構(gòu)。

          重構(gòu)后,搜索條件的組裝會變成一種非常簡潔的形式,幾十處的復(fù)用只需要引用SearchConditionAssembler就好了。

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

          由此可見,提取重復(fù)代碼只是我們重構(gòu)工作的第一步。對重復(fù)代碼進(jìn)行概念抽象,尋找有意義的命名才是我們工作的重點。

          因此,每一次遇到重復(fù)代碼的時候,你都應(yīng)該感到興奮,想著這是一次鍛煉抽象能力的絕佳機(jī)會,當(dāng)然,測試代碼除外。


          強(qiáng)制類型轉(zhuǎn)換是抽象層次有問題



          面向?qū)ο笤O(shè)計里面有一個著名的 SOLID 原則是由 Bob 大叔(Robert Martin)提出來的,其中的 L 代表 LSP,就是 Liskov Substitution Principle(里氏替換原則)。簡單來說,里氏替換原則就是子類應(yīng)該可以替換任何父類能夠出現(xiàn)的地方,并且經(jīng)過替換以后,代碼還能正常工作。

          思考一下,我們在寫代碼的過程中,什么時候會用到強(qiáng)制類型轉(zhuǎn)換呢?當(dāng)然是 LSP 不能被滿足的時候,也就是說子類的方法超出了父類的類型定義范圍,為了能使用到子類的方法,只能使用類型強(qiáng)制轉(zhuǎn)換將類型轉(zhuǎn)成子類類型。

          舉個例子,在蘋果(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方法的入?yún)⒌念愋褪?Fruit,所以為了獲得 Apple 和 Watermelon 上的特有方法,我們不得不使用 instanceof 做一個類型判斷,然后使用強(qiáng)制類型轉(zhuǎn)換轉(zhuǎn)成子類類型,以便獲得他們的專有方法,很顯然,這是違背了里式替換原則的。

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

          因此,解決方法就是對 isSweet 和 isJuicy 進(jìn)行抽象,并提升一個層次,在 Fruit 上創(chuàng)建一個 isTasty() 的抽象方法,然后讓蘋果和西瓜類分別去實現(xiàn)這個抽象方法就好了。 


          下面是重構(gòu)后的代碼,通過抽象層次的提升我們消除了 instanceof 判斷和強(qiáng)制類型轉(zhuǎn)換,讓代碼重新滿足了里式替換原則。抽象層次的提升使得代碼重新變得優(yōu)雅了。

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

          所以,每當(dāng)我們在程序中準(zhǔn)備使用 instanceof 做類型判斷,或者用 cast 做強(qiáng)制類型轉(zhuǎn)換的時候。每當(dāng)我們的程序不滿足 LSP 的時候。你都應(yīng)該警醒一下,好家伙,這又是一次鍛煉抽象能力的絕佳機(jī)會。


          如何提升抽象思維能力



          抽象思維能力是我們?nèi)祟愄赜械?、與生俱來的能力,除了上面說的在編碼過程中可以鍛煉抽象能力之外,我們還可以通過一些其他的練習(xí),不斷的提升我們的抽象能力。

          多閱讀


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

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

          抽象思維的差別讓孩子們的學(xué)習(xí)成績從初中開始分化,許多不能適應(yīng)這種抽象層面訓(xùn)練的,就去讀技校了,因為技校比大學(xué)會更加具象:車銑刨磨、零部件都能看得見摸得著。體力勞動要比腦力勞動來的簡單。

          多總結(jié)沉淀


          小時候不理解,語文老師為什么總是要求我們總結(jié)段落大意、中心思想什么的?,F(xiàn)在回想起來,這種思維訓(xùn)練在基礎(chǔ)教育中是非常必要的,其實質(zhì)就是幫助學(xué)生提升抽象思維能力。

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

          我從四年前開始系統(tǒng)的記錄筆記,做總結(jié)沉淀,構(gòu)建自己的知識體系。這種思維訓(xùn)練的好處顯而易見,可以說我之前寫的《從碼農(nóng)到工匠》和現(xiàn)在正在寫的《程序員必備的思維能力》都離不開我總結(jié)沉淀的習(xí)慣。

          命名訓(xùn)練


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

          現(xiàn)實情況是,我們很多的工程師常常忽略了命名的重要性,只要能實現(xiàn)業(yè)務(wù)功能,名字從來就不是重點。

          實際上,這是對系統(tǒng)的不負(fù)責(zé)任,也是對自己的不負(fù)責(zé)任,更是對后期維護(hù)系統(tǒng)的人不負(fù)責(zé)任。寫程序和寫文章有很大的相似性,本質(zhì)上都是在用語言闡述一件事情。試想下,如果文章中用的都是些詞不達(dá)意的句子,這樣的文章誰能看得懂,誰又愿意去看呢。

          同樣,我一直強(qiáng)調(diào)代碼要顯性化的表達(dá)業(yè)務(wù)語義,其中命名在這個過程中扮演了極其重要的角色。為了代碼的可讀性,為了系統(tǒng)的長期可維護(hù)性,為了我們自身抽象思維的訓(xùn)練,我們都不應(yīng)該放過任何一個帶有歧義、表達(dá)模糊、意不清的命名。


          領(lǐng)域建模訓(xùn)練


          對于技術(shù)同學(xué),我們還有一個非常好的提升抽象能力的手段——領(lǐng)域建模。當(dāng)我們對問題域進(jìn)行分析、整理和抽象的時候,當(dāng)我們對領(lǐng)域進(jìn)行劃分和建模的時候,實際上也是在鍛煉我們的抽象能力。

          我們可以對自己工作中的問題域進(jìn)行建模,當(dāng)然也可以通過閱讀一些優(yōu)秀源碼背后的模型設(shè)計來學(xué)習(xí)如何抽象、如何建模。比如,我們知道 Spring 的核心功能是 Bean 容器,那么在看Spring源碼的時候,我們可以著重去看它是如何進(jìn)行Bean管理的?它使用的核心抽象是什么?不難發(fā)現(xiàn),Spring 是使用了 BeanDefinition、BeanFactory、BeanDefinitionRegistry、BeanDefinitionReader 等核心抽象實現(xiàn)了 Bean 的定義、獲取和創(chuàng)建。抓住了這些核心抽象,我們就抓住了 Spring 設(shè)計主脈。

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

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

          然而,經(jīng)過長期的、刻意的學(xué)習(xí)和鍛煉之后,很明顯可以感覺到我的建模能力和抽象能力都有很大的提升。不但分析問題的速度更快了,而且建出來的模型也更加優(yōu)雅了。


          小結(jié)



          • 抽象思維是程序員最重要的思維能力,抽象的過程就是尋找共性、歸納總結(jié)、綜合分析,提煉出相關(guān)概念的過程。


          • 語言和抽象是一體的,抽象思維也叫詞思維,因為抽象的概念只能通過語言才能表達(dá)出來。


          • 抽象是有層次性的,抽象層次越高,內(nèi)涵越小,外延越大,擴(kuò)展性越好;反之,抽象層次越低,內(nèi)涵越大,外延越小,擴(kuò)展性越差,但語義表達(dá)能力越強(qiáng)。


          • 對抽象層次的拿捏,體現(xiàn)了我們的設(shè)計功力,視具體情況而定,抽象層次既不能太高,也不能太低。


          • 重復(fù)代碼意味著抽象缺失,強(qiáng)制類型轉(zhuǎn)換意味著抽象層次有問題,我們可以利用這些信號來重構(gòu)代碼,讓代碼重新變的優(yōu)雅。


          • 我們可以通過刻意練習(xí)來提升抽象能力,這些練習(xí)包括閱讀、總結(jié)、命名訓(xùn)練、建模訓(xùn)練等。



          參考文獻(xiàn):
          [1] https://baike.baidu.com/item/抽象/9021828
          [2] https://zh.wikipedia.org/wiki/抽象化
          [3] https://baike.baidu.com/item/抽象思維
          [4] https://www.sohu.com/a/359915387_260616


          今天我們來聊聊JVM類加載機(jī)制

          2021-03-09

          2021最新最全Java進(jìn)階資料合集

          2021-03-02

          JAVA的SPI機(jī)制

          2021-01-25







          點個在看,讓更多人看見



          瀏覽 78
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  性无码一区二区三区在线观看 | 插入小逼网站 | 云南省医疗服务质量评估中心官网 | 亚洲韩日中文字幕 | 丁香六月色婷婷 |