程序員特有的畫圖方式——語繪工具小入門.

黑客與畫家

雖然程序員多數時候都在與字符打交道,但偶爾也會像建築或製造業的工程師同樣,畫一些圖,好比:node

  1. 爲了表達多個系統如何協做以實現業務需求,會畫時序圖;
  2. 爲了表達存儲到數據庫中的業務實體間的關係,會畫ER圖;
  3. 爲了表達複雜的業務實體在整個生存期中狀態的變化,會畫狀態圖。

除此以外,還有流程圖、甘特圖、火焰圖,等等。git

儘管軟件開發過程當中產出的這些圖不必定逼真、漂亮,或嚴謹,但憑着圖上不一樣的形狀、顏色,以及佈局,也能夠作到一圖勝千言的效果。程序員

圖頗有用,畫圖的工具也一樣舉足輕重。若是是本地的桌面應用,多數人可能會選擇用Windows平臺的Visio或macOS平臺的OmniGraffle;若說到做圖網站,則可能會選擇ProcessOn或Draw.io。github

但比起用鼠標拖拖拉拉,我更喜歡用代碼來畫圖。web

用代碼畫圖?

用代碼畫圖大體上能夠分爲兩類:算法

  1. 用具體的編程語言控制某種繪圖的API畫出想要的圖形,好比OpenGL、HTML5中的Canvas;
  2. 用DSL描述想要畫的圖,而後用程序根據DSL生成圖片。

我所說的用代碼畫圖指的是上述的第二類。shell

百聞不如一見,以最容易上手的DOT語言爲例,將下列內容保存在名爲hello.dot的文件中數據庫

digraph G {
        Hello -> World
}

而後在shell中運行以下命令npm

dot -Tpng hello.dot -o hello.png

便獲得了相應的PNG文件編程

更多的栗子

下面就帶各位讀者蜻蜓點水地看看不一樣的圖能夠用哪些工具來繪製。

流程圖

說到程序員畫的圖,最出名的當屬流程圖了。依稀記得在高中的時候,某一冊的數學課本中講到了算法(也許是展轉相除法),而且給出了圖示,那應當就是我第一次見到流程圖。上大學後也有一段時間癡迷於尋找可以繪製流程圖的DSL,不過一直未果。直到遇到Boostnote後,才知道的確有這樣的DSL,那即是flowchart.js

flowchart.js是一個JS編寫的、用來繪製流程圖的庫。好比下面這張圖

即是依據下列的DSL生成的

st=>start: Start
op=>operation: Your Operation
cond=>condition: Yes or No?
e=>end

st->op->cond
cond(yes)->e
cond(no)->op

flowchart.js生成的是SVG格式的圖片文件,但SVG文件不方便嵌入到Markdown或Confluence的文檔中,所以我會把它轉換爲PNG格式。折騰了一番後,發如今Mac上最靠譜的方法,是將SVG文件嵌入一個HTML文檔,再用瀏覽器打開這個HTML,而後複製圖片到預覽程序上保存下來。

遺憾的是,不論是Emacs仍是VSCode,彷佛都沒有輔助編輯flowchart.js的DSL的插件。

有限狀態機

有限狀態機的示意圖也是很常見的圖形,尤爲是在講解編譯器的書的語法分析章節中。在Graphviz項目官網的Gallery板塊中,便有一個有限狀態機的例子

它由以下的DOT代碼描述

digraph finite_state_machine {
    rankdir=LR;
    size="8,5"
    node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
    node [shape = circle];
    LR_0 -> LR_2 [ label = "SS(B)" ];
    LR_0 -> LR_1 [ label = "SS(S)" ];
    LR_1 -> LR_3 [ label = "S($end)" ];
    LR_2 -> LR_6 [ label = "SS(b)" ];
    LR_2 -> LR_5 [ label = "SS(a)" ];
    LR_2 -> LR_4 [ label = "S(A)" ];
    LR_5 -> LR_7 [ label = "S(b)" ];
    LR_5 -> LR_5 [ label = "S(a)" ];
    LR_6 -> LR_6 [ label = "S(b)" ];
    LR_6 -> LR_5 [ label = "S(a)" ];
    LR_7 -> LR_8 [ label = "S(b)" ];
    LR_7 -> LR_5 [ label = "S(a)" ];
    LR_8 -> LR_6 [ label = "S(b)" ];
    LR_8 -> LR_5 [ label = "S(a)" ];
}

很多工具將DOT語言做爲中間媒介來實現繪圖的功能。

flowchart.js不一樣,Emacs和VSCode均可以很好地支持DOT代碼的編輯和預覽。Emacs上有dot-mode,VSCode則有Graphviz (dot) language support for Visual Studio Code這個插件。

時序圖

我畫得最多的當屬時序圖。在舊文《時序圖繪製工具蜻蜓點水》中,提到了三個工具:

  1. WebSequenceDiagrams,一個在線繪製時序圖的網站;
  2. sdedit,一個本地的命令行兼GUI繪圖工具;
  3. SequenceDiagram,也是一個網站。

