golang 标准库间依赖的可视化展示


声明:本文转载自https://my.oschina.net/ios122/blog/1547643,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

简介

预览

国庆看完 << Go 语言圣经 >>,总想做点什么,来加深下印象.以可视化的方式展示 golang 标准库之间的依赖,可能是一个比较好的切入点.做之前,简单搜了下相关的内容,网上也要讨论,但是没有发现直接能拿过来用的.标准库之间,是必然存在依赖关系的,不同库被依赖的程度必然是不一样的.但究竟有多大差别呢?

以下内容,数据源自真实环境的 golang 1.9 版本的标准库.所以,本文不仅是一篇可视化相关的讨论文章,更是提供了一个可以直接探究 golang 标准库间依赖关系的快速梳理工具.

数据准备

标准库各个包之间的相互关系,可以直接通过命令获取,然后简单变换为一个标准的 JSON 对象:

go list -json  std 

示例输出:

{ 	"Dir": "/usr/local/go/src/archive/tar", 	"ImportPath": "archive/tar", 	"Name": "tar", 	"Doc": "Package tar implements access to tar archives.", 	"Target": "/usr/local/go/pkg/darwin_amd64/archive/tar.a", 	"Goroot": true, 	"Standard": true, 	"StaleReason": "standard package in Go release distribution", 	"Root": "/usr/local/go", 	"GoFiles": [ 		"common.go", 		"format.go", 		"reader.go", 		"stat_atimespec.go", 		"stat_unix.go", 		"strconv.go", 		"writer.go" 	], 	"IgnoredGoFiles": [ 		"stat_atim.go" 	], 	"Imports": [ 		"bytes", 		"errors", 		"fmt", 		"io", 		"io/ioutil", 		"math", 		"os", 		"path", 		"sort", 		"strconv", 		"strings", 		"syscall", 		"time" 	], 	"Deps": [ 		"bytes", 		"errors", 		"fmt", 		"internal/cpu", 		"internal/poll", 		"internal/race", 		"io", 		"io/ioutil", 		"math", 		"os", 		"path", 		"path/filepath", 		"reflect", 		"runtime", 		"runtime/internal/atomic", 		"runtime/internal/sys", 		"sort", 		"strconv", 		"strings", 		"sync", 		"sync/atomic", 		"syscall", 		"time", 		"unicode", 		"unicode/utf8", 		"unsafe" 	], 	"TestGoFiles": [ 		"reader_test.go", 		"strconv_test.go", 		"tar_test.go", 		"writer_test.go" 	], 	"TestImports": [ 		"bytes", 		"crypto/md5", 		"fmt", 		"internal/testenv", 		"io", 		"io/ioutil", 		"math", 		"os", 		"path", 		"path/filepath", 		"reflect", 		"sort", 		"strings", 		"testing", 		"testing/iotest", 		"time" 	], 	"XTestGoFiles": [ 		"example_test.go" 	], 	"XTestImports": [ 		"archive/tar", 		"bytes", 		"fmt", 		"io", 		"log", 		"os" 	] } 

梳理过的数据源,参见: https://raw.githubusercontent.com/ios122/graph-go/master/data.js

可视化原理

主要涉及一下内容:

  • 可视化显示,使用的是 echarts

  • 使用原始数据的 ImportPath 而不是 Name,来作为每个数据节点的唯一id.这样是因为 golang 本身的包命名规范决定的.

  • 使用原始数据的 Imports 字段,来确定标准库包与包之间的相互依赖关系.golang是不允许循环依赖的,所以一些循环依赖相关的问题,不需要考虑.

  • 节点的大小,和包被其他包引入的次数成正相关.这样做,被依赖越多的包,图上最终显示时,就会越大.常用包和不常用包,一目了然.

数据整理

就是把原始数据,处理成 echarts 需要的数据,这里简要说下最核心的思路:

  • echarts 显示相关的代码,很大程度上参考了 graph-npm

  • 节点坐标和颜色,采用随机坐标和颜色,以去除节点和包之间的联系.我认为这样处理,能更纯粹地观察标准库包与包之间的联系.

  • 需要一个 edges 来记录包与包之间的依赖关系.在每次遍历 Imports 时,动态写入.

  • 需要一个 nodes 来记录包自身的一些信息,但是其 size 参数,需要计算过所有依赖关系后再填入.

  • 使用 nodedSize 来记录每个包被依赖的次数,为了提升效率,它是一个字典Map.

 /* 将原始数据,转换为图标友好的数据.  	ImportPath 作为唯一 id 和 标签; 	Imports 用于计算依赖关系; 	节点的大小,取决于被依赖的次数; 	*/ function transData(datas){ 	/* 存储依赖路径信息. */ 	let edges = []  	/* 存储基础节点信息. */ 	let nodes = []  	/* 节点尺寸.初始是1, 每被引入一次再加1. */ 	let nodedSize = {}  	/* 尺寸单位1. */ 	let unitSize = 1.5  	datas.map((data)=>{ 		let itemId = data.ImportPath  		nodes.push({ 			"label": itemId, 			"attributes": {}, 			"id": itemId, 			"size": 1 		})  		if(data.Imports){ 			data.Imports.map((importItem)=>{ 				edges.push({ 					"sourceID": importItem, 					"attributes": {}, 					"targetID": itemId, 					"size": unitSize 				})  				if(nodedSize[importItem]){ 					nodedSize[importItem] = nodedSize[importItem] + unitSize 				}else{ 					nodedSize[importItem] = unitSize 				} 			}) 		} 	})  	/* 尺寸数据合并到节点上. */ 	nodes.map((item)=>{ 		let itemId = item.id 		if(nodedSize[itemId]){ 			item.size = nodedSize[itemId] 		} 	})  	return { 		nodes,edges 	} } 

效果与源码

相关链接

本文发表于2017年10月09日 08:37
(c)注:本文转载自https://my.oschina.net/ios122/blog/1547643,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1796 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1