猫野詩梨帳

かわいいはかしこい

プログラムの設計がうまくいかんときに読む本

ここ2年くらいでプログラムの設計がちょっとうまくなったのでどういう本を読んだとかを書いておきます。

とはいえまだまだだとは思うので、後で見返してなんかおかしなこと言ってるな~となるかもしれませんが。

読んだ順ではないですがこの順で読むといいだろうという順になっています。

オブジェクト指向のこころ

以前からオブジェクト指向プログラミング言語はそれなりに扱えたのでオブジェクト指向わかった気になっていました。クラスとか使っていい感じにまとめられるのがええんやろみたいな。

この本は、抽象(インターフェース)を用いたプログラミングの強力さを教えてくれます。これが変更に強くテストしやすい設計に於ける基礎となります。

オブジェクト指向デザインパターンの入門書としても、これ1冊読んでおけば問題ないと思います。高凝集・低結合、実装のカプセル化、継承よりコンポジション(委譲)、などがわかるようになります。

物足りない人はバートランド・メイヤーとか読めばいいと思います。

ドメイン駆動設計入門

ドメイン駆動設計 (DDD) は複雑化していくソフトウェアをなんとかするために有効な設計手法として近年注目されています(たぶん)。

DDD はこの後に紹介するエリック・エヴァンスなどが詳しいのですが、如何せん分厚いのと難しいのでとっつきにくさがかなりあります。

最近出たこの本はそんな DDD に入門するための入門書というような感じで、ドメイン駆動設計においてよく使われるリポジトリや集約といったデザインパターンをメインに解説しています。

この1冊だけで DDD 完全に理解したというのは難しいかもしれませんが、次に進むための足がかりにはなるでしょう。

ドメイン駆動設計 モデリング/実装ガイド

little-hands.booth.pm

こちらもドメイン駆動設計 (DDD) への入門として読みやすい本です。DDD とは何なのかという概要から、アーキテクチャや実装までカバーしています。

こちらの本は次に挙げるエリック・エヴァンスや IDDD を読むための準備として読むのもいいですが、100 ページ程でコンパクトに纏まっているのである程度 DDD を学んだ後に再読してみると知識が整理されてよいかもしれません。

エリック・エヴァンスのドメイン駆動設計

ドメイン駆動設計 (DDD) の提唱者、エリック・エヴァンスによる本です。

ドメイン(ソフトウェアが扱う領域)から概念を抽出し、それを抽象化した「ドメインモデル」を中心としてソフトウェアを設計していく手法について語られています。

何言ってんだって感じかもしれません。実際私もいまだに何言ってんだという感じなのですが、今のところは次のような理解をしています。

例えばシンプルな CRUD(作成、読み取り、更新、削除)を行うようなシステムでは、データベースを中心にして設計し、それに対する CRUD 機能を実装していくことが多いとおもいます。

しかし、DDD では、(一旦データベースのことを考えるのをやめて、)いまシステム化したい業務について詳しい人(ドメインエキスパート)と話すときに出てくる概念(「アカウント」とか、「注文」とか、「経路」とか……)やそれに対するロジック(操作、制約、仕様など)をコードで(例えばオブジェクト指向ならクラスなどを用いて)表現することを試みます。

これにより、システムが複雑になっても、コードが明快で読みやすくなります。コード自体がロジックなどの仕様(ドメイン知識)を反映しているため、仕様書とコードの乖離を防ぎやすくなり(場合によってはドキュメントが不要になるかもしれません)、修正や変更が行いやすくなります。

また、システム化したい業務について詳しい人(ドメインエキスパート)とコミュニケーションをとりながらモデリングしていくことは、「顧客が本当に必要なもの」を探しながら設計していくことにつながります。つまり、ソフトウェアの価値の向上が期待できます。

なかなか分厚く、抽象的でわかりにくい話も多いのですが、必ず何か得られるものがある本だと思います。あきらめずに繰り返し挑戦していきましょう。

余談ですが、帯に書かれていた「徹頭徹尾有用な」という推薦文句が好きです。

実践ドメイン駆動設計

実践ドメイン駆動設計

実践ドメイン駆動設計

通称 IDDD。具体的なコード例などが多く実践的な本です。またドメインイベントなどの新しい概念も紹介されています。とはいえめちゃ分厚い上にやっぱりわかりにくいところも多々あります。カウボーイのジョークもわからんし。

