コミケ告知

サークル活動の詳細は circle タグの記事へ。
2013年1月15日火曜日

viユーザー専用PS3コントローラーを作ってみた

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

PS3コントローラーの配線を外に引っ張り出して、テンキーを繋いでみました。
ゲームのコントローラーの入力(=スイッチ)を引っ張りだして挿げ替えただけですが。

f:id:moccos_info:20130115063320j:image:w300f:id:moccos_info:20130115063333j:image:w300


動画版も作ったんだけど、動画にして面白いものではなかった…(゚Д゚) 原理的にはとても単純だし、見た目がすごくなるわけでもないので、不向きでしたね。
D

動機

東方シリーズでSTGを覚えたため、キーボード操作でしかSTGを遊べない身体になっていました。PS2のSTG買っても、十字キーではとてもプレイする気にならなかったのです。勿体ないので、いつか作ろうとは思っていました。
コントローラーの入力スイッチ部分に、カーソルキーに相当する入力デバイスの出力を繋げれば出来るので、回路は簡単です。外形加工は多少めんどくさいですが…。

昨年末に、そろそろ作ろうかなーと思い立ったとき、ちょうどVimを使い始めの頃だったので、ついでにVimのカーソルキー配置にしようかという余計な考えが混入し、こうなりました。

素材

市販の適当なPS3コントローラーと、適当なUSBテンキーを組み合わせました。特定の製品に依存するようなことはないので、好きなもの(安いもの)を使えば良いはずです。今回使ったのは以下の製品。



コントローラーはSTG目的なので、有線を選択しました。この製品は安いし、レビューを見るとPS2規格ソフトウェアでも動作するようなので選びました。
テンキーはスイッチ部分しか使わないので、入力感に拘るつもりがなければ、安さ追求で良いでしょう。秋葉原だと200円くらいで売っているのも見かけました。運がよければ、100円ジャンクにもあるかも。

工作 - コントローラー側

十字キーの入力を外に引っ張り出すのみです。原理としては一切難しいところはありません。

f:id:moccos_info:20130115063317j:image:w200f:id:moccos_info:20130115063316j:image:w250
スイッチの金属らしき部分はソルダーレジストが乗っているのか、半田付けできませんでした。しかし、どのキーの近辺にも半田付けできるエリアが用意されていたので、ありがたくそれを使わせてもらっています。何のためにこうなっているのかは謎です。


f:id:moccos_info:20130115063318j:image:w250f:id:moccos_info:20130115063314j:image:w250
何かで使った余りの基板の欠片があったので、内部と外部を接続するようにネジ止めして、外側にピンヘッダを付けました。

工作 - テンキー側

分解

以前外付けHDDを分解したときに少し大変だったので身構えていましたが、突くと開くタイプの簡易ツメ+底面のネジで組み立てられていて、綺麗に簡単に分解できる製品でした。底面のゴム足シールの裏にネジが隠されているのに気づかず、筐体の端にでかい穴を開けてしまいましたが、そんなことは不要でした。
f:id:moccos_info:20130115063319j:image:w300

メンブレンスイッチ

メンブレンスイッチの構造については、以下の記事がわかりやすく、参考にしました。

導電インクで回路が印刷されたシートが2枚、絶縁用のシートが間に1枚。
f:id:moccos_info:20130115063330j:image:w300

絶縁用シートにはボタンが押される部分に穴が開いていて、キー入力によりそこが接触し、表・裏間が接続されます。こんな簡単な仕組みだったとは。
流用するためには、端に引き出されている部分のどの線がどう配線されているか、解析する必要があります。このシートに直接半田付けしようとすると溶けてしまうので、無難に元々の配線パターンに従って、端から引き出すことにしました。シートの位置固定の問題もあるので、余計なことはせずに、USBテンキーの元々の形態そのままになりました。使うのはキー4つだけなんですけどね。

f:id:moccos_info:20130115063332j:image:w250f:id:moccos_info:20130115063331j:image:w250

配線パターンは、この製品では以下のようになりました。fが表、rが裏です。製品ごとに違ったり、同じ製品でもロットにより改変がされる可能性もあるので、データそのものが誰かの役に立つものではありませんが。

f:id:moccos_info:20130115084333p:image

当然、それぞれの穴が表裏1つずつと繋がる構造になっています。これなら4/5/6/+を使うのが一番簡単そうですね。ただ今回、間違えて10pinの変換基板を用意してしまい、端を落とす必要があったため、1/2/3/Enterの横並びを使う事になりました。
実は変換基板の値段がテンキーと同じくらいかかっていて微妙感溢れるものの、量産しない場合に390円のコストはまあOKということにしましょう…
f:id:moccos_info:20130115063326j:image:w250

