コミケ告知

サークル活動の詳細は circle タグの記事へ。
2012年9月15日土曜日

リファクタリング関連書籍を読み漁って

# はてなダイアリーから移動した記事です。あまり真面目に整形していません。

リファクタリング関係の書籍。少し前の業務で結構な時間を(未熟な自己流の)リファクタリングに費やしたことがあったので、その反省も兼ねて読み漁っていました。今思い起こすと、再発明的なことをたくさんしていたように思います。先に知っていれば。
整理目的で、リファクタリングに対する現時点の理解を書き残しておきます。

Java言語で学ぶリファクタリング入門 / 結城浩



使用頻度の高いであろうリファクタリング技法について、それぞれかなりページ数を割いて説明しています。紹介する数よりも、説明の濃さに重点を置いた本。よく見かけそうで、しかもやろうと思えばすぐ適用出来そうな技法が選ばれています。

  1. シンボリック定数によるマジックナンバーの置き換え
  2. 制御フラグの削除
  3. アサーションの導入
  4. ヌルオブジェクトの導入
  5. メソッドの抽出
  6. クラスの抽出
  7. クラスによるタイプコードの置き換え
  8. サブクラスによるタイプコードの置き換え
  9. State/Strategyによるタイプコードの置き換え
  10. 例外によるエラーコードの置き換え
  11. Factory Methodによるコンストラクタの置き換え
  12. 観察されるデータの複製
  13. 委譲による継承の置き換え
  14. 委譲の隠蔽
  15. 継承の分割

各章が20ページ前後の容量であり、章末には練習問題。トレードオフの説明に分量を割いているのが印象的で、初めてリファクタリングという開発技法および思想に触れる人に向いていそうです。リファクタリングは技法よりもまず、思想(意義や用途の理解)を取り入れないことには始まらないので。
紹介されている技法の数は少ないですが、それはリファクタリングに価値を感じたら、つまりこの本の内容を理解できたなら、他の本を読んでカバーすることになります。

リファクタリング―プログラムの体質改善テクニック / マーチン・ファウラー




序盤でリファクタリングの意義・基本的なやり方・適用場面等、リファクタリング全般に対する姿勢を語り、中盤移行はひたすらリファクタリング技法の紹介。ファウラーさんといえば難解なアナリシスパターン本を思い浮かべてしまいますが、この本は非常に分かりやすいです。コワクナイヨー
それなりに古い本であることは、頭の隅に置いて読む必要があります。本文で用いられている言語はJavaだけども、ジェネリクスなどないJavaです。いまどきのJava、あるいはいまどきの別の言語では、もっと洗練された技法が存在することもあるので。


序盤にあるリファクタリングの意義というのは例えば、以下のようなモチベーションです。

  • ユーザーが望む変更を気軽にコードに反映させるわけにはいかないとなると、結局困るのは開発者であるあなたです。
    • まず機能追加が簡単になるようにリファクタリングをしてから追加を行うこと。
  • 昨日の決定が、今日には意味がなくなったと気づいたならば、過去の決定を変更してしまえばよい。
    • 明日には通用しないかもしれないやり方を通せば、いつか敗者になってしまう。
  • コンパイルに余分なCPUサイクルをちょっと使っても問題にならないが、コード修正に一週間余計にかかれば問題

リファクタリングって意味あんの?という段階を超えるのが最重要課題なので、前半が大事なところなのだと思います。


ところで、文中にはコード例がたくさん出てきますが、これが可能な限りページを跨がないように徹底されています。わかりやすい。そうするためにあっさりと改ページするため、章末でないのにスカスカなページも結構あります。とてもリファクタリング本らしさを感じるところ。

コンパイラが理解できるコードは誰にでも書ける。すぐれたプログラマは、人間にとってわかりやすいコードを書く。



パターン指向リファクタリング入門 / ジョシュア・ケリーエブスキー



構成はファウラー本と同様に、序盤で思想的なところ、中盤以降に技法紹介です。文中でファウラー本のリファクタリング技法名称を頻繁に引用していますが、読んでいなくても大した問題ではありませんでした。小さいレベルのリファクタリングは技法的には難しいことはないし、ぐぐれば何かしら解説が出てくるので。自分はこれをファウラー本よりも先に読みました。


前半のモチベーション部分では、ファウラー本と似たような話の他には、デザインパターンとリファクタリングの関係について1章が割り当てられています。ファウラー本の「パターンは目指そうとするところ、リファクタリングはどこか別のところからそこへ到達する方法」というくだりを引用した上で、そのあたりを少し詳しく説明してありました。
「パターン魔(pattern happy)」についての言及もあります。紹介されていたパターン魔のサンプルはこちら。

