2014年 買ってよかった本10選

2014年も残すところあとわずかです。

そんな今年1年のおさらいに、僕が実際に買って読んだ中でも特に琴線に触れた本を、独断と偏見で10冊ほどピックアップしました。すごい今更ですが適当に紹介したいと思います。

あ、アフィリンクは貼っていませんので、そういうのが気になる方も安心してクリックしてください。

レガシーコード改善ガイド

新人1年目の頃に読んでおくべきだった。
GUIビジネスロジックがべったり癒着していたり、ロジックにDBアクセスがべったり癒着していたり、とてもそのままではテストを書けない既存の「レガシーコード」をテスト可能な形にリファクタリングするための珠玉のノウハウ集。
訳も熟れていて非常に読みやすい。今年一番わくわくした技術書。もし僕が王国を作ったら、国民必読の書にする法律を制定したいくらい素晴らしかった。

レガシーコード改善ガイド(マイケル・C・フェザーズ 平澤章 越智典子 稲葉信之 田村友彦 小堀真義 ウルシステムズ株式会社) | 翔泳社の本

弱虫ペダル

冴えないオタク少年の小野田坂道が、自転車に魅せられて成長していく物語。
絶望的な局面から何度も復活劇を遂げる様子が非常に熱く描かれている。
モチベーションが下がったときに読むとなぜか小野田くんに感情移入してしまい、燃え上がる闘志を抑えきれなくなる。それくらいメンタリティに作用する楽しいマンガ。

弱虫ペダル 第1巻 | 秋田書店

C#ショートコードプログラミング 第2版

Windows開発の中核言語として不動の地位を築いたC#
LINQラムダ式を駆使すればキミのコードはもっと短くなる!
一応逆引きリファレンス形式だが、あまり厚い本でもないので通読してコード短縮テクの引き出しを増やしておきたい。

日経BP書店|商品詳細 - C#ショートコードプログラミング 第2版

ザ・ゴール コミック版

プロジェクト管理に「クリティカルチェーンマネジメント」の思想を取り入れた故エリヤフ・ゴールドラット氏。
彼の名著「ザ・ゴール」が待望のコミック化を遂げた。わー(歓声) ぱちぱち(拍手)
「なぜみんな忙しいのに結果が出ないのだろう」という素朴な問いに対して、叡智に満ちたカイゼンのプロセスが非常に分かりやすく例示されており、中身もかなり濃い。活字嫌いの僕でも楽しく読めた。そんじょそこらのビジネスハウツー本とは格が違う
余談ではあるが、岸良氏の本では「職場の理不尽―めげないヒント45 (新潮新書)」も心底お勧め。

ザ・ゴール コミック版 | エリヤフ・ゴールドラット、ジェフ・コックス・原作/岸良裕司・監修/青木健生・脚色/蒼田 山・漫画 | 書籍 | ダイヤモンド社

15時間でわかるGit集中講座

僕はSubversionで満足な人だったのだけど、なんか周りの人がGitばっかりになってしまい危機感を覚えてやむなく買った本。
GitのGUIフロントエンドであるSourceTreeを使って基本的な操作を体得する楽しい本。
実際に手を動かしてみるうちに、分散型バージョン管理の利点が少しずつ見えてきた。Gitべんり。
Git本は巷に多く溢れているが、当時はこれが一番わかりやすそうだったのでこれにした。

15時間でわかるGit集中講座:書籍案内|技術評論社

北斗の拳 イチゴ味

北斗の拳の外伝的作品。主人公はまさかの南斗の将星・聖帝サウザー
友達がいない・極端にウブ・精神年齢が異常に幼い。完全に問題児と化したサウザーが愉快な仲間達を巻き込んで暴れ回る。
ただし画風(だけ)は完全に元祖「北斗の拳」の世界観を踏襲していたり、原作ファンにしか楽しめない高度なギャグやネタバレも大量に盛り込まれているため、北斗の拳を知らない人が読んでも内容を理解しづらいと思う。

北斗の拳 イチゴ味 第1話 聖帝様、再誕の巻 - 行徒妹 - Yahoo!ブックストア

新装版 リファクタリング

これも有名な本。
プログラムの振る舞いを変えずにコードを変更するための様々なノウハウをカタログ化した名著。今年は装いも新たに再刊行された。訳書にもかかわらず非常に読みやすい。
付録では最新(ではなくJava 7)の開発環境に準拠した「再リファクタリング」が示されている。
本編の内容は前出のレガシーコード改善ガイドと重複する箇所もあるが、2冊あって困ることはない。

新装版 リファクタリング 既存のコードを安全に改善する | オーム社eStore

Nature of Code

一部(Processing界隈)で話題になった。巷に溢れる薄い入門書とは一線を画した本格的な内容を誇る良書。
物理シミュレーション、遺伝的アルゴリズムニューラルネットワークなど、アカデミックなトピックを扱っていながら、それでいて平易な説明とシンプルなコードで楽しく「シミュレーション」の作り方を学べる。
より本格的な実装を知りたくなったら「ゲーム開発者のためのAI入門」を読もう。

Nature of Code|書籍|株式会社ボーンデジタル

裏口からのC#実践入門

ふたたび川俣氏のC#本。Win32APIやMFC時代、あるいは.NET黎明期のまま時が止まってしまっているWindowsプログラマが読めば目から鱗間違いなし。
入門とあるが、プログラミングスタイルが確立したベテランこそ読むべき一冊。あるいは、ベテランの偏向した思想に毒されたくない新人も読んでおこう。
ただし、テスタビリティに関する指針がまったく無い点は気になった。川俣氏は過去にNUnitとモックを使ったテストダブルの技法に関するコラムを書いていたこともあり、その辺りの造詣も深いはずなのだが…。