エリック・エヴァンスではあまり直接的には触れられていないのですが、この本では依存性逆転の原則 (DIP) やそれを利用したヘキサゴナルアーキテクチャなどの話が登場します。これはまさに抽象(インターフェース)を用いたプログラミングです。これにより、ドメインモデルが特定のデータベースなどに依存することを避けることができます。ドメインモデルが特定のインフラに依存していると、例えばデータベースを先に設計する必要が出てきて、ドメイン駆動ではなくデータ駆動っぽくなってしまったりします。

この本を全て理解できなくても、ここまでくれば DDD を実践するための基礎はできているはずです。あとは実践して、試行錯誤して、また読み直してみたり……といった感じでしょうか。頑張りましょう。

ちなみにエリック・エヴァンスより先にこちらを読む人もいるらしいですが、個人的にはエリック・エヴァンスを先に読んだ方がわかりやすいかなと思います。途中で挫折したら変えてみてもいいかも。

まとめ

ぜったい美しいソフトウェアを書いて美少女ハッカーになろうな。

あとほかにもこんな本があるよ! とかこういう設計手法があるよ! とかあったらおしえてね。

追記 2020/10/26

最近みたスライドなのですが、ドメイン駆動設計の目的の一つとして「コアドメインに集中することでビジネス価値を最大化する」ということが挙げられていてなるほどなあと思ったので紹介します。Rails の知識がなくても読めます。

speakerdeck.com

Python クイズ ~ is 編 ~

だいぶん前の話だけれどこんな Python クイズを作りました(Python3 を想定)。

True in [1] is TrueTrueFalse かというもの。

ちょっと考えてみてください。

ヒント

ヒントです。

>>> True in [1]
True

これは True == 1, False == 0 だからです。

答え

さて、答えは実行してみるとすぐわかります。

>>> True in [1] is True
False

True in [1]True なので全体として True だろ、と思った人も多いのではないでしょうか。ぼくはそう思いました。でも違うのです。

こうすると True になります。

>>> (True in [1]) is True
True

これで勘のいいひとはわかったかもしれません。

これはおそらくですが、Python では比較演算子がチェイニング(連鎖)するからです。

Python では以下のような記述をすることができます。

>>> x = 20
>>> 10 < x < 30
True

これにより、普通 (10 < x) && (x < 30) みたいに書くところを 10 < x < 30 と書けます。

そしてこの比較演算子の連鎖は、すべての比較演算子 (< > == >= <= != is is not in not in) で発生するようなのです。

つまり、問題の True in [1] is True は、おそらくですが (True in [1]) and ([1] is True) と評価されているのだと思われます。

[1] is True は当然 False なので全体として False になります。

>>> True in [1] is True
False
>>> (True in [1]) and ([1] is True)
False
>>> [1] is True
False

気を付けること

この挙動が問題になることは殆どないと思います。

1つだけ思いついたのは、以下のような場合です。

if x in [1, 2, 3] == True:
    print("OK!")

意図としては、もし x123 だったら OK! と出力したいというものです。

とりわけ Python 初心者などの場合、もしかしたら上記のようなコードを書いてしまうことがあるかもしれません。

しかし上記のコードでは決して OK! が出力されることはありません。理由はもちろんお分かりですね?

この問題を回避するためには、単に以下のようにします。

if x in [1, 2, 3]:
    print("OK!")

Python では == True(或いは is True)のような記述は通常あまりされないので、覚えておくとよいかもしれません。

参考

docs.python.org

比較はいくらでも連鎖することができます。例えば x < y <= z は x < y and y <= z と等価になります。

...

形式的には、 a, b, c, ..., y, z が式で op1, op2, ..., opN が比較演算子である場合、 a op1 b op2 c ... y opN z は a op1 b and b op2 c and ... y opN z と等価になります。

(上記リンク内のページより引用)

Haskell と Expression problem とかのメモ

※ この記事はメモみたいなものなので, めっちゃ適当なことを書いているかもしれない上に話がまとまっていないので注意してください.

先日こういうツイートをしました.

抽象化による switch の置き換え

最近「オブジェクト指向のこころ」とかを読んで,「抽象化によって switch を置き換える」という手法があることを知りました.

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

例えば,A, B, C の3つの実装があり,実装によって処理を切り替えなければならない場合,条件分岐で

switch (IMPL)
  case A:
     ...
  case B:
     ...
  case C:
     ...

みたいに書けると思います.