當時傾向於使用sdedit。時過境遷,現在的WebSequenceDiagrams變得更好看了,而我也選擇了PlantUML做爲繪製時序圖的主力工具。下面這張圖是PlantUML官網給出的例子

它依據以下的代碼生成

@startuml
用戶 -> 認證中心: 登陸操做
認證中心 -> 緩存: 存放(key=token+ip,value=token)token

用戶 <- 認證中心 : 認證成功返回token
用戶 -> 認證中心: 下次訪問頭部攜帶token認證
認證中心 <- 緩存: key=token+ip獲取token
其餘服務 <- 認證中心: 存在且校驗成功則跳轉到用戶請求的其餘服務
其餘服務 -> 用戶: 信息
@enduml

Emacs的plantuml-mode,以及VSCode的PlantUML插件均可覺得PlantUML的DSL提供語法高亮。

下載了PlantUMLjar包後,在Emacs中添加以下的配置,就能夠不依賴遠程服務器來生成PNG格式的圖片了

(setq plantuml-default-exec-mode 'jar)
(setq plantuml-jar-path "/path/to/plantuml.jar")

UML用例圖

在《架構整潔之道》一書中,做者提出了一個軟件架構模式,其中有一層即是用例。看完這本書後,我愈加地喜歡做者這一套架構模式,漸漸開始在設計文檔中給出需求的典型用例——儘管是文字描述。再後來,才知道原來UML中已經有一類專門用於描述用例的圖形方法——用例圖。

用於畫用例圖的依然是PlantUML。下列這張圖

即是依據以下的源代碼生成的

@startuml
left to right direction
actor 員工 as yg
actor 顧客 as gk
actor 餐廳員工 as ctyg
actor A2 as a2
actor 送餐員 as scy
rectangle cos {
        note "沒註冊工資\n支付的採用\n送餐時收費" as mzc
        usecase 查看菜單 as ckcd
        usecase 註冊 as zc
        usecase 登陸 as dl
        usecase 訂餐 as dc
        usecase "預定/覆蓋預定" as yy
        usecase 備餐 as bc
        usecase 請求送餐 as qqsc
        usecase 記錄送餐 as jlsc
        usecase 打印送餐說明 as dyscsm
        usecase 記錄收費 as jlsf
        zc .> dl : <<extends>>
        dl .> dc : <<extends>>
}
actor A1 as a1
note bottom of a1 : 已註冊工資支付

yg <|-- gk
gk <|-- ctyg
ctyg <|-- a2
ctyg <|-- scy

yg -- ckcd
yg ---- zc
yg --- dl
gk -- dc
gk ---- yy
ctyg -- bc
ctyg --- qqsc
scy -- jlsc
scy --- dyscsm
scy -- mzc
jlsf -- mzc
@enduml

比較遺憾的是,PlantUML自動排版的結果顯得不那麼整齊,左下角有一個明顯的三角形空白區域——這也是DSL大法的一個缺點,即沒法完美地控制最終的排列效果。

UML類圖

最開始接觸UML的時候,學習的即是類圖——儘管接觸得最先,畫得卻最少。比起類圖,ER圖反而畫得更多一點。

若是要畫類圖,首選的工具是mermaid。跟PlantUML同樣,mermaid也是一個大而全的東西,除了畫UML類圖,也能夠畫流程圖、時序圖,以及UML狀態圖等。下面這張圖

即是mermaid-cli依據以下的源代碼生成的

classDiagram
  Image <|-- BMP
  Image <|-- GIF
  Image <|-- JPEG
  Image: +setImpl()
  Image: +parseFile()

  ImageImpl <|-- WinImpl
  ImageImpl <|-- LinuxImpl
  ImageImpl: +doPaint()

  Image ..> ImageImpl

Emacs用戶能夠安裝mermaid-mode,VSCode用戶則可使用Mermaid Preview這個插件,來輔助編輯mermaid的源文件。

方纔提到的mermaid-cli是一個命令行程序,用於在本地根據mermaid的源文件產生PNG格式的圖片,安裝也很簡單

npm install -g mermaid.cli

總結

還有許多的圖能夠用DSL來繪製,感興趣的讀者能夠到mermaidPlantUML的官網瞭解一番,這裏再也不一一舉例。

用DSL來繪圖有一些優勢:

  1. 不須要藉助鼠標工具,純鍵盤黨的福音;
  2. 源代碼爲純文本,生成器跨多平臺,能夠在多個平臺甚至網頁上編輯和查看,不受單一軟件廠商的約束;
  3. 方便修改,改完不須要來回調整各個圖形的位置。

但也有一些缺點:

  1. 不直觀,生成圖片前很差猜想最終的效果;
  2. 沒法準確地控制圖中全部元素的排列和位置,有時候得不到想要的效果;
  3. 須要學習不一樣的DSL,學習成本比可視化工具要高。

就像軟件開發中沒有銀彈同樣,畫圖工具也沒有萬金油,關鍵仍是要因地制宜地選擇最合適的工具來解決眼前的問題。

閱讀原文

相關文章
相關標籤/搜索