裏口からのC# 実践入門 ―バッドノウハウを踏み越えて本物へ!!:書籍案内|技術評論社

開発のプロが教える Swift標準ガイドブック

つい先日買った。「詳解 Swift」もよい本だったが、僅差で「開発のプロが教える Swift標準ガイドブック」の方をプッシュ。「詳解 Swift」では解説を避けていたKVCやKVOが扱われており、また日本語の解説がまだ少ないCore DataやテスティングフレームワークQuickに関する言及もある(本当にさらっと触れているだけだが)。

開発のプロが教える Swift標準ガイドブック | マイナビブックス

以上です。

なんかマンガの比率が多いですね。

関数型プログラミングで行こう 〜 みんな大好きJavaから入る関数型の入り口の入り口

関数型言語ってなに?

ほんとこれ。

僕も、ピュアな関数型言語は7年前にCommon Lispに触ったきりご無沙汰です。

というわけで、IT用語辞典で調べてみました。

すべての計算や処理などを関数の定義の組み合わせとして記述していくタイプのプログラミング言語

「同じ入力には必ず同じ出力を返す」「関数の評価が他の関数に影響を及ぼさない」など数学における関数と似た性質を持った関数の定義としてプログラミングを行い、プログラムの実行は記述された関数群の評価として行われる。

なるほど、わからん

ちょっと説明が抽象的すぎるので、具体例を以下に示します。

「同じ入力には必ず同じ出力を返す」

たとえば  { f(x) = 2x} のように、 {x} が決まれば  {f(x)} も一意になる関数などはこの性質に当てはまります。

一方、ランダムな値を返すrand()や、現在の日時を返すnow()などは、実行のたびに結果が異なるので失格です。

あと、処理が成功したか失敗したかによって結果が変わる関数*1も、この性質には当てはまらないと言えます。

「関数の評価が他の関数に影響を及ぼさない」

これはちょっとイメージしづらいかも知れません。

プログラムの中にhoge()fuga()という関数があるとしましょう。

で、hoge()の後にfuga()を実行したときと、fuga()だけを単体で実行したときで処理結果や内部状態が変わらない性質のことを指しています。

こういう性質を、一般的に「副作用がない」と表現したりします。

参考までに副作用のあるJavaプログラムの例を以下に示します。

public class Sample {
    private static int x;
    
    public static int hoge(int param) {
        return x *= param;     // ←ここ
    }
    
    public static int fuga(int param) {
        x += param;     // ←ここ
        return param;
    }
}

上の例では、関数(まぁメソッドですが)を呼ぶたびにフィールドが書き換わってしまうので、そのフィールドを参照するすべての関数にも影響が及びます。

ここまで理解したところで、そもそもなぜ今さら関数型言語が持て囃されているのかを考えてみます。

なぜいま関数型言語なの?

おそらく関数型の思想で書かれたコードはテストがしやすいからだと思うのです。

最近の開発では、xUnit というテスティングフレームワークを用いたユニットテストが一般的になってきました。

これらのテスティングフレームワークは、一般に以下の性質を持っているコードと特に相性がよいとされています。

  • 同じ入力パラメータに対して、必ず決まった出力が得られる*2
  • 副作用を持たない(クラスの内部状態を変化させない)

なんと、先ほどの「IT用語辞典」の説明と一致するではありませんか。関数型言語の性質がそのままテスタビリティに直結するというかけがえのない利点があるというわけです*3

そう。人々は、テスタビリティの高いコードを追い求めるうちに、あることに気づいてしまったのです。

関数型プログラミングこそ至高」

という、恐るべき事実に。

まぁこれは大げさですが、テストを自動で行うにせよそうでないにせよ、こういう関数型プログラミングのエッセンスを取り入れた設計はシンプルで美しいという思想がやんわりと支配的になりつつあります。

そして、その流れはついに.NetやJavaなどが採用していたパラダイムまで変えてしまいました。

そう、ぼくらが大好きなJavaにまで関数型プログラミングの思想が忍び寄っていたのです!!

Java、彼の場合(ラムダ式編)

関数型言語の主役は、ずばり「関数」です。そのままですね。

小さな関数をうまく組み合わせて、大きな処理を実現しようという考え方です。ちがったらごめん。

一方、オブジェクト指向言語Javaの主役は、ずばり「クラス」でした。オブジェクト指向という思想が現れた20余年前は、システムの仕組みを「オブジェクト同士のメッセージのやり取り」になぞらえて設計していたそうです。

Javaは「一連のアルゴリズムの中で、処理の一部をほんのちょっとだけ変えたい」という柔軟さに弱く、小回りの利きづらい言語でした。

たとえば、Javaのソート処理を考えてみましょう。ソート順は「文字数の多い順」であるとします。

        // 文字数の多い順に並べ替えたい
        List<String> strList = new ArrayList<String>
            (Arrays.asList("****", "**", "***", "*"));

これを今までのやり方で実現しようとすると、ひとまずソートに必要な比較関数 compare()をクラスで一枚包んで、List#sort()クラスごと引き渡す必要がありました。

    class StringCompalatorDesc implements Comparator<String> {
        public int compare(String s1, String s2) {
            return Integer.compare(s2.length(), s1.length());
        }
    }
        strList.sort(new StringCompalatorDesc());
        // 匿名クラスを用いた別解
        strList.sort(new Comparator<String>() {
            public int compare(String s1, String s2) {
                return Integer.compare(s2.length(), s1.length());
            }
        });