ただ,プログラムの各所にこういった switch が現れてくるとだんだん保守が大変になっていきます. 例えば実装 D が増えた場合,こういった switch 全てを見つけ出して case D を追加しなければならなくなります.

これを解決するためにオブジェクトを用いることができます.即ち処理部分を

Impl impl = ImplA()

impl.process()

みたいにしておきます.すると,新たな実装 D が追加されたとしても,ImplAImplD に切り替えるだけでよくなります. もちろん impl は持ち回すかグローバルに取得するか等しないといけないのですが,呼び出し側 (impl.process()) は変更せずに済みます. これにより修正がちょっと楽になります.

Haskell の data におけるパターンマッチ分散問題

ここで,Haskell のパターンマッチでも似たような状況になることを思い出しました.

例えば

data MyData = MyDataA | MyDataB

みたいな data に対してパターンマッチするような処理がいくつかあるとします.

このとき MyDataC が追加されたとしたら,パターンマッチを行っている処理全てを見つけ出して修正する必要が発生します.

もしかしてこれもなんとかして解決できるんじゃろか? というのが上述の一連のツイートの発端です.

型クラスを使用した抽象化によるパターンマッチの置き換え

オブジェクト指向のクラスやインターフェース(というか Java とかの interface)は Haskell の型クラスと比較されることがあります. 直感的にはこれらは「近そう」な感じはしますが,たぶんこれらはどちらも抽象化を実現するものなんだと思います.

どちらも抽象化を実現するものであるならば,例えば先程の「A, B, C の3つの実装」の話については, 「型クラスを使用した抽象化によりパターンマッチを置き換える」こともできそうに思えます.

実際に,

class Impl a where
  process :: a -> ...

data ImplA = ImplA ...

instance Impl ImplA where
  process (ImplA ...) = ...

とすることもできそうです.これはなんとなく「抽象化による switch の置き換え」に近い気がします.

data ふたたび

ここで,逆に Haskell の data はそれ以外の言語での何にあたるのか? という疑問も生じます.

Haskell の data はデータ型を定義するものですが,特に代数的データ型と呼ばれるデータ型を定義することができます.

例えば

data MyData = MyDataA | MyDataB

みたいなのは列挙型と呼ばれるようです.

qiita.com

感じとしては「data に対するパターンマッチ」は「enum(例えば JavaEnum)に対する switch」に近いものなのかもしれません.

さて,上述のツイート中にもありますが, 以下のサイトでは「列挙型とswitch文を使ったソースコードは、ポリモーフィズムを使って書き直すべき典型的な悪い例」という記述が見受けられ ,またその上で列挙型を使用したほうが良い場合もあるとも述べられています.

www.aerith.net

ここで注目したいのは,インターフェースと実装を用いて書いたものを列挙型でも書くことができる場合があり(そしておそらくその逆も), それぞれにメリットデメリットがあるということ(,また Java の列挙型ではある程度ポリモーフィズムを達成することができるということ)です.

インターフェースと実装 列挙型
利点 swtich が減る クラス数が増えない
欠点 クラス数が増える switch が増える (JavaEnum では回避可能)

Haskell においても,先程の「型クラスを使用した抽象化によりパターンマッチを置き換える」の例を

data Impl = ImplA ... | ImplB ...

としてパターンマッチで書き換えることが出来そうに思われます.

しかし,この辺でちょっと混乱してきました. 今まで data と class は明らかに異質なもので,それぞれ使いどころがはっきりしていると思っていたからです. 特に class はインターフェースに似ているとはいえ,同じような使い方が出来るかどうかも疑問です.

data と class の両方で(うまく)書けるような場合が本当に出現するのでしょうか? そして,その場合はどちらを採用するべきなのでしょうか?

これに関して明確な答えはまだ出ていないのですが, data を class で書き換えるという方法はある程度うまく行くようです. これについては後述します.

意見とか

最初のツイートを再掲します.

これに関していろいろなコメントがあったのでいくつか引用させて頂きます.

既存のコードに修正が入ってしまうのはある程度仕方ないという意見です.

これは尤もだと思います. 結局の所真の目的は switch(パターンマッチ)を潰すことではなくて,コードのメンテナンスコストを下げることだからです.

型クラスを使う方法もあるということで,これは先述した data と class の書き換えの可能性を示唆していると思われます. また,いつ data を使うべきかについては,どのくらい流動的(でない)かにもよるという意見もありました.