次の写真は、もともと入っていたUSB接続の基板とコネクタ。ここに半田付けして引っ張りだしても良かったかなあ。
f:id:moccos_info:20130115063325j:image:w300

外部への引き出し

コントローラー側と同様に、筐体に穴をあけて基板を通し、内側をネジ止め・外側にピンヘッダという構成です。
f:id:moccos_info:20130115063322j:image:w250f:id:moccos_info:20130115063324j:image:w250
ネジ穴を適当に開けたため、ちょっと曲がっているのがださいですが、誰も見ませんこんなところ。

使用感

viのカーソルキーが全く身についていない状態なので、まだ上下移動がうまくできません。さらに、ナナメ移動のときに1個空きに同時押しするのが直感的に難しいです。
そして、ゲームをする際には当然4本指を固定で置きますが、本来HJKLはJがホームポジションなので、指使いがちょっと違います。JとKの位置関係は覚えられるかもしれないけれど、指使いを脊髄に叩き込むとまでは行かないかもしれません。


また、実は左手で通常コントローラーの右手部を操作していることにも、ちょっと無理があります。こっちも左手用のテンキーを用意するのがベストなのかもしれません。もうやらんけどw

2013年1月4日金曜日

Windows上でVisualStudio 2010と2012用にboostをビルド(x86/x64)

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

意外とめんどくさくて、次忘れそうなのでメモ。

ビルド環境

cl.exeでビルド。環境変数の設定がたくさんあって大変なので、無理せず、スタートメニューから辿れるビルド環境のコマンドプロンプトを使います。

  • Windows SDK 7.1 Command Prompt
    • Microsoft Windows SDK v7.1の下にある

Visual Studio 2010のVisual Studioコマンドプロンプトだと、x86(32bit)しか作れないっぽいので、x64(64bit)のライブラリをビルドするのには必要。そもそも64bitバイナリをビルドする人はインストールしているはずですが。
Visual Studio 2012ではx86用とx64用のプロンプトが選べるので、2012だけならそちらで良いかも。

x86/x64の選択

setenv でさくっと設定できます。

Usage: "Setenv [/Debug | /Release][/x86 | /x64 | /ia64][/vista | /xp | /2003 | /2008 | /win7][-h | /?]"
/Debug - Create a Debug configuration build environment /Release - Create a Release configuration build environment /x86 - Create 32-bit x86 applications /x64 - Create 64-bit x64 applications /ia64 - Create 64-bit ia64 applications /vista - Windows Vista applications /xp - Create Windows XP SP2 applications /2003 - Create Windows Server 2003 applications /2008 - Create Windows Server 2008 or Vista SP1 applications /win7 - Create Windows 7 applications


Debug版とRelease版両方一度にビルドしてくれるので、そこを設定する必要はなさそうです。
> setenv /x64
または
> setenv /x86


ビルド時の引数による設定

まず、ビルド用の準備(共通)。解凍したboostソースコードのトップディレクトリで、バッチファイルを一撃。

h:\>cd boost_1_52_0
h:\boost_1_52_0>bootstrap.bat


あとは必要に応じてbjamへの引数を変えます。
参考:c++ - 64-bit version of Boost for 64-bit windows - Stack Overflow

  • ターゲットバージョン
    • --toolset=msvc-10.0 など
      • 9.0が2008、10.0が2010、11.0が2012
  • 出力先
    • --stagedir=x64 など
    • 指定したディレクトリの下にさらに lib が掘られて、そこに出力される

例:

bjam --toolset=msvc-11.0 --build-type=complete --stagedir=x86 stage
bjam --toolset=msvc-10.0 address-model=64 --build-type=complete --stagedir=x64 stage


64bit環境にしても、address-modelを設定しないと32bitになります。

32bitのビルドが行われる場合の、ビルド開始直後の表示例。
Performing configuration checks
- 32-bit : yes - x86 : yes


64bitのときは、32-bit の項目が表示されませんでした。(noと表示されるわけではない)
Performing configuration checks
- x86 : yes


出力ファイル

出力ファイル名は boost_random-vc100-mt-1_52.lib のように、コンパイラのバージョン入りになるので、バージョンごとに最終的な置き場所を変える必要はなさそうです。ファイル名のboostバージョンの前の部分に、gdと入っているのがデバッグ版です。boost_random-vc100-mt-gd-1_52.lib など。
ただし、どうやらこの長い名前をプログラマが意識する必要はなさそうです。

リンク

ヘッダをincludeしただけでビルドすると動いてしまい、なんだこりゃ?とヘッダを追いかけてみると、どうやら環境を判断して必要なlibファイルの名前を生成し、勝手にスタティックリンクしてくれているようです。あちこちに転がっているconfig.hppや、config/auto_link.hppなどが暗躍しています。
どうやらダイナミックリンクにしたい場合は…

  • boost関係全てDLLにするなら、BOOST_ALL_DYN_LINKを定義
  • 特定のライブラリのみDLLにするなら、BOOST_PROGRAM_OPTIONS_DYN_LINKのような個別定義を使用