処理の制御のために関数ひとつを指定するだけのために、なにがなんでもクラスの形にしてやらねばなりませんでした。たったひとつの、その場かぎりの関数を指定するためだけに。わざわざ。クラスを。

もしも関数をクラスで包まずに、そのまま引数に渡すことができたら、きっと無駄なクラスが減ってプログラムはすっきりしそうですね。

かくして、Java 8からは以下のように引数に関数(っぽいもの)を指定できるようになりました。マンモスうれぴー。

strList.sort((String s1, String s2) -> Integer.compare(s2.length(), s1.length()));

ちなみに、この引数部分の

        (String s1, String s2) -> Integer.compare(s2.length(), s1.length())

みたいな式は、「ラムダ式」と呼ばれています。

実はこれも元々は関数型言語特有の用語でしたが、今ではC#VBC++などのメジャーな言語にもレギュラー入りしたキモい構文です。

なにゆえJavaはこんなキモい構文を導入してまでラムダ式を採用したかったのでしょうか。

ラムダ式は遅延評価

ラムダ式には遅延評価されるという大きな特徴があります。

不正確を承知でざっくり言うと、遅延評価とは「処理内容を前もって予約しておき、時がきたら実行する(その時がくるまで実行しない)」という性質のことです。

「ボタンが押されたら◯◯の処理をしたい」「アルゴリズムの中で××の箇所に差し掛かったら処理◯◯を実行したい」「ダウンロードが完了したタイミングでユーザに通知するため◯◯の処理がしたい」というとき、前もって◯◯の処理を予約しておくのです。

先ほどのソートの例でも、sort()呼び出しのタイミングで比較関数をラムダ式の形で引数に記述することで、しかるべきときに実行するよう予約しています。

今までのJavaでも一応こういうことはできました。単にクラスが主役か関数(メソッド)が主役か、どっちがコードをすっきり書けるかというだけの話だと思っていて、オブジェクト指向も関数型もそこまで相反する概念ではないというのが僕の思うところです。

ですが、関数が主役になると色々便利なんですよ。

ちょっと次の例をご紹介しますね。

Java、彼の場合(Stream編)

いきなりですがナイスな例題。

以下のリストの全要素を標準出力にプリントしたいとしましょう。

        // リストの全要素を出力したい
        List<String> strList = new ArrayList<String>
            (Arrays.asList("こぶた", "たぬき", "きつね", "ねこ"));

はい。

Javaを習いたての人は、たぶんfor文と変数iを使って以下のように書くでしょう。

        for(int i = 0; i < strList.size(); ++i) {
            System.out.println(strList.get(i));
        }
        // 別解
        for(Iterator<String> i = strList.iterator(); i.hasNext(); ) {
            System.out.println(i.next());
        }

最も一般的な書き方はこうでしょうか。

        // 別解②
        for(String elements : strList) {
            System.out.println(elements);
        }

このfor文による配列の走査は、初級プログラミングの超頻出テクニックです。

ですが、本当にやりたいことは配列の走査そのものではなく、走査によって得られた要素の出力のはずです。

「なにを言ってるんだコイツは」と思われるかもしれませんが、上記のコードには

  • 配列を走査して要素をひとつずつ取り出すこと
  • 取り出された要素に対して加工(出力)すること

という、本来ならば目的の異なる2つの処理が混在してしまっているのです。

本来、この2つは分離しなければならないのですが、今までのJavaの言語仕様による制約のせいでやむを得ず上記のようなコードになってしまっていたのです((余談ですが、Common Lispでもよほどの事情がないかぎりforは使わず、再帰呼び出しによって反復処理を実現し、関数の中で要素に対する処理を書きます。))。

しかし、Java 8では満を持して「リストの走査」と「個々の要素に対する処理」を分離することができるようになりました。

それがこちら。

        strList.forEach((String str) -> System.out.println(str));
        // 別解
        strList.forEach(System.out::println);

なんということでしょう

たった1行です。

Javaは関数型に毒されたおかげで、forEach()に渡すラムダ式(または参照メソッド)によって走査中に得られた要素に対する処理が可換になり、関心の分離をより柔軟に行えるようになりました。

あれ?

これはオブジェクト指向プログラミングにおける「変わらない部分から変わる部分を切り出す」操作そのものです。

そう、関数型はオブジェクト指向と対をなすパラダイムではなく、融合とシナジーにより愛と勇気をもたらす夢の思想だったのです。これらはいつでも2人で1つだった。地元じゃ負け知らず

そんなこんなで強引なまとめ

だいぶ脱線した気もしますが、まとめ。

  • 関数型言語の方針で設計するとなんか美しい
    • 方針1. 同じ入力には必ず同じ出力を返す
    • 方針2. 関数の評価が他の関数に影響を及ぼさない
  • メソッドが主役になるとコードがシンプルになる
    • 関数オブジェクトではなくラムダ式で済む
    • 本来の関心事のみに対して集中できる

だから恐れないでみんなのために。たとえ胸の傷が痛んでも。

おまけ

ちなみに、からあげさんのHello Worldmain()関数の型やreturn文を省略するなどかなりの高等テクが駆使されています。

*1:たとえば、DBのコネクションに成功したらtrueが返り、失敗したらfalseが返る関数など。

*2:戻り値がない関数も基本的に xUnit との相性は悪いです。