ベタ書きでも十分わかりやすいところにデザインパターン導入するとかバカじゃねーの?って話。この方向で貶されている場面って、あまり目にしませんね。


技法の紹介部分では書名の通り、既存のコードにデザインパターンを適用していくサンプルが並べられています。第5章のタイトル「パターンを取り入れるリファクタリングのカタログ」が表す通り。

読んだ3冊の比較

結城さんの本は明らかに入門本です。パターン指向~も書名に入門と付いていますが、ファウラー本と同程度のレベルでしょう。これら2冊は、デザインパターンがだいたい理解できる程度の理解は必要です。パターンを理解していないと、なんでそんな風に直すの?という疑問に負けてしまうので。

技術レベルだけ見れば、デザインパターンが大体わかる程度というのも、そんなに高い要求ではないでしょう。しかし、リファクタリングの意義なり目的を理解しないまま本文を読み進めると、「微妙な技法がたくさん載っている退屈な本だったな」で終わってしまうかもしれません。書いてある手順は余裕で理解できるけれども身に付かず忘れてしまう、あるいは読むのに飽きるのではないかと思います。

コードを修正したり機能追加したりという方向の経験を積んでいて、「ああー同じようなこと○○のときにやったな…」と実感するところがあれば、受け入れやすいのでしょうが、これらは一口にコーディング歴では表せないところ。もし有難味を感じられないのなら、コーディング経験が十分だとしても、結城さんの入門書から読んだ方がいいかもしれません。

ファウラー本とパターン指向~の比較では、後者の方がよりモチベーションが伝わりやすいように思います。著者の知名度は低いですが、出た時期が5年ほど違っていて、ファウラー本以外にも色々と参照した上で整理されています。自分がリファクタリングの意義をそれなりに理解できたのは、この本でパターンとリファクタリングの立ち位置の説明がなされていたからです。また、パターンを導入するような例を示されると現実味があって、リファクタリング何それ?程度の人間が思考を転換するには良かったのかもしれません。
パターン指向~の邦訳本はあまり売れていないのか、ちょっと入手性に難がありそうなのが気になりますが…。

リファクタリングの立ち位置を考える

パターンは目指そうとするところ、リファクタリングはどこか別のところからそこへ到達する方法


二度目の引用。どこか別のところは、最初は無であり、そこから完成形にまっすぐ進んで終了できるならば幸せです。それが夢物語でまっすぐウォーターフォール出来ないことは明らか。目的地点が変わってしまうことは普通であり、その目的変更を取り入れないとひどいソフトウェアが出来るというのも、やはり当然のことです。
そこで出てくるのが、リファクタリングであると。

  • パターンへの理解により、目的達成のための設計能力を得る
    • リファクタリングがわからないと、方針変更に弱い
  • リファクタリングへの理解により、正しい目的地への移動能力を得る
    • パターンの知識が少ないと、進む方向がわからない

こんな感じで相補関係にありそうです。

ファウラー本の目次を眺めているだけでもわかる通り、リファクタリングには真逆の技法がたくさんあります。「サブクラスの抽出」と「階層の平坦化」など。パターン魔の例で示されるように、過ぎたるは及ばざるがごとし、という考えが前提のひとつとしてあると思います。無用な拡張性は悪であり、予想外のところで拡張が必要になったならば、そこで設計を変えていく、と。

テストとの関連性

リファクタリングの意義がわかって、ようやくテストの価値や方向性も感じられるようになってきました。仕様を定義したテストを書いて、それを満たすように設計をしよう!というだけでは、テストが意味ないとは言わないけれども、こんな分かりきったテストを書いてもな…と考えてしまう部分が大きかったのです。
中身を入れ替えるようになると、細かいステップで足元を確かめながら進める有難味を非常に感じます。逆に、もう二度といじらないところや、細粒度すぎて内部実装の変更時に使わなくなるレベルまで、こまごまとテストを書く意味は薄いですね。

総括

リファクタリング、もっと早く読んで知っておけばよい技術でしたが、これまできっかけがありませんでした。ソースコードの修正する人が読む?みたいなイメージがありましたが、ソフトウェア開発全般に有用な技術でした。
サンプルがJavaな本ばかりですが、難しいJava特有の技などは出てこないので、Javaあまり知らないとかJava嫌いとかで避けてしまうのは、勿体なさすぎます。(自分も若干そんな事考えていました)


2012年9月13日木曜日

Beautiful Code という、見た目からオライリーらしからぬ本

# はてなダイアリーから移動した記事です。あまり真面目に整形していません。