2013年1月3日木曜日

2012年に覚えた(はずの)事一覧

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

プログラミング関係で何か覚えるたびにTwitterにポストしていたものの振り返り。
Twitter記法並べたらクソ重いなあ。




言語はJavascript(主にjQuery)、Erlang、Java、Scala、C++、まれにC#やPython。環境はVimとgitが多めでした。
覚えた、って書いてもその後二度と使わないようなものは、見事に忘れてます。tmuxはCygwinで動かなかったから使わなくなったし、Vimで画面分割するのに慣れたからGNU screenでの画面分割も使っていないなあ。


しばらくErlangは使わないだろうから、完全に忘れちゃうかもしれません。一昨年少し触ったHaskellは、もはや入門からやり直さないといけない程度に忘れました。うーん勿体ない。Java・Scala・C++はそれなりに進歩、C#・Python・R言語は実力キープ~やや前進という程度でした。
2012年に新しく覚えた言語が無かった気がするけれども、中途半端に手を出しても使わなければ忘れてしまうので、HaskellかC#あたりを強化する方向にしようかなー…と思います。全く新規の物の方が面白いけれども、これ以上器用貧乏で手を広げるより、何か小さくても自慢できるようなものが出来る程度に、何かに専念した方がいいかなーと。

2013年1月1日火曜日

ソースファイルに書いたメンバ関数ってインライン展開されるんだっけ?

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

ヘッダファイルでクラス宣言中に書けばインライン化されやすいのは知ってるけれども、ソースファイルに書くとどうなるんだっけ?と、突然気になって調べてみました。もちろん、そのクラス内の別のメンバ関数から呼び出す場合の話です。

// Hoge.h
#include <stdio.h>

class Hoge {
 public:
  void moge();
  void pub(int &x);
  void pub_i1(int &x) {
    ++x;
    printf("%d ", x);
  }
  void pub_i2(int &x);

 private:
  void priv(int &x);
  void priv_i1(int &x) {
    ++x;
    printf("%d ", x);
  }
  void priv_i2(int &x);
};

// Hoge.cpp
#include "Hoge.h"

void Hoge::moge() {
  int n = 0;
  pub(n);
  pub_i1(n);
  pub_i2(n);
  priv(n);
  priv_i1(n);
  priv_i2(n);
  printf("%d\n", n);
}
void Hoge::pub(int &x) {
    ++x;
    printf("%d ", x);
}
inline void Hoge::pub_i2(int &x) {
    ++x;
    printf("%d ", x);
}
void Hoge::priv(int &x) {
    ++x;
    printf("%d ", x);
}
inline void Hoge::priv_i2(int &x) {
    ++x;
    printf("%d ", x);
}

Hoge::moge()で何やってるのか確認します。

gcc

  • version 4.5.3 (GCC)

-Sオプションで出力。

デフォルトの最適化オプション
    movl    $0, -12(%ebp)
    leal    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    __ZN4Hoge3pubERi
    leal    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    __ZN4Hoge6pub_i1ERi
    leal    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    __ZN4Hoge6pub_i2ERi
    leal    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    __ZN4Hoge4privERi
    leal    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    __ZN4Hoge7priv_i1ERi
    leal    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    __ZN4Hoge7priv_i2ERi
    movl    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $LC1, (%esp)
    call    _printf
    leave

全部call。

-O2
    movl    8(%ebp), %ebx
    leal    -12(%ebp), %esi
    movl    %esi, 4(%esp)
    movl    $0, -12(%ebp)
    movl    %ebx, (%esp)
    call    __ZN4Hoge3pubERi
    movl    -12(%ebp), %eax
    movl    $LC0, (%esp)
    addl    $1, %eax
    movl    %eax, -12(%ebp)
    movl    %eax, 4(%esp)
    call    _printf
    movl    -12(%ebp), %eax
    movl    $LC0, (%esp)
    addl    $1, %eax
    movl    %eax, -12(%ebp)
    movl    %eax, 4(%esp)
    call    __printf
    movl    %esi, 4(%esp)
    movl    %ebx, (%esp)
    call    __ZN4Hoge4privERi
    movl    -12(%ebp), %eax
    movl    $LC0, (%esp)
    addl    $1, %eax
    movl    %eax, -12(%ebp)
    movl    %eax, 4(%esp)
    call    __printf
    movl    -12(%ebp), %eax
    movl    $LC0, (%esp)
    addl    $1, %eax
    movl    %eax, -12(%ebp)
    movl    %eax, 4(%esp)
    call    __printf
    movl    -12(%ebp), %eax
    movl    $LC1, (%esp)
    movl    %eax, 4(%esp)
    call    _printf
    addl    $32, %esp
    popl    %ebx

