白嫖一個Android項目的類圖生成工具!(建議收藏)
大家好,我是劉望舒,騰訊最具價值專家,著有三本業(yè)內(nèi)知名暢銷書,連續(xù)五年蟬聯(lián)電子工業(yè)出版社年度優(yōu)秀作者,百度百科收錄的資深技術(shù)專家。
前華為面試官、獨角獸公司技術(shù)總監(jiān)。
想要加入?BATcoder技術(shù)群,公號回復(fù)BAT?即可。
作者:leobert-lan?
https://juejin.cn/user/2066737589654327/posts
前言
獲得一個生成類圖的工具,并通過文章快速了解是否方便且好用 了解一下我是如何折騰的

主要問題與方案
業(yè)務(wù)迭代后或者代碼改進(jìn)后,文檔(uml圖)未及時更新 手動維護(hù)耗時耗力
方便向他人介紹業(yè)務(wù)和代碼 項目龐大或者復(fù)雜時,更容易找到需求對應(yīng)的關(guān)注點,重新維護(hù)時日久遠(yuǎn)的業(yè)務(wù)時,狀態(tài)來的快 圖比代碼親切而且保護(hù)隱私??
編碼時分析
編譯時分析
運行時反射分析
問題分治與解決
分治1-簡化輸出產(chǎn)物
從源碼、或者編譯的中間產(chǎn)物中分析出類關(guān)系;ps:我們已經(jīng)確定了要從編譯中間產(chǎn)物出發(fā) 將類關(guān)系轉(zhuǎn)變?yōu)閳D
分治2-確定分析的起始點

@Target(AnnotationTarget.CLASS)
annotation?class?GenerateClassDiagram?{}
class?Animal
@GenerateClassDiagram
class?Dog?:?Animal()
@GenerateClassDiagram
class?Cat?:?Animal()
為何不 "雙向" 分析:繼承和實現(xiàn)關(guān)系,雙向分析會帶來額外的復(fù)雜度,且在使用上規(guī)則不清晰,依賴關(guān)系難以雙向分析。但是,如果使用規(guī)則上可以做到清晰明了,這一點值得實現(xiàn) 為何不標(biāo)注在Animal上,進(jìn)行反向分析:如果高層級的類在庫包中,則需要修改庫包,這不利于日常管理與維護(hù) 如果只標(biāo)注了Cat而沒有標(biāo)注Dog,Dog將不會體現(xiàn)在圖中?:是的 如果全部標(biāo)注了,是否產(chǎn)生不良影響 :不會,但是沒有必要
分治3-確定關(guān)系的分析方法
繼承&實現(xiàn)
public?interface?TypeElement?extends?Element,?Parameterizable,?QualifiedNameable?{
????TypeMirror?getSuperclass();
????List?extends?TypeMirror>?getInterfaces();
????//其他無關(guān)代碼略去
}
依賴&關(guān)聯(lián)&聚合&組合
public?interface?TypeElement?extends?Element,?Parameterizable,?QualifiedNameable?{
????List?extends?Element>?getEnclosedElements();
????//無關(guān)代碼略去
}
分治4-確定分析的終點
Relation(From,End)?表述?從From?到?End?的關(guān)系
分治5-分治3的補充,處理集合、數(shù)組、泛型
private?abstract?class?CastingTypeVisitor<T>?constructor(private?val?label:?String)?:
????SimpleTypeVisitor6Void ?>()?{
????override?fun?defaultAction(e:?TypeMirror,?v:?Void?):?T?{
????????throw?IllegalArgumentException("$e?does?not?represent?a?$label")
????}
}
private?class?FetchClassTypeVisitor?:?CastingTypeVisitor<List>> (label?=?"")?{
????override?fun?defaultAction(e:?TypeMirror,?v:?Void?):?List?{
????????//ignore?it
????????return?emptyList()
????}
????override?fun?visitArray(t:?ArrayType,?p:?Void?):?List?{
????????return?t.componentType.accept(this,?p)
????}
????override?fun?visitWildcard(t:?WildcardType,?p:?Void?):?List?{
????????val?ret?=?arrayListOf()
????????t.superBound?.let?{
????????????ret.addAll(it.accept(this,?p))
????????}
????????t.extendsBound?.let?{
????????????ret.addAll(it.accept(this,?p))
????????}
????????return?ret
????}
????override?fun?visitDeclared(t:?DeclaredType,?p:?Void?):?List?{
????????val?ret?=?arrayListOf(t)
????????t.typeArguments?.forEach?{
????????????ret.addAll(it.accept(this,?p))
????????}
????????return?ret.toSet().toList()
????}
????override?fun?visitError(t:?ErrorType,?p:?Void?):?List?{
????????return?visitDeclared(t,?p)
????}
????override?fun?visitTypeVariable(t:?TypeVariable,?p:?Void?):?List?{
????????val?ret?=?arrayListOf()
????????t.lowerBound?.let?{
????????????ret.addAll(it.accept(this,?p))
????????}
????????t.upperBound?.let?{
????????????ret.addAll(it.accept(this,?p))
????????}
????????return?ret
????}
}
fun?TypeMirror.fetchDeclaredType():?List?{
????return?this.accept(FetchClassTypeVisitor(),?null)
}
分治6-關(guān)系的存儲
分治7-類型的細(xì)節(jié)處理
abstract?class?UmlElement(val?diagram:?ClassDiagram?,?val?element:?Element?)?{
????/**
?????*?return:?plant-uml?中相應(yīng)的文本
?????*?*/
????abstract?fun?umlElement(context:?MutableSet<UmlElement>):?String
????abstract?fun?parseFieldAndMethod(diagram:?ClassDiagram,
?????????????????????????????????????graph:?DAG<UmlElement>,
?????????????????????????????????????cache:?MutableSet<UmlElement>)
????abstract?fun?drawField(fieldDrawer:?FieldDrawer,?
???????????????????????????builder:?StringBuilder,
???????????????????????????context:?MutableSet<UmlElement>)
????abstract?fun?drawMethod(methodDrawer:?MethodDrawer,
????????????????????????????builder:?StringBuilder,
????????????????????????????context:?MutableSet<UmlElement>)
}
UmlInterface:接口 UmlEnum:枚舉 UmlClass:類 UmlStub:分治6中提到的虛擬頂點
interface?IElementDrawer?{
????fun?drawAspect(builder:?StringBuilder,?element:?UmlElement,?context:?MutableSet<UmlElement>)
}
interface?IJavaxElementDrawer?{
????fun?drawAspect(builder:?StringBuilder,?element:?Element,?context:?MutableSet<UmlElement>)
}
abstract?class?SuperClz?:?SealedI?{
????var?superI:?Int?=?0
}
abstract?class?"SuperClz"{
??..?fields?..
??{field}-superI?:?int
??..?methods?..
??{method}+?getSuperI():?int
??{method}+?setSuperI(int):?void
}

