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