PHPの現場#27を聴いた / interfaceについて

面白かった。最近ぜんぜんPHPは書いてないんですが。

php-genba.shin1x1.com

感想書かなきゃとおもいながらダラダラしてたらもう次のエピソードが出ていたんだけど、1つ前のエピソードの感想です。

いろんな過程を経て、@shin1x1 さんの伝えたいことが熟成されてきている感じがして良い。

途中、interfaceを書くかという話で意見が分かれていた。僕も @shin1x1 さんと同じで、interfaceを使うことに特別な感情はない。

僕も昔はinterfaceを何のために書くのかわからなかったけど、今は「とりあえずinterfaceにしておいて、classでもいいかと思ったらclassにしよう」くらいの感じに思っている。これはきっと僕がOutside Inなプログラムの書きかたをするようになったからだと思う。いま自分が実装しているのはもちろんclassだけど、「そのclassが使っている何か」はinterfaceであろうがclassであろうが単体テストを書いている時点ではどちらでもいいし、そのとき再利用するかどうかは気にしない。

interfaceを何のために書くのかわかっていなかったころの僕は、interfaceというとFooInterfaceというインターフェースに対してFooImplみたいな実装があるみたいなイメージを持っていて、これって何の意味があるのって思っていた。でも、インターフェースと実装は1対1ではない。

f:id:iakio:20190226214528p:plain

phpspecで学ぶLondon School TDD

上のスライドは、Markdown#toHtmlFromReaderというメソッドの引数に渡すのはFileStorageクラスとReaderインターフェースのどちらが良いかという話(この例はphpspecのドキュメントを参考にさせてもらった)。Markdown#toHtmlFromReaderの単体テストを書いている時点では、それを動かすために最小限必要なものをモックする。その方が、意図を正しくコードに反映できると思っている。