分治8-輸出為PlantUml文件
保持優(yōu)雅
繪制多張ClassDiagram 增加配置,屏蔽一些輸出,例如:不想看見private修飾的fields 包名是在是太長了,存在閱讀干擾 等等
維持簡單
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation?class?ClassDiagram(
????val?qualifier:?String?=?"",
????val?fieldVisible:?Array?=?[Visible.Private,?Visible.Protected,?Visible.Package,?Visible.Public],
????val?methodVisible:?Array?=?[Visible.Private,?Visible.Protected,?Visible.Package,?Visible.Public],
)
@ClassDiagram("Demo")
annotation?class?DemoDiagram
ClassDiagram : 標(biāo)識注解表達(dá)分組,并且包含配置 GenerateClassDiagram :標(biāo)識類圖中的分析起始點
@GenerateClassDiagram
@DemoDiagram
class?Clz?:?SuperClz(),?SealedI?{
????val?int:?Int??=?null
}
減少侵入
擴(kuò)展能力
使用示例
添加依賴
implementation?"io.github.leobert-lan:class-diagram-reporter:1.0.0"
annotationProcessor?"io.github.leobert-lan:report-anno-compiler:1.1.4"
annotationProcessor?"io.github.leobert-lan:class-diagram-reporter:1.0.0"

kapt?{
????arguments?{
????????arg("module",?"ktsample")?//模塊名稱
????????arg("mode",?"mode_file")?
????????arg("active_reporter",?"on")
????}
}
自行定義注解
@ClassDiagram(qualifier?=?"BridgePattern")
annotation?class?BridgePatternDiagram
//or
@ClassDiagram(
????qualifier?=?"AAAB",
????fieldVisible?=?{Visible.Package,?Visible.Public}
)
public?@interface?AAAB?{
}
class?BridgePattern?{
????@ClassDiagram(qualifier?=?"BridgePattern")
????annotation?class?BridgePatternDiagram
????interface?MessageImplementor?{
????????fun?send(message:?String,?toUser:?String)
????}
????abstract?class?AbstractMessage(private?val?impl:?MessageImplementor)?{
????????open?fun?sendMessage(message:?String,?toUser:?String)?{
????????????impl.send(message,?toUser)
????????}
????}
????@BridgePatternDiagram
????@GenerateClassDiagram
????class?CommonMessage(impl:?MessageImplementor)?:?AbstractMessage(impl)
????@BridgePatternDiagram
????@GenerateClassDiagram
????class?UrgencyMessage(impl:?MessageImplementor)?:?AbstractMessage(impl)?{
????????override?fun?sendMessage(message:?String,?toUser:?String)?{
????????????super.sendMessage("加急:$message",?toUser)
????????}
????}
????@BridgePatternDiagram
????@GenerateClassDiagram
????class?MessageSMS?:?MessageImplementor?{
????????override?fun?send(message:?String,?toUser:?String)?{
????????????println("使用系統(tǒng)內(nèi)短消息的方法,發(fā)送消息'$message'給$toUser")
????????}
????}
????@BridgePatternDiagram
????@GenerateClassDiagram
????class?MessageEmail?:?MessageImplementor?{
????????override?fun?send(message:?String,?toUser:?String)?{
????????????println("使用郵件短消息的方法,發(fā)送消息'$message'給$toUser")
????????}
????}
}

https://github.com/leobert-lan/ReportPrinter/

? 耗時2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!
為了防止失聯(lián),歡迎關(guān)注我的小號
??微信改了推送機制,真愛請星標(biāo)本公號??
評論
圖片
表情