*3:余談ですが、日時に依存する処理やランダム性のある処理は、極端に xUnit との相性が悪いので、あの手この手でリファクタリングを行い、なんとかテストできる形に持っていきます。

Quickの手習い

前回までのあらすじ

今日やったこと

せっかくなので、Quickで使えるmatcherについて、いろいろ試してみました。

xUnitと違って、基本的にmatcherには動詞の原形が使われており、慣れるまではタイポしそうです。

等値判定 equal

    override func spec() {
        describe("等値のテスト") {
            it("2と3の和は5と等しい") {
                let value = 2 + 3
                expect(value).to(equal(5))
            }
            
            it("400と600の和は100とは異なる") {
                let value = 400 + 600
                expect(value).toNot(equal(100))
            }
        }
    }

ポインタ比較 beIdenticalTo

    override func spec() {
        describe("ポインタ比較のテスト") {
            class Hoge : NSObject { }

            it("二つのHogeは同じポインタ") {
                let obj: Hoge = Hoge()
                let obj2 = obj
                
                expect(obj).to(beIdenticalTo(obj2))
            }
            
            it("二つのHogeは異なるポインタ") {
                let obj: Hoge = Hoge()
                let obj2: Hoge = Hoge()
                
                expect(obj).toNot(beIdenticalTo(obj2))
            }
        }
    }

大小比較 beLessThan / beGreaterThan

    override func spec() {
        describe("大小比較のテスト") {
            
            describe("不等号で比較する場合"){
                it("99は100より小さい") {
                    expect(99).to(beLessThan(100))
                }
            
                it("100は100より小さくない") {
                    expect(100).toNot(beLessThan(100))
                }
            
                it("101は100より大きい") {
                    expect(101).to(beGreaterThan(100))
                }
            
                it("100は100より大きいくない") {
                    expect(100).toNot(beGreaterThan(100))
                }
            }
            
            describe("等号つき不等号で比較する場合") {
                it("100は100以下") {
                    expect(100).to(beLessThanOrEqualTo(100))
                }
                
                it("101は100以下ではない") {
                    expect(101).toNot(beLessThanOrEqualTo(100))
                }
                
                it("100は100以上") {
                    expect(100).to(beGreaterThanOrEqualTo(100))
                }
                
                it("99は100以上ではない") {
                    expect(99).toNot(beGreaterThanOrEqualTo(100))
                }
            }
        }
    }

誤差判定 beCloseTo

    override func spec() {
        describe("浮動小数点誤差のテスト") {
            it("1.0と0.99999の誤差が0.00001以内である") {
                expect(1.0).to(beCloseTo(0.99999, within: 0.00001))
            }
            it("1.0と1.000011の誤差が0.00001以内ではない") {
                expect(1.0).toNot(beCloseTo(1.000011, within: 0.00001))
            }
        }
    }

Is-aのテスト beAnInstanceOf / beAKindOf

    override func spec() {
        describe("Is-aのテスト") {
            it("文字列「HelloWorld」はNSStringのサブクラスである") {
                expect("HelloWorld").to(beAKindOf(NSString))
            }
            
            it("文字列「HelloWorld」はNSStringのインスタンスではない") {
                expect("HelloWorld").toNot(beAnInstanceOf(NSString))
            }
            
            it("文字列「HelloWorld」は_NSContiguousStringのインスタンスである") {
                expect("HelloWorld").to(beAnInstanceOf(_NSContiguousString))
            }
        }
    }

真理値 beTrue / beFalse

    override func spec() {
        describe("真理値のテスト") {
            it("TrueはTrueである") {
                expect(true).to(beTrue())
            }
            
            it("TrueはFalseではない") {
                expect(true).toNot(beFalse())
            }

            it("FalseはFalseである") {
                expect(false).to(beFalse())
            }
            
            it("FalseはTrueではない") {
                expect(false).toNot(beTrue())
            }
        }
    }

Nil判定 beNil

    override func spec() {
        describe("Nil判定") {
            it("NilはNilである") {
                let nilObj:NSObject? = nil
                expect(nilObj).to(beNil())
            }
        }
    }

空値判定 beEmpty

    override func spec() {
        describe("空値判定") {
            it("要素が1つも存在しない配列は空オブジェクトである") {
                let emptyObj:[String] = []
                
                expect(emptyObj).to(beEmpty())
            }
            
            it("要素が1つも存在しない辞書は空オブジェクトである") {
                let emptyObj: [String:Int] = [:]
                
                expect(emptyObj).to(beEmpty())
            }
            
            it("文字数0の文字列は空オブジェクトである") {
                let emptyString = ""
                
                expect(emptyString).to(beEmpty())
            }
        }
    }

前方一致 beginWith

    override func spec() {
        describe("前方一致判定") {
            it("配列「AAA, BBB, CCC」の最初の要素は「AAA」である") {
                let array:[String] = ["AAA", "BBB", "CCC"]
                
                expect(array).to(beginWith("AAA"))
            }
            
            it("文字列「HelloWorld」は「Hell」で始まる") {
                let helloString = "HelloWorld"
                
                expect(helloString).to(beginWith("Hell"))
            }
        }
    }

後方一致 endWith

    override func spec() {
        describe("後方一致判定") {
            it("配列は「AAA, BBB, CCC」の最後の要素は「CCC」である") {
                let array:[String] = ["AAA", "BBB", "CCC"]
                
                expect(array).to(endWith("CCC"))
            }
            
            it("文字列「HelloWorld」は「ld」で終わる") {
                let helloString = "HelloWorld"
                
                expect(helloString).to(endWith("ld"))
            }
        }
    }