僕がプログラマーとして最も影響を受けた読み物の1つは、平鍋さんが2005年に書いた次のブログエントリーだ*1

  • 「テストしやすい」ことが、良い設計(EoT=Ease of Testing):An Agile Way:オルタナティブ・ブログ

  • ぼくは、

    EoT(*1)の高い設計が、よいオブジェクト指向設計である。

    と主張する。設計品質の中で「テスト容易性(EoT)」を最上位と見るのだ。オブジェクト指向のさまざまな機構、用語、考え方は、すべて EoT のため、と捕らえられる。

    僕は「オブジェクト指向とは / interfaceとは / DIとは何か」ということは説明できないけど、それらを何のために使っているかは説明できる。それはテスト容易性のためであり、言いかえれば、「今書いたコードをすぐに動かしてフィードバックを得る」ためとも言える。素早くフィードバックを得るためには、動かすのは大きなコードより小さなコードの方が良い。そのために、大きなコードのほんの一部だけを切り出して動かす方法が必要となる。僕にとってオブジェクト指向やinterfaceやDIは、そのための道具だ。

    オブジェクト指向やinterfaceやDIの話をするときにしばしば再利用性の話がとりあげられる。例えばDIを使えば永続化レイヤーを別のものに差し替えられる、みたいな。でも実際差し替えることなんてそうはおきないんのではないだろうか(まあ無くはないんだけど)。

    でも、あるコードの一部分を、プロダクションコードとしても単体テストとしても動作するようにデザインするということは、それはある意味「再利用している」と言える。僕はこの意味での再利用の方が重要だと思っている。

    もっとも、僕がこのエントリーに強烈なインパクトを受けた理由は、2005年という時代が、前者の意味での再利用性が強調されがちだった時代だったからなのかもしれない。そしてその意味を本当に理解するのは、それから何年も経ってからだった。

    テーブルを作らずにクエリをちょっと試す

    あるクエリがどんな動きをするのかを手を動かしながら探索的に調べたいときに、テーブルを作らずにCTE(Common Table Expression)を使ってデータを作ればはかどるんじゃないだろうかと思った。

    たとえばこれはSQLアンチパターンにでてくるクエリだけど。

    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';

    これがどんな動きをするのかを、テーブルを作らずにCTEを使って実行してみる。まずそのまま実行すると、

    => SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  relation "bugs" does not exist
    LINE 1: SELECT * FROM Bugs

    当然エラーとなる。Bugsが無いのでWITH句を使って適当に作ろう。

    => WITH Bugs AS (
        VALUES (1)
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  relation "tags" does not exist
    LINE 5:   JOIN Tags AS t1 USING (bug_id)

    今度はTagsが無い。

    => WITH Bugs AS (
        VALUES (1)
    ),
    Tags AS (
        VALUES (1)
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  column "bug_id" specified in USING clause does not exist in left table

    両方のテーブルにbug_idが必要だ。

    => WITH Bugs(bug_id) AS (
        VALUES (1)
    ),
    Tags(bug_id) AS (
        VALUES (1)
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
    ERROR:  column t1.tag does not exist
    LINE 10: WHERE t1.tag = 'printing' AND t2.tag = 'performance';

    Tags.tagが必要だ。

    => WITH Bugs(bug_id) AS (
        VALUES (1)
    ),
    Tags(bug_id, tag) AS (
        VALUES (1, 'printing')
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
     bug_id | tag | tag
    --------+-----+-----
    (0 rows)

    OK。エラーはなくなった。後は値を工夫すればいい。

    => WITH Bugs(bug_id, summary) AS (
        VALUES
        (1, 'bug 1'),
        (2, 'bug 2'),
        (3, 'bug 3'),
        (4, 'bug 4')
    ),
    Tags(bug_id, tag) AS (
        VALUES
        (1, 'printing'),
        (1, 'performance'),
        (2, 'printing'),
        (3, 'performance')
    )
    SELECT * FROM Bugs
      JOIN Tags AS t1 USING (bug_id)
      JOIN Tags AS t2 USING (bug_id)
    WHERE t1.tag = 'printing' AND t2.tag = 'performance';
     bug_id | summary |   tag    |     tag
    --------+---------+----------+-------------
          1 | bug 1   | printing | performance
    (1 row)

    データとクエリが1つになるので試行錯誤の過程を記録しやすそうだ。

    あと、この方法はテーブルが存在する場合もWITH句を優先してくれるので、たとえば実際にはBugsが100万行くらいあるんだけど5行くらいで試したい、みたいなこともできる。

    2018年はこんな年でした

    英語学習の動画いろいろ見たけどこれが一番お気に入りです。

    www.youtube.com

    もっと前はこんな年でした

    Windows10の新フォント「UDデジタル教科書体」をブラウザで使ってみる

    ちょっと前の話だけど、Windows 10 Fall Creators Updateでフォントが追加された。

    forest.watch.impress.co.jp

    で、UDデジタル教科書体 NK-Rをブラウザのデフォルトのフォントにしてみた。 ちなみに使っているのはSurface Pro 4で解像度は2736 x 1824、スケーリングは200%。ブラウザはFirefox Developer Editionです。

    f:id:iakio:20180206190717p:plain

    長いドキュメントを読むときはなかなか良いと思う。

    f:id:iakio:20180206192633p:plain

    あと書くときも良い。なんというか、ちょっと違った気分になる。

    他のフォントと混ざったとき、最初は違和感があったけどだんだん気にならなくなってきた。

    f:id:iakio:20180206190749p:plain

    2017年はこんな年でした

    あと適当に動画貼っておきます。 www.nicovideo.jp

    もっと前はこんな年でした

    PostgreSQL勉強会@札幌で発表してきました

    SQLを実行したときに、PostgreSQLはどのようにデータにアクセスしているのか」というタイトルで発表してきました。

    jpug-ezo.connpass.com

    ほとんどデモだけなので、発表資料はありません。

    データベースって、ただ単にデータを読んだり書いたりするだけのものなのに、それを何十年も作り続けている人たちがいて、 PostgreSQLに関して言えば毎年メジャーバージョンアップをしてるわけです。

    その裏側にはものすごくいろいろな工夫があって、自分はそれをソースコードを読んで、頭の中で動きを想像して面白いなーって思うんだけど、 データベースが動くところが目に見えれば、もっと面白いんじゃないかなあ、という感じの発表でした。

    autovacuumが動いてほしいときにはなかなか動かなくて、余計な時に動くという古典的なコントみたいな展開も手伝って、 予想以上に楽しんでもらえたように思います(ポカーンだったらどうしようと思ってました)。

    最後にFILLFACTORを設定しないテーブルと設定したテーブルの比較をしたのですが、 懇親会で @kkkida_twtr さんに、FILLFACTOR設定した方ではHOTが効いてインデックスが更新されていなかったはずということを教えてもらって、 めっちゃその説明すればよかった!と思いました。

    当日の様子は#jpugezoで多少感じられるかもしれません。

    #jpugezo - Twitter Search

    2016年はこんな年でした

    取りあえず今年一番言いたいことは、これ本当にいい話なのでみんな見てほしいということです。 今年というかここ数年で一番のスライド。

    speakerdeck.com

    あと適当に動画貼っておきます。

    www.nicovideo.jp

    www.nicovideo.jp

    もっと前はこんな年でした