社内勉強会20110622の振り返り
時間がたってしまいましたが、今回も簡単に振り返ります。
Keep
- 前に出て話す2
- スライドに章ごとの扉を入れる2
Problem
- メール業が長引いて少し遅刻。定時に出られれば間に合うはず・・
- 少し時間オーバー。この時間で会話しながらやろうとすると、ボリューム大きすぎか。でもLTというより勉強会なので、会話の時間は取りたい。
- 少し準備不足で細かい説明ができなかった。
Try
- これだけは伝えたいポイントを明確にする2
- 原稿を作っておく2
- もう少し時間に対するボリュームを減らす。
JavaでCodeKata of Bowlingをやってみた
この動画をリスペクト。久々に見たのですが、前にも増して楽しかったです!
スはスペックのス〜RSpecによるテスト駆動開発の実演〜 - 角谷信太郎 (1/3)
開発環境
- Eclipse Java SEまたはEE
- JUnit4.4以上
- QuickJUnit(推奨)
- Subversive(任意)
- Vrapper(任意)
今回は、日本語 Eclipse 3.6 Pleiades All in One Javaを使用しました。これだけで日本語の開発環境が手に入ります。設定不要なのでとても楽です。
JUnitは、4.4からの新機能assertThatを使用していますが、アサーションをassertEqualsに変えてケース名をtest〜〜に変えれば3系でも問題ないです。
QuickJUnitはCtrl+9でテストクラス間切り替え、Ctrl+0でカーソル下のテストを実行できるため、テストが高速化されます。自動テストと同じくらい快適です。Eclipse3.6以上ならヘルプのEclipseマーケットプレイスからインストールできます。
SubversiveはEclipseからSubversionを使用する際の橋渡しプラグインです。今回はローカルにリポジトリを作成して、チーム=>プロジェクトの共有でfile://localhost/c:/〜〜〜/Bowlingを指定して逐一コミットしながら行いましたが、初めのうちはなくてもよいと思います。QuickJUnitと同じくEclipseマーケットプレイスからインストールできます。
Vrapperは任意ですが、ほとんど副作用なくEcllipseでVimのキーバインドを実現できるという素晴らしいプラグインです。参考:Eclipseのキーバインドをvim風にできるVrapperが素晴らしすぎる件について - ( ꒪⌓꒪) ゆるよろ日記
はじめ!
まずは、Bowlingプロジェクトを作成してGameクラスとGameTestクラスを作成します。
// Game.java public class Game { }
// GameTest.java public class GameTest { }
最初のテスト
以降は動画の手順をまるっとパクって進めます。
import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class GameTest { @Test public void すべてガターの場合() { Game game = new Game(); for(int i = 0; i < 20; i++) { game.roll(0); } assertThat(game.score(), is(0)); } }
Gameクラスにrollメソッドやscoreメソッドがないため、ビルドエラーになります。まずはエラーをなくしましょう。
public class Game { public void roll(int pins) { } public int score() { return -1; } }
ここでCtrl+0でテストを実行します。0を期待したのに-1が返ってくるのでRedになります。
RedになったのでCtrl+9で切り替えてコードを書きます。まずはいんちきで。
public class Game { public void roll(int pins) { } public int score() { return 0; } }
Ctrl9=>Ctrl+0でテストを実行すると、見事Greenバーが出現します。これで1ケース終了です。バージョン管理システムを使っていればここでコミットしましょう。
2つめのテスト
次はすべて1ピンの場合です。
@Test public void すべて1ピンの場合() { Game game = new Game(); for(int i = 0; i < 20; i++) { game.roll(1); } assertThat(game.score(), is(20)); }
Ctrl+0でRedになったことを確認してCtrl+9で切り替えます。
public class Game { private int score = 0; public void roll(int pins) { score += pins; } public int score() { return score; } }
Ctrl+9=>Ctrl+0でGreenバーを見ます。これで2ケース終了です。
第三のテスト
ここで次のテストを書くのですが、このままではGreenにするために大幅な修正が必要なことわかったので、いったんコメントアウトしてリファクタリングを行います。
// @Test // public void ストライクの場合() { // Game game = new Game(); // game.roll(10); //strike // game.roll(3); // game.roll(4); //24 // for(int i = 0; i < 18; i++) { // game.roll(0); // } // assertThat(game.score(), is(24)); // }
戻してリファクタリング
ここでリファクタリングを行います。リファクタリングする前とした後にCtrl+9=>Ctrl+0でバーがGreenになっていることを確認しましょう。
import java.util.*; public class Game { private int score = 0; private List<Integer> rolls = new ArrayList<Integer>(); public void roll(int pins) { rolls.add(pins); } public int score() { for (int roll : rolls) { score += roll; } return score; } }
まだGreenです。Greenなので、切りのいいところでコミットしておきます。
テスト復活
リファクタリングができたので、コメントアウトしたテストを復活させてCtrl+0で実行します。
@Test public void ストライクの場合() { Game game = new Game(); game.roll(10); //strike game.roll(3); game.roll(4); //24 for(int i = 0; i < 18; i++) { game.roll(0); } assertThat(game.score(), is(24)); }
見事Redになりました!
Redになったので、コードを書きます。
import java.util.*; public class Game { private List<Integer> rolls = new ArrayList<Integer>(); public void roll(int pins) { rolls.add(pins); } public int score() { int score = 0; int rollIdx = 0; for (int i = 0; i < 10; i++) { score += rolls.get(rollIdx) + rolls.get(rollIdx + 1); rollIdx += 2; } return score; } }
見事Greenになりました!リファクタリングによって機能追加がかんたんになっています。
パーフェクトゲームの場合
すぐに通るとおもいきや、点数計算でArrayIndexOutOfBoundsExceptionが発生してしままいました。Rubyでも同じロジックならFixnum + nilでエラーになると思うのですが、どこか間違えているでしょうか。
@Test public void パーフェクトゲームの場合() { Game game = new Game(); for(int i = 0; i < 10; i++) { game.roll(10); } assertThat(game.score(), is(300)); }
最後のゲームでストライクだった場合に対応させます。
import java.util.*; public class Game { private List<Integer> rolls = new ArrayList<Integer>(); public void roll(int pins) { rolls.add(pins); } public int score() { int score = 0; int rollIdx = 0; for (int i = 0; i < 10; i++) { if (rolls.get(rollIdx) == 10) { int next = ((rollIdx + 1) < rolls.size()) ? rolls.get(rollIdx + 1) : 0; int nextNext = ((rollIdx + 2) < rolls.size()) ? rolls.get(rollIdx + 2) : 0; score += 10 + next + nextNext; rollIdx += 1; } else { score += rolls.get(rollIdx) + rolls.get(rollIdx + 1); rollIdx += 2; } } return score; } }
テストケースのリファクタリング
ここでガターとストライクのロールをメソッド化します。
import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class GameTest { @Test public void すべてガターの場合() { Game game = new Game(); for(int i = 0; i < 20; i++) { rollGutter(game); } assertThat(game.score(), is(0)); } @Test public void すべて1ピンの場合() { Game game = new Game(); for(int i = 0; i < 20; i++) { game.roll(1); } assertThat(game.score(), is(20)); } @Test public void ストライクの場合() { Game game = new Game(); rollStrike(game); game.roll(3); game.roll(4); //24 for(int i = 0; i < 16; i++) { rollGutter(game); } assertThat(game.score(), is(24)); } @Test public void パーフェクトゲームの場合() { Game game = new Game(); for(int i = 0; i < 12; i++) { rollStrike(game); } assertThat(game.score(), is(300)); } private void rollGutter(Game game) { game.roll(0); } private void rollStrike(Game game) { game.roll(10); } }
まだまだGreenです。
スペアの場合
@Test public void スペアの場合() { Game game = new Game(); rollSpare(game); game.roll(4); game.roll(3); //21 for(int i = 0; i < 16; i++) { rollGutter(game); } assertThat(game.score(), is(21)); } private void rollSpare(Game game) { game.roll(5); game.roll(5); }
import java.util.*; public class Game { private List<Integer> rolls = new ArrayList<Integer>(); public void roll(int pins) { rolls.add(pins); } public int score() { int score = 0; int rollIdx = 0; for (int i = 0; i < 10; i++) { if (rolls.get(rollIdx) == 10) { int next = ((rollIdx + 1) < rolls.size()) ? rolls.get(rollIdx + 1) : 0; int nextNext = ((rollIdx + 2) < rolls.size()) ? rolls.get(rollIdx + 2) : 0; score += 10 + next + nextNext; rollIdx += 1; } else if (rolls.get(rollIdx) + rolls.get(rollIdx + 1) == 10) { int next = ((rollIdx + 2) < rolls.size()) ? rolls.get(rollIdx + 2) : 0; score += 10 + next; rollIdx += 2; } else { score += rolls.get(rollIdx) + rolls.get(rollIdx + 1); rollIdx += 2; } } return score; } }
Ctrl+9とCtrl+0だけでファイル切り替えとテスト実行できます。右クリックメニューからテスト実行を選択しなくてよいので、繊細なマウス操作が不要となり健康的ですね。
受け入れテスト
いよいよ受け入れテストを通してみます。
@Test public void 受け入れテスト() { Game game = new Game(); int[] pins = new int[] {1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6}; for(int pin : pins) { game.roll(pin); } assertThat(game.score(), is(133)); }
見事通りました!
最後までリファクタリング
さいごに、明後日の自分のためにリファクタリングを行って終了です。
import java.util.*; public class Game { private static final int FRAMES_OF_A_GAME = 10; private List<Integer> rolls = new ArrayList<Integer>(); public void roll(int pins) { rolls.add(pins); } public int score() { int score = 0; int rollIdx = 0; for (int i = 0; i < FRAMES_OF_A_GAME; i++) { if (isStrike(rollIdx)) { score += strikeBonus(rollIdx); rollIdx += 1; } else if (isSpare(rollIdx)) { score += spareBonus(rollIdx); rollIdx += 2; } else { score += scoreOfFrame(rollIdx); rollIdx += 2; } } return score; } private boolean isStrike(int rollIdx) { return rolls.get(rollIdx) == 10; } private boolean isSpare(int rollIdx) { return rolls.get(rollIdx) + rolls.get(rollIdx + 1) == 10; } private int scoreOfFrame(int rollIdx) { return rolls.get(rollIdx) + rolls.get(rollIdx + 1); } private int strikeBonus(int rollIdx) { int next = nextPins(rollIdx); int nextNext = nextNextPins(rollIdx); return 10 + next + nextNext; } private int spareBonus(int rollIdx) { int nextNext = nextNextPins(rollIdx); return 10 + nextNext; } private int nextPins(int rollIdx) { return ((rollIdx + 1) < rolls.size()) ? rolls.get(rollIdx + 1) : 0; } private int nextNextPins(int rollIdx) { return ((rollIdx + 2) < rolls.size()) ? rolls.get(rollIdx + 2) : 0; } }
おつかれさまでした!
追記
すべて終わってから試したところ、ArrayIndexOutOf〜〜は発生しませんでした。なので
private int nextPins(int rollIdx) { return rolls.get(rollIdx + 1); } private int nextNextPins(int rollIdx) { return rolls.get(rollIdx + 2); }
でOKです。仕組みはあとで考えよう・・。これはデジャヴ?
テストコード全容
import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import org.junit.Test; public class GameTest { @Test public void すべてガターの場合() { Game game = new Game(); for(int i = 0; i < 20; i++) { rollGutter(game); } assertThat(game.score(), is(0)); } @Test public void すべて1ピンの場合() { Game game = new Game(); for(int i = 0; i < 20; i++) { game.roll(1); } assertThat(game.score(), is(20)); } @Test public void ストライクの場合() { Game game = new Game(); rollStrike(game); game.roll(3); game.roll(4); //24 for(int i = 0; i < 16; i++) { rollGutter(game); } assertThat(game.score(), is(24)); } @Test public void パーフェクトゲームの場合() { Game game = new Game(); for(int i = 0; i < 12; i++) { rollStrike(game); } assertThat(game.score(), is(300)); } @Test public void スペアの場合() { Game game = new Game(); rollSpare(game); game.roll(4); game.roll(3); //21 for(int i = 0; i < 16; i++) { rollGutter(game); } assertThat(game.score(), is(21)); } @Test public void 受け入れテスト() { Game game = new Game(); int[] pins = new int[] {1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,6}; for(int pin : pins) { game.roll(pin); } assertThat(game.score(), is(133)); } private void rollGutter(Game game) { game.roll(0); } private void rollStrike(Game game) { game.roll(10); } private void rollSpare(Game game) { game.roll(5); game.roll(5); } }
ファイル名を途中から採番してリネームするスクリプト
file01.xxx file02.xxx
上のディレクトリに加えたファイルfugafuga.yyyを
file01.xxx
file02.xxx
fugafuga.yyy # 追加
file03.xxxにリネームしたいときに。
file01.xxx
file02.xxx
file03.xxx # リネーム
手順
以下のスクリプトをコマンドラインで実行します。
(リネーム対象ファイルかどうか判定できれば、同じ拡張子でも大丈夫です)
> ruby -r fileutils -e "Dir.glob('*').sort_by {|v| File.mtime(v) }.each_with_index{|f, i| FileUtils.mv(f, \"file#{ sprintf('%02d', i + 1) }.xxx\") if /\Afile\.xxx\z/ !~ f }"
展開するとこうなります。(ブロックチェーンをインデントしてみた)
require "fileutils" # ワイルドカード"*"(アスタリスク)でディレクトリ内の全ファイルを処理 Dir.glob("*").sort_by {|f| # タイムスタンプ順に採番する File.mtime(f) }.each_with_index {|f, i| # リネーム対象ファイルかどうか正規表現で判定(!~はマッチしない場合を真とする演算子) if /\Afile.*\.xxx\z/ !~ f # リネーム対象ファイルの場合、リネームを実行 FileUtils.mv(f, "file#{ sprint('%02d', i + 1) }.xxx") end }
ディレクトリ内に余計なファイルが混じっている場合、Dir.globの引数で排除してください。
(※globは正規表現ではなくワイルドカード方式です。)
朝日職場対抗戦のふりかえり
4/29に北海道将棋会館で行われた朝日職場対抗戦に参加してきました。
この大会は3人一組の団体戦で、職場対抗戦という名前ですが同好会での参加も可能です。久々の大会参加だったので、一局目は駒が手につかなくて焦りました。
今回も一人KPTをやってみたいと思います。
Keep
- 飛車を振る位置
- 目先の利よりも、遊び駒をなくすようにゆっくり指す
- 人数分の会費をおつりなしで準備する。(社会人として)
- 十分な戦費を用意する。(社会人として)
Problem
- このチーム名は社会人としてどうなのか
- このチーム名で新聞に載る可能性があった。もしかすると載ったかもしれない
- てぶらで行ったため、夜中に解散するまで賞品を先輩に持ってもらった
- てぶらで行ったため、筆記用具がない(備品を借用した)
- 首謀者と2人でギリギリに行ったため、巻き込んだ先輩を待たせてしまった。
Try
- 時間の使い方。序盤はある程度割り切って指す
- チーム名は真面目に付ける
- チーム名は事前にキメておく。
- チーム名は人任せにしない。いや、学生大会とかならいい名前だったんだけど。
- かばんを持って行く。
- かばんに筆記用具を入れておく。
- 余裕を持って行動する。
大会に参加していた小学生より低レベルですが、現状を認識しなければ成長はありえません。
いつか会社のメンバーで出てみたいなあ。
ボナンザVS勝負脳―最強将棋ソフトは人間を超えるか (角川oneテーマ21)
- 作者: 保木邦仁,渡辺明
- 出版社/メーカー: 角川書店
- 発売日: 2007/08
- メディア: 新書
- 購入: 16人 クリック: 2,027回
- この商品を含むブログ (71件) を見る
とある超人のデザインパターン〜Template Methodで部分的に異なる振る舞いを〜
はじめに
今回はGoFのデザインパターンの一つであるTemplate Methodパターンについて書きました。自分が覚えるためのまとめ記事なので説明が甘いところは多々あると思います。ご意見、補足等ありましたらぜひどうぞ。
序
(V)o\o(V):フォフォ・・。
(o|o):どうしたんだ、元気ないな?
(V)o\o(V):フォッフォ・・。
(o|o):ん?分身の行動パターンが単調で困っている?
(V)o\o(V):フォフォ。
(o|o):分身って全部同じクラスから作ってるんだっけ?
(V)o\o(V):フォ。
(o|o):そうだなー、じゃあ新しいクラスを作ったらどう?
(V)o\o(V):フォ?
機能を追加
(o|o):例えば、今のクラスがこれだとする。
public class Baru { public action() { move(); attack(); defense(); } private move() { ・・・ } private attack() { ・・・ } private defense() { ・・・ } }
(o|o):メソッドの抽出はよくできているが、これじゃあ動きが単調になるのも仕方ない。
(V)o\o(V):フォ。
(o|o):そこで、別々の攻撃手段を持ったクラスを作成してみる。
// 火炎で攻撃するクラス public class FireBaru { public action() { move(); attack(); defense(); } private move() { ・・・ } private attack() { fire(); } private defense() { ・・・ } } // 稲妻で攻撃するクラス public class ThunderBaru { public action() { move(); attack(); defense(); } private move() { ・・・ } private attack() { thunder(); } private defense() { ・・・ } }
(o|o):これで君の仕様は満たされたわけだが、コードがかなり重複しているね。
(V)o\o(V):フォ。
(o|o):そこで、Template Methodパターンを適用してみよう。
Template Methodパターンを適用
(o|o):まず、共通部分を抽象クラスにまとめる。
(V)o\o(V):フォ。
(o|o):で、変化をつけたい動作は実装クラスでオーバーライドさせるってわけ。
// 抽象クラス public abstract class Baru { // テンプレートメソッド public void action() { move(); attack(); defense(); } // 共通の動作 private void move() { ・・・ System.exit(0); } // 変化を付けたい動作 protected abstract void attack(); // 共通の動作 private void defense() { ・・・ } } // 火炎で攻撃するクラス public class FireBaru extends Baru { @Override protected void attack() { fire(); } } // 稲妻で攻撃するクラス public class ThunderBaru extends Baru { @Override protected void attack() { thunder(); } }
(o|o):これでどうだい。
(V)o\o(V):フォッフォー!
(o|o):これでお前を倒せるぞ?なるほどね。がんばって!
(V)o\o(V):フォ!
(o|o):どういたしまして。
後日談
(V)o\o(V):フォ・・(分身が動いたらオレまで消える)
(o|o):(クスッ)
本日のクレーム
監督:クラス、増えすぎ!
参考
サルでもわかる 逆引きデザインパターン 第3章 逆引きカタログ J2EE編 Template Method(テンプレートメソッド)
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (397件) を見る
パターン指向リファクタリング入門~ソフトウエア設計を改善する27の作法
- 作者: ジョシュア・ケリーエブスキー,小黒直樹,村上歴,高橋一成,越智典子
- 出版社/メーカー: 日経BP社
- 発売日: 2005/08/04
- メディア: 単行本
- 購入: 11人 クリック: 313回
- この商品を含むブログ (130件) を見る
リファクタリング―プログラムの体質改善テクニック (Object Technology Series)
- 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2000/05
- メディア: 単行本
- 購入: 94人 クリック: 3,091回
- この商品を含むブログ (312件) を見る
社内勉強会のふりかえり
せっかく発表者の役割をもらっていたのですが。。
参考:KPTについて
KPTを使ったプロセス改善:An Agile Way:オルタナティブ・ブログ
Keep
- 前に出て話す
- スライドに章ごとの扉を入れる
Problem
- 大遅刻をしてしまいとても迷惑をかけた
- 話のポイントが曖昧
- 少し間をとりすぎかも
- 連絡手段がTwitterだけ
Try
- 行けなくなりそうなら、例え前日でも日程変更等を相談する
- これだけは伝えたいポイントを明確にする
- 原稿を作っておく
- メルアド等を聞いておく
他にもありますが、今回はこれくらいで。プレゼンには少しずつ慣れてきたので、次回はもっと内容面の準備をしっかりしよう。