リファクタリング関係の本をいくつか読み終わったあと、なんとなく目につくところにあったので、Beautiful Codeを読んでいました。見た目も中身も、昔からの(動物系のシンボルが表紙に描かれた)いかにもオライリーな本ではなく、読み物系の詰まったいまどきのオライリー本という印象です。



目次を見るとわかる通り、各著者が1章ずつ執筆するスタイルです。おそらく、Beautiful Codeってタイトルの本で何か書いてよ!程度のゆるいテーマで投げられたのでしょう。解釈もさまざまでした。コード自体の美しさの場合もあれば、設計思想である場合もあり、あるいは、筆者のやったことが書いてあるけど何も面白くねえ…という章も。
自分が特に楽しく読めたのは以下の章でしたが、どれが良いかについては興味のある・よく接している問題領域に依存するものでしょう。

  • 15章 美しいデザインの長期にわたる恩恵 / CERNのライブラリにおけるコーディング方針
  • 26章 労力節約のアーキテクチャ / ネットワークサービスの設計
  • 28章 美しいデバッグ / デバッグ手法

章の並び順は気にせず、面白そうなところからつまみ食いしていく読み方をお勧めします。

総括

この本全体の印象に合う表現が、おまけで付いている対談コーナーにある、まつもとゆきひろ氏の発言のひとつにありました。

人によってはどの章を面白いと感じるかは違うと思いますけど、これだけあると、どれか1つは必ず面白いのが見つかると思います。


もちろんこれはポジティブ方向から見た表現ですが、ややネガティブに書くならば、「33あるうち、1つくらいは楽しめるかも」ということ。実際そこまでヒット率(?)が低くはありませんでしたが。とても良いのが数本、まあ楽しめるのまで合わせて、10本くらいですかねえ。
さまざまな言語、幅広い問題領域、OSのメモリ管理からユーザーインタフェースまで広範囲のレイヤーに散らばった話が33本。馴染みのある問題領域の話でなければ意味がない…なんてことは無いですが、著者に近い視線で深く理解できるかどうか、理解できる美しさなのか、はまた別の話。思考プロセスだけ参考にするにしても、問題領域や言語が違ったときに正しいかどうかは結局考えないと。


個人で4000円出して欲しいかと言われると、Noですね。しかし、研究室に一冊・職場に一冊といった存在としては、悪くないのではないでしょうか。


2012年8月25日土曜日

Visual Studio 2010 Express + NUnit セットアップ段階のくだらない罠いくつか

# はてなダイアリーから移動した記事です。あまり真面目に整形していません。

環境はWindows 7 + Visual Studio 2010 Express Edition。

NUnit.orgがクソ重くて繋がらない

ダウンロードすべきファイルは別のドメインにあるようなので、ダウンロードページのGoogleのキャッシュからたどればOK。

セットアップ時に文句を言われる

f:id:moccos_info:20120826054045j:image:w300
2.6.0.1205をインストールしようとしたところ、こんなダイアログが出てインストールが止まりました。
2.5.10.11092 を試したらこれは出ませんでした。なんで2.6で突然古いバージョンの.NETが必要なんてことになってるんでしょうね?2.5系のインストールの時には、.NET1.1系はインストールオプションで選択できるようですが。


設定は詳しい記事があるので今更新しく書くことはなし。

プロジェクトを読もうとすると文句を言われる

f:id:moccos_info:20120826054341j:image:w350
メッセージの通り、x86のバイナリをx64用のNUnitに食わせたので文句を言われています。
NUnitの実行ファイルには64bit(x64)版と32bit(x86)版があって、スタートメニュー等にショートカットが用意されているのはx64の方。同じディレクトリに32bitのnunit-x86.exeがあるので、32bitアプリはこっちで。Program Files (x86)の下なんですけどね…

コードを書き換えてもNUnit側で更新されない

NUnitの問題じゃないんですが、Visual C#の初期設定だと、上部のメニューバーにあるべきReleaseやらDebugやらの切り替えメニューが表示されず、Release版のみが作成されるようです。NUnitはDebugビルドを見ているようなので、初期設定のままではだめです。そして、コード書き換えたらビルドが必要です。

Debugビルドできるように設定しておかないと、サンプルの実行もできなくて悲しい思いをすることになります。
(ここを設定する前にテスト用の設定に取り掛かる人は少ないかもしれませんが)

2012年7月12日木曜日

ByteBufferの挙動を確かめるには

# はてなダイアリーから移動した記事です。あまり真面目に整形していません。

java.nio.ByteBufferってクラスがあります。Byteの配列を直接扱うのはクールではない、という純情な感情から生まれたクラスに違いありません。たぶん。

putInt(x)や getFloat() のようなひとつ上のレイヤーのメソッドで値を出し入れ出来たり、asLongBuffer()などで特定型が並んだものとして扱えたりと、何かと便利です。