ほか,次のようなコメントもありました.

Expression problem (The Expression Problem) とは,既存のコードをいじらずに,データ型に新しいケースを追加するにはどうしたらいいかという問題らしいです. つまり,正に今考えている問題のことです.ちゃんと名前がついていたんですね.

Expression problem については以下の記事が詳しいみたいです. 名前がわかると検索ができるので,名前は大事ですね.

maoe.hatenadiary.jp

この中で(正確にはこの中で紹介されている動画の中で),データを追加するという問題を型クラスで解決する方法が述べられています. これはまさに先程の data を class で書き換えるということだと思います.

ということで,結論としては上の記事を読んでくださいということになってしまいました.

Data Types a la Carte や Tagless final 等については知らなかったので,また今度調べてみたいと思います.

機会があれば続く.

余談

ウェアハウス川崎が閉店するらしいです.悲しい.

nlab.itmedia.co.jp

【読書記録】達人に学ぶSQL徹底指南書 第2版

発信能力のないクソザコエンジニアなので読書記録をつけることにしました.よんだら書くぞ🐢

達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ (CodeZine BOOKS)

達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ (CodeZine BOOKS)

内容

前半は SQL を書く上でのテクニック.CASE, HAVING, ウィンドウ関数の使いこなし方など.後半は SQL についての歴史・コラムなど.

動機・目的

ふだん DynamoDB など NoSQL を利用しているが,RDB と上手く使い分けたい.RDB についてはまず次の本を読んだので,関連して.

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

感想

Web 連載を加筆修正したものらしく,良くも悪くも web 連載を読んでいる気分になる.また数学的な基礎付けのお話や歴史など読み物としても面白く,筆者の RDB/SQL に対する深い知識が感じられる.

得たもの

おおかた内容どうり.SQL を実務で使ったことがまだあまりないため,実務で使ってからまた読むといいかもしれない.あとセルコの SQL パズルを読む前に読むとよさそう.

得られなかったもの

とくになし.

関連して読みたい

RDB/SQL 関連の本で読みたいのはたくさんあるけど,まずはやはり設計関連の本としてこのあたりかな.

SQLアンチパターン

SQLアンチパターン

失敗から学ぶRDBの正しい歩き方 (Software Design plus)

失敗から学ぶRDBの正しい歩き方 (Software Design plus)

数学的な理論に関しては,この前本屋で立ち読みしたこれがかなり詳しそうだった.時間があったら読みたい(読まなそう).

あと NoSQL(特に DynamoDB みたいなキーバリューとか)周りの設計とかについていい本があればいいんだけどなあ.洋書でなんかいいのあるのかな.詳しい人いたら教えてください🐢

10連休

10連休ですね.お仕事したいと行ったらダメって言われました(ホワイト職場).

遊びにも行きにくいので英語の勉強とかを始めました.英語の会議とかいるだけで一切発言しないパーソンになっている……

話す英語 (実戦力徹底トレーニング)

話す英語 (実戦力徹底トレーニング)

さて,今年の?目標はおくいず (QMA) と仕事の両立ということで,おくいずは今まで通り頑張りつつお仕事の方でも何かやっていきたいなと思っています.

その1つとして資格を取るということをやっていこうかなと思っています.とりあえずは

  • 英検準1級
  • AWS ソリューションアーキテクト アソシエイト

あたりを目標にしていこうかなあと.

英検は TOEIC / TOEFL の方がいいのでは? という話もあると思いますが,単純に合格不合格が出たほうがモチベーションになりやすいと思いました(適当).実際受けるかは不明です.

あとちまちまドイツ語とか勉強してるので仕事の役には立ちそうもないけどココらへんもなんかやりたいかも.

AWS ソリューションアーキテクトはアソシエイトのサンプル問題解いたらなんかすごい簡単だったのでいけるんじゃね? と思った.

徹底攻略 AWS認定 ソリューションアーキテクト ? アソシエイト教科書

徹底攻略 AWS認定 ソリューションアーキテクト ? アソシエイト教科書

↑この本のまえがきにソリューションアーキテクトアソシエイトは年収 12 万ドル稼げる資格ですみたいなこと書いてあるけどマジ? あたくしの年収 XXX くらいしかないんですが……

皆さんの有意義な 10 連休をお祈り申し上げます.ねこのしりでした.

2年くらいお仕事したのでふりかえろ〜!

