10月から新しい現場で作業してます。
で、その現場は典型的な大規模案件でして、
前の現場と比較して鈍重かつ洗練されていない開発プロセスになってしまっている感じ。
何をするにしても意思決定が遅い。無駄な作業が多い。進捗はエクセルで管理。等々…
でも弊社に与えられたミッションを達成するには、このままでは困るため、
少しでも作業効率を上げようと画策しています。(周辺環境から逸脱しすぎない範囲で)
その一環として、昔どこかで見た『AspectJで自動コードレビュー』はどんなもんだべ、と思ったので
早速試してみました。
環境設定
今のお客様はEclipse3.5という古い環境を使用していますので、
AJDTのEclipseプラグインは上手く動作しませんでした。
なので仕方なく、昔ながらのantタスクでajc(AspectJコマンドラインコンパイラ)を動かすことに。
AJDTでiajcタスクが用意されていますのでそれを使用。
詳しい設定方法は割愛しますが、
特定のソースディレクトリにおいているアスペクト(*.aj)を全部取り込むことができるので、
コーディング規約を順次別のajファイルとして追加していけるので、管理上便利。
コーディング規約の実装例
最初に書いたルールはコレ。
public aspect DBUnitTestCaseShouldExtendsFrameworkClass { declare error: execution(jp.co.hoge..*DBUnitTest.new(..)) // コレでもいけるはず→ within(jp.co.hoge..*DBUnitTest) && !call(jp.co.hoge.framework.BaseDBUnitTestCase.new(..)) : "DBUnitのテストケースはフレームワーク提供のBaseDBUnitTestCaseクラスを継承しなければならない。"; }
です。早速トリックを活用している感じですが。。。
AspectJはそのコンセプト上、コードの特定の場所を指定するパターンマッチングに特化した言語です。
だから、『クラスABCを継承しているクラス』と言う静的なマッチングができない。なぜなら、それは処理ではないから。
要するに『クラスABC内で、メソッドhogeを呼んでいる処理』というマッチングしかできない、と言うことです。
だから、無理やり
execution(jp.co.hoge..*DBUnitTest.new(.))
でコンストラクタ(コンストラクタも一種のメソッド)を指定し、
call(jp.co.hoge.framework.BaseDBUnitTestCase.new(..))
で親クラスのコンストラクタ呼び出し、すなわちsuper()を指定し、それのNOTにすることで、
- jp.co.hogeで始まるパッケージ名を持ち、クラス名のサフィックスがDBUnitTestであるクラスのコンストラクタ呼び出し内で、jp.co.hoge.framework.BaseDBUnitTestCase『以外の』super()を呼び出している箇所→(すなわち、BaseDBUnitTestCaseを継承していない)
というマッチングを行います。で、これが要するに
『BaseDBUnitTestCaseを継承していない、*DBUnitTestという名前のクラス』
と事実上等価になるわけ。うーん、回りくどい。
まぁこんな感じで、簡単なルールはどしどし作成できそうな感じです。
ちょっとトンチみたいな発想を強いられることも多いですが。
今のところの課題
繰り返しになりますが、AspectJは特定の処理をしている場所にマッチさせることに特化した言語です。
だから、『XXXをしていない』箇所にマッチさせることが原理上不可能です。
でも、コーディング規約では『XXXをした場合はYYYをすること』系のルールも多い。
例えばリソースをオープンしたらクローズすべし、とか。まぁこれは規約以前に当然のことですけど。
今考えているのは、AspectJコンパイラのAPIを直接叩いて
『XXXをしている箇所にマッチした』というイベントが発生したら、
そのクラス内やメソッド内で『YYYをしている箇所』にマッチするものがあるか否かを判定する、
という処理を実装できないかな?ということ。まだ調べてないけども。
誰か、良いアイデアありませんか?
ナイスなアイデアには、酒おごりますぜー。
いっその事
まぁこんな調子で、AspectJを使って簡単な自動コードレビューができそうな感じですが、
いっその事、コードレビューに特化したDSLを誰か書けばいいんじゃないでしょうか。
仕様記述言語みたいな、アカデミックで現実離れしているやつじゃなくて、ね。
え?僕は嫌ですよ?メンドクサイからw