Route 477 - GNU Smalltalkのクラス階層をグラフ化した が面白かったので、参考にして Squeak Smalltalk (Squeak3.10J) と Ruby1.9 (Ruby1.9.1p0) のを作ってみました。
Squeak Smalltalk | Ruby |
![]() |
![]() |
Squeak Smalltalk には Traits(ミックスインみたいで、もう少し柔軟な機構)があるのでこれを青で、抽象クラスは #subclassResponsibility を含むメソッドを持つクラス OR 自分が抽象クラスだとコメントで主張しているクラス OR 名前が Abstract で始まるクラス で判断し、赤で塗りつぶしています。Ruby は抽象クラスの判断ができなかったので、モジュールだけ青にしてみました。
Squeak Smalltalk の一部のクラスが孤立しているように見えますが、これはおおざっぱな抜粋のせいで、実際には継承ツリーの一部になっています。念のため。
Squeak Smalltalk 用
| abstractClasses nodes file packages | abstractClasses := OrderedCollection new. nodes := OrderedCollection new. packages := #('Kernel' 'Collections' 'Exceptions' 'Files' 'Graphics' 'Morphic' 'Multilingual' 'Network' 'ST80' 'SUnit' 'Sound' 'Compiler' 'Tools' 'Traits'). Smalltalk allClassesDo: [:class | (packages includes: (class category copyUpTo: $-)) ifTrue: [ | hasSubResp comment hasAbstComment hasAbstName | hasSubResp := class selectors anySatisfy: [:sel | (class >> sel) messages includes: #subclassResponsibility]. comment := class comment asString asLowercase. hasAbstComment := #('i am an abstract' 'i am the abstract') anySatisfy: [:each | comment includesSubString: each]. hasAbstName := class name beginsWith: 'Abstract'. (hasSubResp or: [hasAbstComment or: [hasAbstName]]) ifTrue: [abstractClasses add: class]. nodes add: class -> class superclass. class traitComposition traits do: [:trait | nodes add: class -> trait]]]. file := FileStream forceNewFileNamed: 'squeakClasses.dot'. file lineEndConvention: #lf. [ file nextPutAll: 'digraph G{'; cr. file nextPutAll: 'graph[rankdir=RL];'; cr. file nextPutAll: 'edge[arrowsize=0.5];'; cr. file nextPutAll: 'node[fontsize=12, shape=box, width=0.05, height=0.05];'; cr. abstractClasses do: [:abst | file nextPutAll: abst name, ' [style=filled, fillcolor="#FF8888"];'; cr]. Trait allInstances do: [:trait | file nextPutAll: trait name, ' [style=filled, fillcolor="#8888FF"];'; cr]. nodes do: [:assoc | file nextPutAll: assoc printString, ';' ; cr]. file nextPutAll: '}' ] ensure: [file ifNotNil: [file close]]
Ruby 用
nodes = [] classes = [] ObjectSpace.each_object(Class){ |c| classes << c } modules = [] ObjectSpace.each_object(Module){ |m| modules << m unless classes.include?(m) } def nil.name; "nil" end classes+modules.each{ |c| nodes << [c, c.superclass] ancestors = c.ancestors (ancestors[1..(ancestors.index(c.superclass) || 1)-1]).each{ |m| nodes << [c, m] } } puts [ "digraph G{", "graph[rankdir=RL];", "edge[arrowsize=0.5];", "node[fontsize=12, shape=box, width=0.05, height=0.05];", modules.collect{ |m| "\"#{m.name}\" [style=filled, fillcolor=\"#8888FF\"];" }, nodes.collect{ |pair| "\"#{pair.first.name}\" -> \"#{pair.last.name}\";" }, "}" ].flatten.join("\n")