ヘッダファイル内のクラス定義で記述された関数と、ソースファイル中でinline指定された関数が展開されました。
publicでinlineな関数も展開されています。これで外部から呼ぶと…たとえばmain関数からpub_i2()を呼ぶと、リンクエラーになります。inlineキーワードはあくまでもヒントです!というスタンスかと思いきや、インライン化を優先するんですね。意外でした。

#include "Hoge.h"

int main(int argc, char **argv) {
  Hoge hoge;
  int n = 0;
  hoge.pub(n);
  hoge.pub_i1(n);
  hoge.pub_i2(n);

  return 0;
}
% gcc Main.cpp Hoge.o
/cygdrive/z/Temp/ccgFs4xP.o:Main.cpp:(.text+0x4e): undefined reference to `Hoge::pub_i2(int&)'
collect2: ld returned 1 exit status

cl.exe (Visual Studio)

  • Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86

/FAオプションで出力。

デフォルトの最適化オプション
    push    ebp
    mov ebp, esp
    sub esp, 8
    mov DWORD PTR _this$[ebp], ecx
; Line 4
    mov DWORD PTR _n$[ebp], 0
; Line 5
    lea eax, DWORD PTR _n$[ebp]
    push    eax
    mov ecx, DWORD PTR _this$[ebp]
    call    ?pub@Hoge@@QAEXAAH@Z            ; Hoge::pub
; Line 6
    lea ecx, DWORD PTR _n$[ebp]
    push    ecx
    mov ecx, DWORD PTR _this$[ebp]
    call    ?pub_i1@Hoge@@QAEXAAH@Z         ; Hoge::pub_i1
; Line 7
    lea edx, DWORD PTR _n$[ebp]
    push    edx
    mov ecx, DWORD PTR _this$[ebp]
    call    ?pub_i2@Hoge@@QAEXAAH@Z         ; Hoge::pub_i2
; Line 8
    lea eax, DWORD PTR _n$[ebp]
    push    eax
    mov ecx, DWORD PTR _this$[ebp]
    call    ?priv@Hoge@@AAEXAAH@Z           ; Hoge::priv
; Line 9
    lea ecx, DWORD PTR _n$[ebp]
    push    ecx
    mov ecx, DWORD PTR _this$[ebp]
    call    ?priv_i1@Hoge@@AAEXAAH@Z        ; Hoge::priv_i1
; Line 10
    lea edx, DWORD PTR _n$[ebp]
    push    edx
    mov ecx, DWORD PTR _this$[ebp]
    call    ?priv_i2@Hoge@@AAEXAAH@Z        ; Hoge::priv_i2
; Line 11
    mov eax, DWORD PTR _n$[ebp]
    push    eax
    push    OFFSET $SG3877
    call    _printf
    add esp, 8
; Line 12
    mov esp, ebp
    pop ebp
    ret 0

全部call。

-O2
; Line 5
    push    1
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 6
    push    2
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 7
    push    3
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 8
    push    4
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 9
    push    5
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 10
    push    6
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 11
    push    6
    push    OFFSET ??_C@_03PMGGPEJJ@?$CFd?6?$AA@
    call    _printf
    add esp, 56                 ; 00000038H
; Line 12
    ret 0

全部展開。
さすがに関数の中身がcallの手続きより語数少ないのは少なすぎたか、と関数内のprintfを増量してみましたが、その程度では結果は変わりませんでした。

-Ox
; Line 5
    push    1
    push    OFFSET $SG3882
    call    _printf
; Line 6
    push    2
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 7
    push    3
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 8
    push    4
    push    OFFSET $SG3891
    call    _printf
; Line 9
    push    5
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 10
    push    6
    push    OFFSET ??_C@_03JDANDILB@?$CFd?5?$AA@
    call    _printf
; Line 11
    push    6
    push    OFFSET $SG3877
    call    _printf
    add esp, 56                 ; 00000038H
; Line 12
    ret 0

全部展開。
なお、-O2も-Oxも、cl Main.cpp Hoge.obj としてみたところ、何事もなくリンク・実行とも成功しました。objdump -rするとゴチャゴチャついてます。インライン化するけど実態も定義しておく、と。速度を最適化してるんだから、サイズでかくなることに文句いっちゃいかんだろ、ということですかね。

総括

gccは想像通りの動作をする。
cl.exeは予想以上にばりばり展開する。

gccはとても小さなプラットフォームまで対象なので、仕様通りに動いてくれないと困る…という感じでしょうか。
とにかく、private関数の定義にinlineと書けばinline化されそうな事はわかりました。

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