「TDD Boot Camp 札幌」にSqueak Smalltalkで参加
1日目は @mrknさんとペアプロで、2日目はソロでしたが、例題のレガシーコードを昼休みに Squeak Smalltalk に書き直してから、仕様化テスト書きとリファクタリングまでやってみました。両日ともとても充実していました。振り返りでも言いましたが、テストのある生活の安心感と、TDD の躍動感・ライブ感と Smalltalk との相性の良さをあらためて実感できました。
元になったレガシーコードの公開は避けて欲しいということでしたので、書き足したテストとリファクタリング後のコードだけ載せておきます。もっとも当日ご参加のみなさんに感じ取っていただけたであろう Smalltalk のパワーについては、この拙いコードだけからはまったく伝えられないので、それがちょっと残念です。^^;
風邪で不調をおしての来札、熱いご講演とご指導をいただいた輝きの @t_wadaさん、運営の @shuji_w6eさんはじめスタッフの皆様、ありがとうございました。今回は準備が間に合わなかったのですが、Smalltalk のリファクタリングブラウザのデモとかできたらよかったですね。またの機会に。
Object subclass: #StrParser category: 'TDDBC-LegacyCode' StrParser >> parse: string | separators subStrings tokens | separators := self class separators. separators do: [:sep | subStrings := string findBetweenSubStrs: {sep}. subStrings size >= 2 ifTrue: [ tokens := {subStrings at: 1. sep. subStrings at: 2}. ^ tokens. ] ]. ^ nil StrParser class >> separators ^#('<=' '>=' '<' '>' '==' '+=' '-=' '!=' '=' '+' '-' )
TestCase subclass: #StrParserTest category: 'TDDBC-LegacyCode' StrParserTest >> test01インスタンス化する | sp | sp := StrParser new. self assert: sp notNil StrParserTest >> test02parseに空文字列を渡す "→nilを返す " | sp result | sp := StrParser new. result := sp parse: ''. self assert: result isNil StrParserTest >> test03parseに1プラス1を渡す "→演算子の前後で区切って返す " | sp result | sp := StrParser new. result := sp parse: '1+1'. self assert: result = #('1' '+' '1') StrParserTest >> test05parseメソッドに1イコールイコール2を渡す "→演算子の前後で区切って返す " | sp result | sp := StrParser new. result := sp parse: '1==2'. self assert: result = #('1' '==' '2') StrParserTest >> test06parseメソッドに1空白イコール空白2を渡す "→演算子の前後で区切って返す。トークンの前後のスペースは放置。 " | sp result | sp := StrParser new. result := sp parse: '1 = 2'. self assert: result = #('1 ' '=' ' 2') StrParserTest >> test07parseメソッドに1空白小なり空白2を渡す "→演算子の前後で区切って返す。 " | sp result | sp := StrParser new. result := sp parse: '1 < 2'. self assert: result = #('1 ' '<' ' 2') StrParserTest >> test08parseメソッドに1空白小なり空白2空白小なり3を渡す "→2の直後の区切り文字列までをトークン対象としてトークンを返す。 " | sp result | sp := StrParser new. result := sp parse: '1 < 2 < 3'. self assert: result = #('1 ' '<' ' 2 ') StrParserTest >> test09parseメソッドがトークン化できる区切り文字列群 | separators sp result | separators := StrParser separators. sp := StrParser new. separators do: [:sep | result := sp parse: '1 ', sep, ' 2'. self assert: result = {'1 '. sep. ' 2'}] StrParserTest >> test10parseメソッドに1小なりを渡す "トークンを2つ以上切り出せないためnilを返す" | sp result | sp := StrParser new. result := sp parse: '1 <'. self assert: result isNil StrParserTest >> test11parseメソッドに1小なり小なり2を渡す "1<<2でも1<2と同じ振る舞いをする" | sp result | sp := StrParser new. result := sp parse: '1<<2'. self assert: result = #('1' '<' '2') StrParserTest >> test11parseメソッドにabc小なりdefを渡す "アルファベット文字列を与えた場合でも演算子で区切ったトークン列を返す。" | sp result | sp := StrParser new. result := sp parse: 'abc<def'. self assert: result = #('abc' '<' 'def') StrParserTest >> test01セパレーターの定義順に問題はないか "→あとで登場するセパレーターはそれまでのセパレーターの部分文字列ではいけない。" | separators | separators := StrParser separators. (1 to: separators size) do: [:idx | (separators first: idx - 1) do: [:sep | self deny: ((separators at: idx) includesSubString: sep)]]