含有判定 contain

    override func spec() {
        describe("含有判定") {
            it("文字列「イチゴミルク」は「ゴミ」を含む") {
                let ichigoMilk = "イチゴミルク"
                
                expect(ichigoMilk).to(contain("ゴミ"))
            }
            
            it("文字列「ポークソテー」は「クソ」を含む") {
                let porkSauté = "ポークソテー"
                
                expect(porkSauté).to(contain("クソ"))
            }
        }
    }

iOS界隈の新手のテスティングフレームワークQuickをCocoaPods的なもので導入して満足して死ぬまでの記録

これまでのあらすじ

ひょんなことがきっかけで『iOSアプリ テスト自動化入門』を読みました。今年の3月20日くらいに出版された比較的新しい本なのですが、その直後にSwiftの出現とか冷戦の影響とかのせいでユニットテストを取り巻く状況は急速に変化してしまったようです。

同書の中で、ユニットテストにおけるフレームワークとしてXCTestGHUnitKiwiの3つが紹介されていましたが、そんな最中(さなか)に新手のQuickが颯爽と登場まーた新しいものつくって…


Swift界隈で話題沸騰中のテストフレームワーク Quick とは? - Qiita


[Swift] Quick で始める振る舞いテスト入門 #1 インストール | Developers.IO

序文

ところで話は変わりますが、昨日は朔旦冬至(さくたんとうじ)といって新しいことを始めるのにぴったりの日だったそうです。

なんか1日遅れているような気がしますが、ときには新しいこともしたいですしQuickにちょっと触ってみます。ちょっと触るだけだから

ちなみに上記記事では「QuickはCocoaPodsからインストールできない」と書いてありますが、一応公式に「How to install Quick using beta CocoaPods」という情報がある通り、やってやれないことはないようです。

そんなわけで、今回はQuickをCocoaPods経由でプロジェクトに導入するまでの話を備忘録的に書いていきます。ところどころ躓きました。後半適当になります。

なお使用したXcodeのバージョンは6.1.1です。CocoaPodsのバージョンは0.35.0です。

プロジェクトの作成

まずXcodeを起動して、適当にプロジェクトを作ります。

今回はテスティングフレームワークを動かすことだけが目的で、べつにアプリを作りたいわけではないので、プロジェクトの種類はなんでもよいと思います。ちなみに僕はiOSのSingle View Applicationを選択し、プロジェクトを「Sample1223」と名付けました。

Bundlerの導入

せっかくですが、ここで一旦 Xcodeを終了させます

How to install Quick using beta CocoaPods」にとりあえずBundlerを入れろって書いてあったのでおとなしく入れます。

Bundlerってなんだろう。Rubyerの言うことは分からん


Ruby - ツールを使いたいだけの人のための bundler 入門 (例: vagrant + veewee) - Qiita

ターミナルを起動して、gem installコマンドを打ちます。

bash
$ sudo gem install bundler

あ、sudo*1で実行しないと下記のようなエラーが出てしまうことがあります。

ERROR:  While executing gem ... (Gem::FilePermissionError)
    You don't have write permissions for the /Library/Ruby/Gems/2.0.0 directory.

Gemfileの作成

続いて、.xcodeprojのあるディレクトリまでcdで移動し、Gemfileを作ります*2。以後、特に断りがない限りはこのディレクトリで作業を行うものとします。

bash
$ vi Gemfile

Gemfileの中身は、「How to install Quick using beta CocoaPods」で書けと言われた通りに書いています。

Gemfile
source 'https://rubygems.org'

gem 'cocoapods', :git => 'https://github.com/CocoaPods/CocoaPods.git', :branch => 'swift'
gem 'cocoapods-core', :git => 'https://github.com/CocoaPods/Core.git'
gem 'xcodeproj',  :git => 'https://github.com/CocoaPods/Xcodeproj.git'
gem 'claide', :git => 'https://github.com/CocoaPods/CLAide.git'

gemインストール

bash
$ sudo bundle install

下記のメッセージが表示されたら成功。

Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

Podfileの作成

続いて、NimbleとQuickを取得するようPodfileに書いてやります。このPodfileも、.xcodeprojのあるディレクトリに作ります。

bash
$ vi Podfile
Podfile
platform :ios, "8.0"

source 'https://github.com/CocoaPods/Specs.git'

pod 'Nimble', :git => "https://github.com/Quick/Nimble"
pod 'Quick', :git => 'https://github.com/Quick/Quick', :tag => 'v0.2.1'

あとはEscからの:wq!で上書き終了して、以下のコマンドを打つだけでOK。

bash
$ bundle exec pod install

実験、そしてまさかのエラー

Finderで.xcodeprojのあるディレクトリに行き、Xcode拡張子が.xcworkspaceの方を開きます。

Xcodeが勝手に作ったテスト用のソースSample1223Tests.swiftを以下のように書き換えてみました。

import Quick

class Sample1223Tests: QuickSpec {
}

すると、1行目のimportでまさかのエラー。「No such module 'Quick'」と怒られてしまいました。

f:id:tercel_s:20141223115025p:plain

どうやら、テストコードからCocoaPodsで追加したライブラリをうまく参照できていないようです。

参照の設定

設定手順は以下の画像を参照してください(手抜き)。

f:id:tercel_s:20141223115224p:plain

これで、「No such module 'Quick'」エラーが消えました! わはー

テストコードを書いてみる

というわけで、Developers.ioのこの記事から丸ごとパクった以下のコードで実験してみます。

import Quick
import Nimble