皆さんお久しぶりです.猫野詩梨です(だれ?).今の会社で働き始めてからちょうど2年くらい立ちました.なんかいい機会なので,ここ2年間と最近について簡単に振り返ってみました.

喋る人がいない

これは私のコミュニケーションスキルのせいという感じが大分あるのですが,「技術について」喋る人があんまりいないです.

関数型プログラミングについて! とか,新しい言語・新しいツールについて! とか,GitHub でトレンドになってたリポジトリについて! とか,なんかこういう技術書が出てた! 読んだ! とか.

そんな感じの話が出来る人がいればなーと思います.

開発に関して

TypeScript じゃなくて JavaScript, Scala じゃなくて Java, Phoenix じゃなくて Ruby on Rails, GraphQL じゃなくて REST, アジャイルじゃなくてウォーターフォールなどなど.常に前者が優れているというわけではなく,選択できるということが大事なのだとは思いますが,そもそも選択するという余地が与えられていないといったことが多いです.てか Phoenix って何?みたいな.まあこれは仕方のないところもあると思います.そもそも最新技術バリバリで開発できるなんて幻想なのかもしれません……(でも今並べたの,最新技術でもないよね)

あと気になるのは CI/CD や IaC といった概念が浸透していない等といったところでしょうか.私がそう感じてるだけかも.人数が少ないせいもあるのかな.

AWS/サーバーレスはすごい!

なぜか AWS 周りをよく担当しています.AWS はすごいです.早い,安い,そして可用性が高い! Lambda と DynamoDB とその他色々でだいたい何でもできます.しかも最近では中規模システムでも仮想サーバーすら立てずに構築できたりしますし,サーバーレス万歳です.

いつの間にかサービス増えてたりしてるのもすごい.私が入社してからの2年でも,AWS はすごい進化してるように感じます.こんなことできたらいいな〜と思ってたら,いつの間にか出来るようになってたとか.最近だと,EKS とか Managed Blockchain(まだプレビュー)とか.Lambda のカスタムランタイムとかもありましたね.DynamoDB まわりのアップデートとかも……キリがないのでやめましょう.

でも実際使ってるとコレ出来ないの? みたいな不満も結構あります.でもあと 5 年くらいしたら今の不満は全部無くなってるんじゃないかな! 楽しみです.

まとめ

今季はえんどろ〜!,私に天使が舞い降りた!,バーチャルさんはみている,けものフレンズ2,ケムリクサと5本もアニメ見ました.

迷ったのですが,パッケージのユーシャちゃんのお腹に負けてえんどろ〜!の Blu-ray を購入いたしました.大変満足しています.

追記(3/31):わたてんとケムリクサの円盤も買いました.

Common Lisp で dircat

あるディレクトリ内の全ファイルを cat(猫)する必要があった.しかし cat(猫)でやろうとしたら引数が多すぎると文句を言われた.そこで Common Lisp で書いた.なお CL の知識は Land of Lisp を半分読んだくらいしかありません.

roswell とかいうのを使っていて,インストールすれば実行可能ファイルとして実行できる.

github.com

#!/bin/sh
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#

;;;; cat recursively in specified directory

;; init forms
(progn
  (ros:ensure-asdf)
  #+quicklisp (ql:quickload '(:cl-fad :alexandria) :silent t)
  )

(defun write-bytes (result-type seq stm)
  (map result-type
       (lambda (x)
         (write-byte x stm))
       seq))

(defun mainproc (dirpath)
  (cl-fad:walk-directory
    dirpath
    (lambda (path)
      (write-bytes
        nil (alexandria:read-file-into-byte-vector path) *standard-output*))))

(defun print-usage ()
  (format t "Usage: ./dircat.ros DIRPATH~%"))

(defun error-exit (msg)
  (format t "~a~%~%" msg)
  (print-usage)
  1)

(defun main (&rest argv)
  (if (eql (length argv) 1)
    (let* ((arg (car argv))
           (dirpath (cl-fad:directory-exists-p arg)))
      (if dirpath
        (mainproc dirpath)
        (error-exit (format nil "~s is not a directory" arg))))
    (error-exit "Illegal number of arguments (Expected 1)")))

;;; vim: set ft=lisp lisp:

一見うまく動いているようなので,残業せずに済んだ.しかし,普通の cat(猫)と比べると遅い.にゃんでかな.

お会社にお言語オタクみたいな人がいなくて寂しい.