ただ、隠蔽されているメンバ変数、特にpositionの動きには、慣れるまでは翻弄されるかもしれません。メソッドによっては自動的にインクリメントしてくれたり、ByteBufferを受け取るメソッド内で読み取っていたりするので、何がどうなっているかわからずに使うと、はまる可能性があります。参照透明性とは真逆の存在ですね。仮にasReadOnlyBufferで変換しても、中でpositionやlimitは勝手に動くので。

対話環境を起動

こういうときは対話環境、すなわちコマンド打ったらその場で実行してくれるような環境で遊んでみるのが分かりやすいです。おもむろにscalaのREPL(対話環境)を起動!
Javaユーザーも、とりあえずScalaをインストールしましょう。一行ずつコマンド打つ程度なら、大して違うものでもないので大丈夫。

~ % scala
scala> import java.nio.ByteBuffer
import java.nio.ByteBuffer

scala> ByteBuffer.allocate(128)
res0: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=0 lim=128 cap=128]

返り値は自動的にresNに入ります。res0にByteBufferが確保できたので、以後この値を使います。
変数に入ると同時にStringとして表示してくれるのが、特に今回は有難いところです。position/limit/capacityの値がすぐ確認できます。

scala> res0.putInt(123)
res1: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=4 lim=128 cap=128]

scala> res0.putInt(456)
res2: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=8 lim=128 cap=128]

値を置くと勝手にpositionが進んでいますね。

scala> res0.putInt(0, 789)
res3: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=8 lim=128 cap=128]

index指定のput系ではposition変わらず。

scala> res0.position()
res4: Int = 8

scala> res0.position(4)
res5: java.nio.Buffer = java.nio.HeapByteBuffer[pos=4 lim=128 cap=128]

position()で現在のpositionを得られます。(scalaなので()要らないけど、しばらくはJava的表記)
引数ありのpositionを使うと、positionを動かせます。

scala> res0.getInt()
res6: Int = 456

scala> res0
res7: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=8 lim=128 cap=128]

scala> res0.getInt(4)
res8: Int = 456

scala> res0
res9: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=8 lim=128 cap=128]

getInt()でもpositionが自動的に移動。やはりindex指定ならばposition変わらず。

scala> res0.rewind()
res10: java.nio.Buffer = java.nio.HeapByteBuffer[pos=0 lim=128 cap=128]

rewindで先頭に巻き戻ります。でも返るのがBufferなので、若干びびります。

scala> res0.limit(4)
res11: java.nio.Buffer = java.nio.HeapByteBuffer[pos=0 lim=4 cap=128]

scala> res0.putFloat(1.5f)
res12: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=4 lim=4 cap=128]

scala> res0.putFloat(2.6f)
java.nio.BufferOverflowException

limitもpositionと同じような引数あり・なし版があり。limitより先に書きこもうとすると、溢れた扱いになります。

実行例がやたらと長くなりましたが、とにかくjavaいじるのにscala便利だぜってことで。

ついでにDatagramChannel

scala> import java.nio.channels.DatagramChannel
import java.nio.channels.DatagramChannel

scala> import java.net._
import java.net._

scala> val ch = DatagramChannel.open(StandardProtocolFamily.INET)
ch: java.nio.channels.DatagramChannel = sun.nio.ch.DatagramChannelImpl@11045dfe

resNが複数あると紛らわしいのでval chに入れてみた。
(Javaっぽく書こうとしていたことは忘れる。importのワイルドカードもちょっとJavaと異なる)

scala> res0.limit(100)
res16: java.nio.Buffer = java.nio.HeapByteBuffer[pos=4 lim=100 cap=128]

scala> ch.send(res0, new InetSocketAddress("localhost", 10000))
res17: Int = 96

scala> res0
res18: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=100 lim=100 cap=128]

sendの返り値は、送信したByte数です。positionからlimitまで送信、そしてpositionは移動。
(自動インクリメントのputでデータを配置したあと、positionを巻き戻し忘れると…)


2012年2月2日木曜日

Erlangでのディレクトリ作成、file_lib:ensure_dir/1

# はてなダイアリーから移動した記事です。あまり真面目に整形していません。

Erlang -- filelib
ディレクトリがなければ作る、既に存在しても正常動作として、何もしない。

Eshell V5.9  (abort with ^G)
1> filelib:ensure_dir("log").
ok

ok。しかしディレクトリが作成されていないので、log/hoge.logなど開こうとすると死にます。ensure→ok→失敗ってなんじゃい。

2> filelib:ensure_dir("log/").
ok

末尾にスラッシュを付けるのが正解でした。