class Sample1223Tests : QuickSpec {
    override func spec() {
        it("is equal") {
            let value = 2 + 3
            expect(value).to(equal(5))
        }
    }
}

Command+uで無事にテストができました。
f:id:tercel_s:20141223193041p:plain
めでたし。

と見せかけて、まだまだ続くよ

今回導入するQuickとNimbleはテストでしか使わないライブラリなので、Podfileを書き換えてSample1223Tests以外のビルドターゲットからはQuickやNimbleを呼べなくしてやります。targetに「Sample1223Tests」を指定するだけです。

Podfile
platform :ios, "8.0"

target :Sample1223Tests, :exclusive => true do
        source 'https://github.com/CocoaPods/Specs.git'

        pod 'Nimble', :git => "https://github.com/Quick/Nimble"
        pod 'Quick', :git => 'https://github.com/Quick/Quick', :tag => 'v0.2.1'
end
bash
$ bundle exec pod update

ところが、Podfileを修正してCommand+uしたらまたしても謎のエラーが発生しました。

f:id:tercel_s:20141223182220p:plain

なんだこれは。

リンカエラーはさておき、まずはシェルスクリプト起動エラーからなんとかせねば。と思っていたら、GitHubで「diff: /../Podfile.lock: No such file or directory · Issue #2303 · CocoaPods/CocoaPods · GitHub」といういかにも関係ありそうな議論を見つけました。


diff: /../Podfile.lock: No such file or directory · Issue #2303 · CocoaPods/CocoaPods · GitHub

結局これを読んだところでなにひとつ解決しなかったわけですが。

試行錯誤

おそらく、target指定を行わないライブラリが1つ以上ないと、プロジェクトがConfiguration Fileを参照できなくてビルド時にコケるんじゃないか……とあたりをつけました。真偽のほどは分かりません。

f:id:tercel_s:20141223191738p:plain

そこで試しに、Podfileにtarget指定なしでAFNetworking を追加してみます(べつにAFNetworkingでなくてもよいのですが、定番中の定番なのでとりあえず)。

Podfile
platform :ios, "8.0"

pod 'AFNetworking'

target :Sample1223Tests, :exclusive => true do
        source 'https://github.com/CocoaPods/Specs.git'
        pod 'Nimble', :git => "https://github.com/Quick/Nimble"
        pod 'Quick', :git => 'https://github.com/Quick/Quick', :tag => 'v0.2.1'
end

pod updateを実行。

bash
$ bundle exec pod update

そして再度プロジェクトを開き、Configurationの設定を確認。プロジェクトと各ビルドターゲットに対して、Based on Configuration Fileを以下のように設定してCommand+u

f:id:tercel_s:20141223192245p:plain

シェル起動エラーがリンカエラーを道連れにして消えてくれました。
f:id:tercel_s:20141223193440p:plain
今度こそ本当にめでたし。

とはいえば、要りもしないライブラリ(今回はAFNetworking)が導入されてしまうのはあまり気持ちのよいものではありませんな。

実際に何か作るときには、何かしら一つはライブラリを入れるだろうから特に気にすることはないのかもしれませんが…。

まとめ

  • Quickは一応CocoaPodsでインストールできる
  • xUnit ぽくないから慣れるの大変そう
  • CocoaPodsの挙動がいまいちわからない
  • クリスマスが今年もやってくる*3
  • ヒメはヒメなのヒメなのだ*4

*1:root権限。

*2:Gemfileをどこに作ってよいのかわからなかったのでとりあえずプロジェクトのルートっぽいところに作りました。流儀として間違っていたらごめんなさい。

*3:ケンタッキーフライドチキンのCMソング。聞くと鬱になる。

*4:弱虫ペダルの主人公・小野田坂道が好きなアニメの主題歌。劇中では小野田が入学した時点で既に彼のiPodに収録されていたが、季節が変わってインターハイ終了後の秋葉原でもまだ店頭でCDが平積みされているなど異様なまでのロングセラーを誇っている。

僕とリファクタリングとNUnitとNSubstitute

はぅ君さんが、NUnit(と、それを動かすためのJenkins)に手を出されたらしい。

NUnitでテストを書く際、セットで使うとちょっと幸せになれる(かもしれない)NSubstituteというライブラリがあります。

というわけで本日は、はぅ君さんがご存知なさそうだったNSubstituteの便利さについてご紹介します。

レガシーコード改善ガイド (Object Oriented SELECTION)』を題材に、リファクタリングNUnitを使用したテスト → NUnit + NSubstituteを使用したテストをステップバイステップで書いていきます。

なお、本記事の執筆にあたり使用した環境は、Visual C# 2013 + NUnit 2.6.4 + NSubstitute 1.8.0 です。

ナイスな例題

ある販売管理システムにSaleというクラスがあるとします。このクラスのScanというメソッドは、顧客が購入しようとしている品物のバーコードを受け取ります。Scanメソッドが呼び出されると、Scanオブジェクトは読み取った品物の名前と価格とをレジの画面に表示します。

f:id:tercel_s:20141220220320p:plain

このクラスをテストして、正しい文字列が表示されるかどうかを確認するにはどうすればよいでしょうか。

ちなみにこのScan()メソッド、データベースへのアクセスやレジ画面のAPI呼び出しが深く埋め込まれているモンスターメソッドであると仮定しましょう。

    public class Sale
    {
        /// <summary>
        /// バーコードを受け取り、
        /// 品物の名前と価格をレジの画面に表示します。
        /// </summary>
        /// <param name="barcode">バーコード</param>
        public void Scan(string barcode)
        {

            //   :
            //   :
            // とてつもなく長いメソッドにつき省略
            //   :
            //   :

        }
    }

