如何將你的代碼可視化?(如何將你的代碼可視化出來)
本文最初發(fā)布于 Alex Ellis 的個人博客。
有一天,我正在閱讀關(guān)于個人電腦和桌面 GUI 發(fā)展的內(nèi)容,我就想,我們都已經(jīng)非常習(xí)慣個人電腦“桌面“這個類比。我們把文件放在文件夾里,并把它們放在桌面上。這個過程有很多物理動作發(fā)生。
人類非常善于理解空間,尤其是在記憶物理空間的時候,這讓我聯(lián)想到了我們通常如何將代碼可視化。在思考和可視化代碼的時候,有沒有什么好的方法可以利用這一點?
如何可視化代碼?
這讓我想到了我往往如何可視化代碼,有點難以描述。我認(rèn)為,它通常以不同的方式存在于我的腦海中,這取決于抽象和特殊性水平,而且同時存在若干不同方式的組合。根據(jù)需要,我可以很好地在它們之間切換,特別是當(dāng)我對代碼庫非常熟悉的時候。例如,當(dāng)我想象各種微服務(wù)之間的交互時,在每個服務(wù)的周圍畫個大方框是很有幫助的,把每個服務(wù)視為一個大的工作單元,彼此之間通過 RPC 交互。
一個簡單的面向微服務(wù)的汽車租賃服務(wù)架構(gòu)圖,以及代表一條執(zhí)行路徑的分布式跟蹤。
另一方面,如果對于計算機如何讀取我給它的東西,我想知道微末的細節(jié),那么把所有東西放大到物理內(nèi)存表示是有幫助的。
我曾經(jīng)做過的一個 Google Sheets 頁的截圖,上面有內(nèi)存地址和匯編指令。這對于非常仔細地了解整個過程很有幫助。
幸運的是,我把大部分時間都花在了中間的某個地方,在閱讀實際的代碼(比匯編高級),把大塊的代碼作為一個個大的單元來思考,和研究架構(gòu)圖及系統(tǒng)間通信之間做一些平衡。即使是代碼本身也已經(jīng)有了很多物理關(guān)系;想想目錄路徑、命名空間、行縮進以及代碼行的線性排序。
這些可視化的效果如何?
對于這個問題,我考慮了一些不同的可視化技術(shù),每一種技術(shù)都有不同的應(yīng)用場景??紤]一下下面這個不完全的清單:
- 架構(gòu)圖
- 依賴圖
- 分布式跟蹤
- 序列圖
- 類圖
- 打印語句
- 火焰圖
- 閱讀源代碼
怎么對它們進行比較呢?首先,似乎有一個天然的抽象等級軸線,從低級的代碼閱讀到高級的架構(gòu)圖。似乎還有一些其他的軸線,我們也可以對它們進行排序。
也許我們可以根據(jù)它們在多大程度上代表了更大的系統(tǒng)來對它們進行排名?架構(gòu)圖在這方面做得很好,但火焰圖只能代表單個執(zhí)行路徑。也許是變化的頻率?這是一個有趣的問題,雖然源代碼經(jīng)常變,但架構(gòu)圖(有望)保持穩(wěn)定。
讓我們想一下,可視化如何很好地表示整個系統(tǒng)實際的代碼執(zhí)行情況呢?在這種情況下,高級的架構(gòu)圖得分也許不會很高,因為它抽象掉了服務(wù)框內(nèi)的許多細節(jié)。分布式跟蹤可以做得更好,不過具體程度取決于有多少個跟蹤點。類圖雖然有助于可視化類之間的關(guān)系,但可能并不表示實際的路徑。火焰圖可以顯示清晰的執(zhí)行路徑,但只能顯示單條代碼路徑,而不能為更大的系統(tǒng)提供可見性。
畫到圖上可能會像下面這樣,不過上面的這些點表示為一個范圍可能更好:
這讓我思考,右上角的部分會是什么樣子。一個能讓我們洞察細節(jié)的、有用的可視化該是什么樣子?有沒有一種方法可以在較低的層次上,將整個系統(tǒng)的路徑可視化?
如果我們也使用空間會是什么樣子?
看圖的感覺仍然像看地圖。在那一刻,你把在代碼中看到的東西在空間上轉(zhuǎn)化為圖表,就像你在一個不熟悉的地方用地圖確定方向。就像電腦上的東西,我們用了桌面隱喻一樣,我想知道是否有另一種方式將代碼可視化為實際存在的東西,以便讓翻譯過程變得更容易。
如果我們用“玩具型可視化(toy visualization)”在物理空間中表示代碼,會怎么樣?
看下面這個基本的例子,我們有一個 Counter 類,它有一個私有的 count_ 變量并提供了各種訪問方法。也許它會在一個簡單的 main 函數(shù)中被訪問,做一些計數(shù)。
#include <iostream>class Counter {public: void reset() { count_ = 0; } int get() { return count_; } void incr() { count_; } void set_count(int x) { count_ = x; }private: int count_;};int main() { Counter counter; counter.reset(); counter.incr(); counter.incr(); std::cout << counter.get();}
如果我們把代碼的不同部分表示為三維結(jié)構(gòu),會怎么樣?如果我們把 Counter 類表示為一個大房間,墻上寫著咒語,會怎么樣?你可以想象代碼的可能路徑,變量和它們所代表的事物之間的聯(lián)系,就像下圖中的粉紅線:
我們可以從中看到什么?首先,作為私有變量,count_ 的用法顯然沒有什么不當(dāng),因為沒有任何粉紅線從它那里離開房間。公共方法也很清楚,它們與房間外的事物有粉紅色的連接。
更有趣的是,這個房間與另一個房間相連,會是什么樣子?main 函數(shù)可以是另一個房間,其符咒線也做了相應(yīng)的連接:
現(xiàn)在,我們可以跨房間對話了;在 main 函數(shù)調(diào)用 counter.reset() 的地方,我們可以有一個從調(diào)用者 main 到被調(diào)用者 Counter 類的連接。你甚至可以想象有一個調(diào)試器單步遍歷這個過程,觀察這條線路上的參數(shù)和返回值。想象一下,我們可以放大不同的區(qū)域來查看本地狀態(tài)和數(shù)值,然后沿著調(diào)用路徑返回到活動區(qū)域。
這有用嗎?
像這樣的東西有用嗎?我不確定。用這樣的方式走查一個熟悉的代碼庫會很有趣,特別是當(dāng)你能在 3D 表示(或是 VR 環(huán)境)中做空間探索,并可以根據(jù)需要縮小和放大時。當(dāng)?shù)谝淮翁剿饕粋€新的代碼庫,查看事物之間的連接關(guān)系時,不知道它是否會特別有用。不過,我確實處理過一些代碼庫,如果這樣看會非常嚇人。我很想看看這樣看會有什么不同,每新引入一個類,這兒那兒就會引入新的連接。
話雖如此,我認(rèn)為在制作這樣的東西時,你至少會遇到以下問題:
- 復(fù)雜的代碼很難推理。把意大利面代碼中的意大利面可視化可謂大快人心,但是對于非常復(fù)雜的代碼來說,這樣做不知道會有多繁瑣?
- 如何表示出像線程同時執(zhí)行這樣的東西?
- 如何表示是引用傳遞而不是值傳遞?
- 如何表示異步工作?如何表示遞歸?房間一直嵌套下去?
- 如何防止里面的東西變得陳舊和過時?至少,這個需要能夠自動生成。
問題
有幾個考量因素使這個問題變得棘手。一個是物理位置的變化比代碼的變化耗時通常長得多。使用熟悉的物理位置作為記憶宮殿,一部分原因是它在你的記憶中每次出現(xiàn)都是一樣的。如果你在記憶一副撲克牌,你可以把梅花 A 暫時存放在櫥柜門后面,下次你需要存放撲克牌時,櫥柜門仍然在那。
如果你的代碼庫經(jīng)常變化,反映事物空間布局的地圖就可能會發(fā)生變化,不管這些地圖是 3D 生成的還是純意識的。這就像回到一個你曾經(jīng)熟悉的地方,想象一下,不只是地標(biāo)變了,路也改道了。即使我們天生具有記憶空間事物的天賦,如果那個空間發(fā)生了變化,如果我們不得不重新學(xué)習(xí),我們還能從空間可視化受益嗎?
看看這樣的東西對于探索一個新代碼庫(就像使用地圖探索一座新的城市),以及隨著時間推移再次回到該代碼庫(就像離開很長時間后回到自己的家鄉(xiāng)),有多大幫助,這會很有趣。
代碼可視化項目
我對這一領(lǐng)域的數(shù)據(jù)可視化不是很熟悉(其他領(lǐng)域的也不熟悉),但經(jīng)過簡單的搜索(也就是 30 分鐘的 Google 搜索),我發(fā)現(xiàn)有幾個項目似乎在做類似的事情:
- SoftVis3D:其中的“代碼城市”視圖提供了項目層次結(jié)構(gòu)的可視化。
- Code Park:一款新的 3D 代碼可視化工具(2017),“在類似三維游戲的環(huán)境中可視化代碼庫”,其中,代碼被表示為 "代碼室",代碼在墻上(現(xiàn)在讀到這個,感覺和我的想法非常類似)。
- 使用 3D-Flythrough 實現(xiàn)代碼結(jié)構(gòu)可視化(2016),提供空間隱喻和第一人稱代碼探索。
- Primitive:一家 VR 合作初創(chuàng)公司,擁有矩陣式的“沉浸式開發(fā)環(huán)境”,包括“面向 3D 視覺分析軟件的新工具”。
下面是讀者指出的一些項目:
- AppMap:一個自動化代碼分析工具,包括依賴關(guān)系圖和跟蹤視圖。
- plurid:一個用于在三維可探索結(jié)構(gòu)中可視化和調(diào)試代碼的框架。
- fsn(文件管理器):一個實驗性的應(yīng)用程序,支持以 3D 方式查看文件系統(tǒng)(出現(xiàn)在 Jurassic Park 中)。
如果你了解到其他類似的項目,歡迎和我聯(lián)系,我非常樂意聽到更多這樣的項目!
有趣的想象
顯然,這個概念并不是什么突破性的東西,但我認(rèn)為,對于我們使用的工具,這是一個有趣的思考方式,重要的是,我們?nèi)绾巫龅酶?。一定有更好的方法存在,設(shè)想下它們可能的樣子會很有趣。