このままではNUnitアサーションが使えません。

なにはともあれリファクタリング

もし、画面の更新が行われている箇所を特定して別クラス(クラス名は引用元に従いArtR56Display)に切り出すことができたら(下図参照)、テストしやすくなるかもしれません。

f:id:tercel_s:20141220220517p:plain

このときArtR56Displayクラスの役割は、渡された引数lineをそのままレジの画面に表示するだけです。外から見たシステム全体の動きは全く変わっていません。

さらに、このArtR56Displayクラスを、テストのときだけ擬装オブジェクトにすり替えできるよう、Interfaceを一皮かぶせてあげます。

f:id:tercel_s:20141220220710p:plain

    public interface IDisplay
    {
        void ShowLine(string line);
    }

Saleの中で、IDisplayは以下のように使われるでしょう。

    public class Sale
    {
        private IDisplay display;

        public Sale(IDisplay display)
        {
            this.display = display;
        }

        public void Scan(string barcode)
        {
            //   :
            // 前略
            //   :

            // 画面表示部
            string itemLine = item.Name + " " + item.Price;
            display.ShowLine(itemLine);

            //   :
            // 後略
            //   :
        }
    }

画面表示部を丸ごとIDisplayに委譲してあげるイメージですね。

NUnitでテストを書こう!

ここからテスト用のプロジェクトに書くコードです。

まず、ArtR56Displayの擬装クラスFakeDisplayの実装です。

    public class FakeDisplay : IDisplay
    {
        private string _lastLine = string.Empty;
        public string LastLine
        {
            get{ return _lastLine; }
            private set{ _lastLine = value; }
        }

        public void ShowLine(string line)
        {
            _lastLine = line;
        }
    }

こいつには、直前にShowLine()で出力した文字列を、LastLineプロパティに保持する隠し機能を持たせます。これで、LastLineにアクセスすると、出力文字列を取得することができ、期待値との突合ができるようになる寸法です。

NUnitを用いたテストコードを以下に示します。

    [TestFixture]
    public class SaleTest
    {
        [Test]
        public void TestDisplayAnItem()
        {
            FakeDisplay display = new FakeDisplay();
            Sale sale = new Sale(display);
            sale.Scan("1");

            Assert.AreEqual(@"ブルーレット置くだけ ¥1,000-", display.LastLine);
        }
    }

……ちなみにMicrosoftが密かに推奨しているMSTestを利用した場合のテストコードは以下のようになります。ほとんど一緒ですな。

    [TestClass]
    public class SaleTest
    {
        [TestMethod]
        public void TestDisplayAnItem()
        {
            FakeDisplay display = new FakeDisplay();
            Sale sale = new Sale(display);
            sale.Scan("1");

            Assert.AreEqual(@"ブルーレット置くだけ ¥1,000-", display.LastLine);
        }
    }

テスト自体は一応これでもよいのですが、擬装クラスを書いたせいでテストコードが増えてしまってたいへんです。

もう少しスマートにならないものでしょうか。

NSubstituteを使ってテストコードを減らす

ここからちょっとした小技で、NSubstituteというライブラリを使います。NuGetからインストール可能なので導入もベリーイージーです。

FakeDisplayを作らなくてもよくなるのでテストコードがすっきりします。

以下に、NSubstituteの使用例を示します。

Sale.Scan()経由でIDisplay#ShowLine()が呼び出されたときに、IDisplay#ShowLine()の受け取ったパラメータが期待値と一致するかどうかを確認するテストコードです。

    [TestFixture]
    public class SaleTest
    {
        [Test]
        public void TestDisplayAnItem()
        {
            var substitute = Substitute.For<IDisplay>();
            Sale sale = new Sale(substitute);
            sale.Scan("1");

            // IDisplay#ShowLineに渡された引数が期待値と一致するかを確認
            substitute.Received().ShowLine(@"ブルーレット置くだけ ¥1,000-");
        }
    }

これで、わざわざ《テストのためだけの余分なクラス》を作る手間が一つ減るので、ちょっと幸せになる気がします。

なお、期待値を「ルーレット置くだけ ¥1,000-」に変更してわざとテストを失敗させた結果がこちら。

f:id:tercel_s:20141220214811p:plain

めでたし。

いやめでたくない。

NSubstituteのそのほかの使い方は公式のドキュメントに書いてあるんじゃないでしょうかね(投げやり

たーせる戦記(於Processing) #p5advent

Processing Advent Calendar 2014 第15日目の記事です。

月曜日が始まり、日本列島に絶望感が満ち溢れる中、満を持して僕の番がやってきました!

ごあいさつ

はじめましての人ははじめまして。たーせるです。

都内某所のIT企業に勤めております。誕生日は8月12日。血液型はO型。火属性*1と水属性*2のひとです。

好きなたべものは新潟のラーメン。好きな飲み物はコーラです。

本日のコンテンツ

本日のメニューはこちら。まぁ新作の出来があまりにもヘボかったので過去のいろいろでごまかs(ry

  1. 今までに作ったもの紹介
    1. OpenProcessingダイジェスト
    2. 立体スケッチ
  2. 新作40秒スケッチ
    1. おまけスケッチ
  3. スペシャルサンクス

旧作なんてどうでもいい!もういきなり新作が見たい!という人はこちらへどうぞ。その代わり何があっても知りません。WebGLが動作するブラウザでご覧ください

今までに作ったもの紹介

OpenProcessingダイジェスト

実は、僕がProcessingと出会ったのは4年前。まだ大学生の頃でした。その当時は、OpenProcessingにスケッチとソースを投下して遊んでおりました。

まずは経歴紹介の代わりに、過去のスケッチをいくつか厳選して紹介したいと思います。再生しても音は出ないので、職場や学校でも安心してご覧いただけます。

最も反響が大きかったのは「3次元Delaunay分割」です。地味な割にそこそこマニアックな計算幾何アルゴリズムで、いろんな人(主に外国人)からいっぱいコメントもらいました。わーい。しにたい。

立体スケッチ

描いた建物が立体になったらいいのになぁと適当に思いついたことから始動したプロジェクト。ちなみに構想の段階で、東京大学の五十嵐先生*3の影響をあからさまに受けている*4

開発期間1ヶ月・予算0円(※ Processing代)という超低予算研究であり、僕の黒歴史的作品でもあります。以下のムービーは、2012年の某学会の講演予告として、僕のシステムを実際に操作する様子をキャプったものです。音が出るので注意*5

内部では線形代数計算幾何組み合わせ最適化の理論などを使ったような気がします。

講演中に、キャンパス内の建物をスケッチで再現したりしたのですが、K都大学の偉い先生から「フッ…ゴミめ」と斬り捨てられてあっけなく終わりました。

ここまで前置き。

新作40秒スケッチ「和」

毎度のことながら、何を作ろうか本当に悩みます。

ネタが無ければ無いなりになんとかするしかないわけで。

  • 何も考えずにコードを書く
  • 何ができあがるかは分からない

というルールで、設計なし・コンセプトなしでガリガリ書いて40秒くらいのスケッチを作りました。こちらからご覧いただけます。

新作スケッチをみる!

今回はリソースがデカすぎてOpenProcessingでは動きやや不安定となってしまったため、H∀∟さんが紹介されているように、GitHubのgh−pagesブランチで公開することにしました。

ちなみにソースコードは一応GitHubに置いたのですが、大半が直感で書いたコードなのでぶっちゃけ見るに堪えないです。ごめんなさい。

おまけスケッチ

さすがにこれだけではあんまりにあんまりなので、今回使用した個別技術を以下にシングルカットしました。

これらについてもソースコードを公開しています。

スペシャルサンクス

Processing.jsでloadImage()が時々失敗する現象の回避策をはぅ君に教えていただきました。感謝。

というわけで、そろそろ力尽きたので本日はこのへんで。

あ、明日12/16は、@tomoeさんです。

*1:インドの伝統的医学における、気質・体質の区分の一つ。火属性はピッタ(पित्त)と言われ、集中力と知的好奇心が旺盛となるエネルギーが溜まりやすいとされている。

*2:カパ(कफ)。安定感があり穏やかなエネルギーが溜まりやすいとされている。

*3:東京大学 五十嵐健夫教授。インタラクション研究の世界的権威。

*4:Takeo Igarashi, Satoshi Matsuoka, Hidehiko Tanaka "Teddy: A Sketching Interface for 3D Freefrom Design", ACM SIGGRAPH 99 ( Impact Paper ) Los Angels, August, 1999.

*5:オリジナルのムービーには、もちろんBGMはついていませんでした。

しあわせってなんだっけ

ある日、こんなツイートがRTされてきました。


人生ってなんだろう

 {
\begin{align}
\textit{Life} &= \displaystyle\int_{\textit{birth}}^{\textit{death}}\dfrac{\textit{happiness}}{\textit{time}}\Delta \textit{time} \\
 &= \textit{happiness} \displaystyle\int_{\textit{birth}}^{\textit{death}}\dfrac{1}{\textit{time}}\Delta \textit{time} \\
 &= \textit{happiness} \bigl[ \log \left| \textit{time} \right| \bigr]_{\textit{birth}}^{\textit{death}} \\
 &= \textit{happiness} \left( \log \left| \textit{death} \right| - \log \left| \textit{birth} \right| \right) \\
 &= \textit{happiness} \log \left| \dfrac{\textit{death}}{\textit{birth}} \right|
\end{align}
}

幸せってなんだろう

 {
\begin{align}
\textit{Life} &= \textit{happiness} \log \left| \dfrac{\textit{death}}{\textit{birth}} \right| \\
\textit{happiness} &= \dfrac{\textit{Life}}{\log \left| \frac{\textit{death}}{\textit{birth}} \right|} \\
 &= \dfrac{\textit{Life} \log e}{\log \left| \frac{\textit{death}}{\textit{birth}} \right|} \\
 &= \textit{Life}\log_{\left| \frac{\textit{death}}{\textit{birth}} \right|}e
\end{align}
}

おまけ:微分してみた

 {
\begin{align}
\textit{Life}(\textit{time}) &= \textit{happiness} \log (\textit{time}) \\
\textit{Life}'(\textit{time}) &= \dfrac{\textit{happiness}}{\textit{time}} \\
\textit{Life}''(\textit{time}) &= -\dfrac{\textit{happiness}}{\textit{time}^{2}} \\
\textit{Life}'''(\textit{time}) &= \dfrac{2\textit{happiness}}{\textit{time}^{3}} \\
&\vdots \\
\textit{Life}^{(n)}(\textit{time}) &= (-1)^{n-1}\dfrac{\left( n-1 \right)! \textit{happiness}}{\textit{time}^{n}} \quad(n \in \mathbb{Z}, n > 0) \\
\end{align}
}

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.