Yabu.log

ITなどの雑記

golangを触って見た雑感

Goならわかるシステムプログラミング

Goならわかるシステムプログラミング

C言語はいろいろ大変だからgoでシステムプログラミングを学ぼうぜ。 という趣旨の本を読んでいる。ちょうど半分くらい(146/338)読めたのでgoの雑感でも適当に箇条書きで書いてみる。*1

goはtcfmで何度か触れられていたり、インタプリタ作る本が出ていたり

Go言語でつくるインタプリタ

Go言語でつくるインタプリタ

で結構興味がある。直近でOSの仕組みを結構学んだのでそれに近いことも書かれているし、横浜で楽しそうな読書会をやっている

yokohama-go-reading.connpass.com

などなど、色んなタイミングが重なってちょうどいいかなと思って手に取ってみた次第です

  • go run <.gofile>で動かすので、一見インタプリタっぽいと思ったが、コンパイルしてネイティブの実行バイナリを作成できる
$ go helloworld.go
helloworld

$ go build helloworld.go

$ ls
helloworld       helloworld.go

$ file helloworld
helloworld: Mach-O 64-bit executable x86_64

$ ./helloworld
helloworld
  • 試していないがクロスコンパイルとかもできるらしい

https://qiita.com/Jxck_/items/02185f51162e92759ebe

  • アセンブルしてみようと思ったがバイナリサイズができすぎてよくわからなかった
$ ls -alt helloc helloworld
-rwxr-xr-x  1  staff     8432  9  3 02:14 helloc
-rwxr-xr-x  1  staff  2011816  9  3 02:13 helloworld
  • hellocはC言語のhelloworldだけど、goはその200倍近いサイズがある。

    • ランタイムや何かでかいライブラリをリンクしているのかな。
  • goはgoとアセンブラで書かれている

  • インターフェースの説明がわかりやすい
    • interfaceを実装したい型の宣言時にinterfaceの情報を含めなくて良い
    • 確かrubyもこんな感じの仕様だった気がする(publicメンバが同一ならポリモーフィックな関係がある)
      • ちょっと調べましたがダックタイピングと言われるものですね

ダック・タイピング - Wikipedia

type Talker interface{
  Talk()
}

//構造体GreeterにはTalkerを明示的に実装しているという情報は含めない
//Javaのimplements的なやつがない
type Greeter struct{
  name String
}

//この宣言の時の(g Greeter)はレシーバ。どの構造体にインスタンスメソッドを実装するか、的なやつ
func(g Greeter) Talk(){
  fmt.Printf("hello,"g.name)
}
func main(){
  var talker Talker
  talkder = &Greeter{"wozozo"}
  talkder.Talk()
}
  • メソッド名は大文字で書くとpublic,小文字で書くとprivateの可視性をもつ
  • goのbufio.Writerのデフォルトバッファサイズは4096
    • いろんなバッファ、4096のものが多い気がする
  • goのprint文は書いた分だけシステムコールが発行される

  • p44より

    (goには)残念ながらErlangの文法に組み込まれているバイナリパターンマッチのような強力なバイナリ解析はありません

    • バイナリパターンマッチ、よくわからないけどErlang詳しい人に聞いたとところ、ヘッダの解析とかにめちゃくちゃ使えるらしい。
  • goは関数呼び出し時に関数名に"go "をつけることでgoroutine(軽量スレッド)と呼ばれる並列処理が使える
    • goのランタイムがOSっぽくこのスレッドを管理する
      • CPUからOSからはプロセスが増えたようには見えないらしい
      • Cだとfolkするとtopコマンドとかで表示されるプロセスが増える
  • goにはジェネリクス今の所ない
    • go2で入るらしい
  • goには例外がない
    • channelというものを使って処理の動機を取ることができる
    • FIFOっぽく使える
  • rustみたいに特定のポスト特定の言語みたいなのを狙っている感じはない
    • rustはC++の代替を目標?にしている空気がある
  • goはML系言語をやっている人たちからあまり好ましく思われていない?らしい

本書はgoそのものの情報よりもシステムプログラミングのトピックが多い。HTTP2やファイルディスクリプタ,RESTやunix socketなどなど。

聞きかじりの知識と適当にググった情報でやってるけど大丈夫かな。*2

これはgoに限った話ではないけど、後発の言語は、以前の言語の問題点とかハマりポイントをうまく回避したり、 いろんな言語で成功している概念などを取り入れている印象があるので、特に使わなくても新しい言語を学んでおくメリットはあると思し、実際学んでみると結構楽しい。

*1:最初の環境構築をサボったので大急ぎで演習問題を解いて追い付こうとしているところです

*2:それでもある程度挫折せずに進めているのはgoの凄いところなんじゃないかなと思う

Effective SQL 読書会(4)に参加

Effective SQL

Effective SQL

冒頭で書籍紹介。SQLパフォーマンス詳解

SQLパフォーマンス詳解

SQLパフォーマンス詳解

インデックスなどの説明が詳しい他の本。

様々な実装のインデックスなど日本語で説明している本は少ないとか?

  • MySQLはnested loop結合しかない

  • 中間一致の条件を使うとBTreeが使えない

    • like '%ELLE%'
  • 主キーのインデックスはリーフノードに値を持つ
  • 主キー以外のインデックスはリーフノードに主キーの値をもつ
    • セカンダリーインデックス
  • 多量のデータを検索する場合はインデックスが使われないものもある
    • 実装にもよるけど、数10パーセントの量とか

MySQLでは、インデックス宣言のASCキーワードとDESCキーワードは無視される

とあるが、MySQLは8.0から対応している

  • MySQLのchange buffer(index更新の最適化)

  • 冗長なインデックスの例

    • index(col1)
    • index(col1,col2)
    • というインデックスがある場合は、前者は冗長なので消すこと!
  • indexのrebuildは
  • 参照整合性のことをDRI(Declartive Referential Integrity)と書いてあるのは初めて見た
  • check制約
    • MySQL使えない。宣言してもエラーにはならないが無視される
    • トリガーを作るDBMSもある
  • トリーが、ストアドルーチンに可搬性はない
    • PLSQLはOracleのものだがEnterprizeDB,DB2でも動くように取り入れられている。
  • PostgreSQLBSDライセンスのため派生が作りやすい(たくさん)
  • 書籍データベースリファクタリングではデータ移行の段階としてトリガーをフル活用する
    • 部分インデックス
      • MySQLだと独自の意味(長い文字列の先頭3文字とか)
      • 他のDBMSだとindexにwhereが入ったもの(index化するデータを選択する)
  • パーティション分割は必ず早くなるわけではない
  • 適当なWHERE句が必要。
  • インデックスのフィルター化
  • MySQLでは使えない
  • データに寄せる(寿命の長い方に設計を寄せる)
  • ストアドなどはオプティマイザが見ない?
  • MySQLは大文字小文字を区別している
    • FS依存
    • 他のDBは区別しない
  • 最近はNULLS FIRST,NULLS LASTをサポートしているDBは他にもあるはず
    • MSQLとSQLServer以外は最近はサポートしている
  • UNKNOWNリテラルは存在する。
  • SQLパフォーマンス詳細の作者が書いているブログが面白い(luke!indexを使え!)

use-the-index-luke.com

- SQL2016の詳解など
  • rtfm(Read The F***ing Manual)
  • サポートの人がよく使うスラングらしい

    感想

    インデックスのフィルター化は初めて知りました。

余談ですが初参加。スケジュールに調整がつかなかったり、体調が悪かったりして参加ができていなかった。 ちなみに今日も1時間しか寝ていないので死ぬほどしんどい。

C言語で美咲フォントを扱う4

前回から引き続き、美咲フォントという8*8ピクセルのサイズで第二水準の漢字までが扱えるビットマップフォントをC言語で扱うプログラムを作っています。

ソースはここにあります。

github.com

処理は結構完成していて、呼び出し側から使うAPIの仕様などが固まってきました。

  //文字のデータ。"美しい美咲フォントの世界"
  char test_letters[12][2] ={{0x94,0xfc}, {0x82,0xb5}, {0x82,0xa2}, {0x94,0xfc}, {0x8d,0xe7}, {0x83,0x74}, {0x83,0x48}, {0x83,0x93},{0x83,0x67}, {0x82,0xcc}, {0x90,0xa2}, {0x8a,0x45}};
  for(int i=0;i<(int)sizeof(test_letters)/2;i++){
    char test_letter0[2];

    //多次元配列もう少しスマートにコピーしたいなぁ
    test_letter0[0] =  test_letters[i][0];
    test_letter0[1] =  test_letters[i][1];
    
    //独自定義型。実態はchar[8]
    font_data print_test;

    //フォントを取得する
    get_font_data(test_letter0,&print_test);
    
    //取得したフォントをコンソールに表示。
    print_font(print_test);
  }

print_fontはutil的なものですが。bmpで扱いたかったりvramにデータを描く場合は呼び出し側に何か便利な関数が欲しいですね。

00100100
11111110
01111100
00010000
11111110
00010000
11101110
00000000
00100000
00100000
00100000
00100000
00100000
00100010
00011100
00000000
00000000
10001000
10000100
10000010
10000010
01010000
00100000
00000000
00100100
11111110
01111100
00010000
11111110
00010000
11101110
00000000
00010100
11100000
10111110
10101000
11111110
00001000
00110110
00000000
00000000
01111110
00000010
00000010
00000100
00001000
00110000
00000000
00000000
00000000
00001000
01111100
00011000
01101000
00011000
00000000
01000000
00100000
00000010
00000010
00000100
00001000
01110000
00000000
00100000
00100000
00100000
00111000
00100100
00100000
00100000
00000000
00000000
00111000
01010100
10010010
10100010
01000100
00011000
00000000
01010100
01010100
11111110
01010100
01011100
01000000
01111110
00000000
01111100
01010100
01111100
01010100
01111100
10101010
01001000
00000000
01010100
11101110
01111100
11111010
11110100
01000010
01110100
00000000

適当に見やすく置換して見ました

    爨    爨    
爨爨爨爨爨爨爨  
  爨爨爨爨爨    
      爨        
爨爨爨爨爨爨爨  
      爨        
爨爨爨  爨爨爨  
                
    爨          
    爨          
    爨          
    爨          
    爨          
    爨      爨  
      爨爨爨    
                
                
爨      爨      
爨        爨    
爨          爨  
爨          爨  
  爨  爨        
    爨          
                
    爨    爨    
爨爨爨爨爨爨爨  
  爨爨爨爨爨    
      爨        
爨爨爨爨爨爨爨  
      爨        
爨爨爨  爨爨爨  
                
      爨  爨    
爨爨爨          
爨  爨爨爨爨爨  
爨  爨  爨      
爨爨爨爨爨爨爨  
        爨      
    爨爨  爨爨  
                
                
  爨爨爨爨爨爨  
            爨  
            爨  
          爨    
        爨      
    爨爨        
                
                
                
        爨      
  爨爨爨爨爨    
      爨爨      
  爨爨  爨      
      爨爨      
                
  爨            
    爨          
            爨  
            爨  
          爨    
        爨      
  爨爨爨        
                
    爨          
    爨          
    爨          
    爨爨爨      
    爨    爨    
    爨          
    爨          
                
                
    爨爨爨      
  爨  爨  爨    
爨    爨    爨  
爨  爨      爨  
  爨      爨    
      爨爨      
                
  爨  爨  爨    
  爨  爨  爨    
爨爨爨爨爨爨爨  
  爨  爨  爨    
  爨  爨爨爨    
  爨            
  爨爨爨爨爨爨  
                
  爨爨爨爨爨    
  爨  爨  爨    
  爨爨爨爨爨    
  爨  爨  爨    
  爨爨爨爨爨    
爨  爨  爨  爨  
  爨    爨      
                

このライブラリのtodoとしてはヘッダの整理とライブラリとしてのコンパイルでしょうか。 あとエラー処理を一切考えていないので、それも必要かもしれません。

CC本読み会第11回に参加

Concurrency Control and Recovery in Database Systemsという本を読む読書会に参加しました。なお11回からの途中参入になります。

p77の3.10 distributed two phase lockingから読みました。

本書について

Concurrency Control and Recovery in Database Systems

Concurrency Control and Recovery in Database Systems

データベースの基礎理論を扱う古典になります。

私が今ポチったからアマゾンのマケプレの在庫1個になっちゃいましたwwww

と希少品を自慢したいところですが、ご安心ください。こちらから書籍と全く同内容のPDFがダウンロードできます。

内容ですが、本当に「基礎」という感じです。

超絶技術を扱っているわけではありませんが、簡単という意味でもありませんが、 データをどう保護するか、という点で非常に本質的な知識だという印象です。

私に限らずどうしても昨今のエンジニアはツールとかフレームワークに関心が言ってしまい,こういう「基礎」の部分ががいい加減になりがちですね。

感想

11回目から初参加になります。こちらは分散システムの読書会と違って、

  • DBという自分の得意分野である
  • 分散本ほど周りの参加者が読み込んでいない
  • 分散本は36回もすでに経過しているところに途中参入した。

などの点から案外ついていけるのでは?と思っていましたが、

本書の扱うテーマはRDBMSの内部構造のものがテーマななので全くの専門外でした。

そのため非常に難しかったです。私が得意だったDBの知識というものはあくまで業務システムから利用する側の知識でしかなかったり、

そもそもの知識量が資格の勉強やここ1年くらいで頑張って身につけたものに過ぎなかったのです。*1

ちょうど1年前にこんな記事

qiita.com

を書いている程度の状態だったので、周りの参加者のレベルを考えるともっと精進する必要があります。

トランザクション設計はもちろん業務システムの要件として確認しますが、 ミドルウェアが担保しているトランザクションというのが自分には全く知識外でした。

質問をしましたが、「自明!」という感じの回答が帰ってきたので、本書の前のパートに書かれているのかもしれません。しっかり復習して次回は挑みたいと思います。

ライブロックも単語は知っていますが、内容は知りません。

ノート

  • global serializable

    • local serializable
      • localのものを足し混んでgrobalのserializableになるような単純なものではない。
  • global なserializableを保証するのは難しい

  • Effective Javaに乗っているJavaのSerializationの代替

    • 私がこの記事で書いている内容を話して見ました。

yuyubu.hatenablog.com

  • protcol bufferでも遅い

  • castor

    • 神林さんが詳しいらしい
    • Object指向のDBで使う
  • CorbaではJavaなどのserializeなどの意味でマーシャライズという言葉を使う

  • 2PLと2PCはいまだに進歩していない

    • 分散合意が関わってくるから
    • 2PCのまま
    • 人類に分散システムは早い
  • 3-4の章で普通のdeadlockはやっている(きちんと復習すること!)

The global waits-for graph, WFG, is the union of the local WFG,s.

  • global dead lockはabortの際にvictimを選ぶ必要がある。
    • その情報が必要
      • がその情報は送信コストが大きくならないようにpiggybackで送る

廃止判断としてp58掲載の

  • The amount of effort that has already been invested in the transaction. This effort will be lost if the transaction is aborted.

  • The cost of aborting the transaction. This cost generally depends on the number of updates the transaction has already performed.

  • The amount of effort it will take to finish executing the transaction. The scheduler wants to avoid aborting a transaction that is almost finished. To do this, it must be able to predict the future behavior of active trans- actions, e.g., based on the transaction’s type (Deposits are short, Audits are long}.

  • The number of cycles that contain the transact~iion. Since aborting a transaction breaks all cycles that contain it, it is best to abort transac- tions that are part of more than one cycle (if such transactions exist).

のどれを送るかを選択する必要がある 最悪トランザクションそのものを送る羽目になる

  • Mysqlはこのような検知はせずにタイムアウトさせる
  • globalなWFGは見たことがない

  • 分散システムのdead lockの検知は難しいらしい。(タイムアウトがメイン)

  • 今後の分散システムはmany coreが潮流になる。

  • マルチノードでやっている仕組みをラックの単体のサーバーに持ってきてもうまくいかない

  • 業務要件に依存しないトランザクションの設計?実装?などがあるのか?

  • grobalのロックは使わずにlocalのロックを使う

  • コンカレントな処理由来のバグはベンダーに持っていってもわからないことが多い

    • シナリオが複雑
    • ハードウェアの障害
  • ECCがついているからメモリのビットエラーではない
  • Long型がひっくり返って6兆円?
  • パケットが増えたバグ

    • 謎のパケットのバーストが発生する
    • 原因は冷蔵庫のインバーターによるノイズ
      • 夜テストしても落ちない
      • 某メーカーのミッションクリティカルチームが発見した
  • ハードウェアのエラーはソフトウェアに変な形ででる

  • Phantom Deadlock

    • delayのdeadlock
    • 哲学者の食事のやつ
  • ほとんどのWFGの長さは2

For typical appIications, over 90% of WFG cycles are of length two.

90%の数値は計算できる?みたいな話になった。 ここは何を言っているのかさっぱり

  • livelock
  • cyclic restart

  • タイムスタンプをつけてpriorityにしてlivelockを防ぐ

  • wait-die

Wait-Die: if ts( T,) < ts( Ti) then T, waits else abort T,. Wound-Wait: if ts( T,) < ts( T,) then abort Tj else T, waits.

  • どっちがいいか度々議論になる
  • wait-die一択?またはno wait(ここにはない)
  • no waitが潔い。最近はno wait一択

    • たまにwait-dieの実装がある
    • Wound-Waitの実装は見たことがない
  • タイムスタンプはいずれにせよいる

    • idでも良い
  • タイムスタンプはよくカウントアップする

    • 5分に一回くらい
    • 64bitにすれば良い
      • 32bit cpuの時は
    • カウントアップに対応した理論の論文がある
  • 論文の査読の話題になりました。(流派で暗黙の前提があるなど)

次回は「3.12 LOCKING PERFORMANCE”」から!

*1:たまに評価されることもありますが、流石に倍以上のエンジニア歴を持っていて、本当のテクニカルな分野でシビアなものが必要とされる世界で生き残ってきた人には到底叶いません

プログラマのためのSQL 読書会(25)に参加

28.4の計算列によるグルーピングからを読みました

  • MySQLではGroup By句にASC,DSCが書ける(一同驚き)
  • 安定ソート、非安定ソートは具体例がないとわかりにくい
    • Order Byで指定すると安定ソートになる。
      • Group By句では非安定ソート、安定ソートが保証されない
        • InnoDBでは主キーに関して、安定ソートの動きになる
  • Order By句に列番号を各機能は20年以上前から非推奨?廃止予定らしい。
    • その場限りのクエリを描くときは便利だから使う。
  • PostgreSQL,MySQLはorder by句に0がかけない。

    • MySQLは0を指定するとエラー。-1を指定すると変な動きになる
    • 正の整数以外を指定すると、基本的におかしな動きになる。
  • 集約関数にDISTINCTが書ける。

    • 数年前にこれを知って驚きました。知らない人はぜひ試して見ましょう。

qiita.com

SELECT COUNT(DISTINCT shohin_bunrui)
  FROM Shohin;
  • デフォルトでALLが使われている。(明示することもできるが結果は変わらないので、意味はない)
  • countのなかにdistinct複数名が書けるか?

    • 検証したところ、かけない
  • count以外の集約関数は引数がNULLの場合は空集合を返す

    • countのみ0を返す。
  • 小数点
  • 近似値について話題になりました。 https://qiita.com/y-ken/items/b8601092d0261c3a556d http://d.hatena.ne.jp/hnw/20171221

  • 計算誤差をきにするならdouble,floatではなくdecimal型を使うべき。

  • 中央値をだす関数は標準SQLではない

  • そういえば統計詐欺といえば日本人の貯蓄ですよね。。。という話から以下のリンクが話題に上った。

  • SQL ServerのTOPは色々指定できる。

    • 上級者の方でも引数に与えられるのは数値しか知らなかったらしい。
  • 難解なSQLの出典はなんなのか?

次は29.4.3 複数の基準の極致関数から!

分散処理本第37回に参加

Distributed Computing: Principles, Algorithms, and Systems

Distributed Computing: Principles, Algorithms, and Systems

本書の概要

CHAPTER 10 Deadlock detection in distributed systems

から

10.5  Knapp’s classification of distributed deadlock detection algorithms

までを読みました

  • 広く浅く学べる大学院の教科書的な本らしいです。
  • これを手引きに論文を引いて実装するのが良い。
  • 余談ですが、読書会の参加人数は対数線形で減っていくらしい

感想

「待ちグラフ」って名前はDBスペシャリスト試験などで何度も出てくるから知っていますが、 具体的にどのようなものか知りませんでした。DBの実装経験があるような方々もおられたため、実際に実装するならこういうアルゴリズムでやる、など会話をしているうちに、机上の理論だったものが実際のイメージに落ちていく感覚がとても面白かったです。

研究職やミドルウェアを作っておられるような本物の技術者のような方が中心なので、レベルの差を感じるものがありました。強い

勉強会ノート

雑でまとめきれていませんが。一応書いたのでブログに乗せます。

  • 分散システムではデットロックの検出は基本的にできない、という結論になりそう?

There is only one copy of each resource.

  • それぞれのコピーは一個しかない、的な慣用句

Wait-for graph

  • 待ちグラフの実装は面倒
  • グラフ処理は行列処理でやる。
  • 待ちグラフは効率が悪い

  • deadlockの対処

    • prevention:非効率
    • avoidance:(分散システムでは)実用的ではない
    • detection:これが良い(本書ではここを掘り下げる)

This is the main reason why many deadlock detection algorithms reported in the literature are incorrect.

  • とあるが、分散システム界隈では論文記載のデッドロック検出アルゴリズムが不完全というのは一般的な認識なのか?
  • 論文が不完全というのは常識ではない。
  • 想定が甘い、というのはある。

  • 10.4.2のANDモデルの例が適切ではない?

    • デッドロックを起こしているものがAndモデルの例として適切でない(複数のリソースを要求しておらずどのプロセスも一つの要求しか出していない)
  • Orモデルの場合のデッドロックの検出条件

    • 1.Each of the processes in the set S is blocked.
    • 2.The dependent set for each process in S is a subset of S.
    • 3.No grant message is in transit between any two processes in set S.
  • knotの定義がわからない?

    • 分散システムでも一般的な概念ではない
    • サイクルとの違いがわからない
      • サイクルよりも強い
        • サイクルとの違いは

In the OR model, the presence of a knot indicates a deadlock [19]. In a WFG, a vertex v is in a knot if for all u :: u is reachable from v: v is reachable from u. No paths originating from a knot shall have dead ends.

  • 参加者の方が論拠の論文をサクッと読んで定義を説明まとめてくれました(すごい)

  • この例だと(緑色の遷移はないものとして)

    • {c,d}はknot
    • {a,b},{c,d}はcycle
  • こちらのサイトがわかりやすい

http://cse.csusb.edu/tongyu/courses/cs660/notes/deadlock.php

  • そのノード以外に到達できないような集合がknotでは?

  • ノットにしか到達できないグラフはORモデルのデッドロック

  • 現段階ではデータベースは全てシングルリソースべき

    • ORモデル ANDモデルはない?
  • マルチマスターでのデッドロックの検知はそんなに優しくない

  • 従来の検知手法が使えない
  • knot以外へのリソース要求がないことを証明しないといけない。
  • タイムアウトするしかないのでは?
  • AWSはマルチマスターをやっているらしい(無謀?超絶技術?)
  • pqモデル?
    • 定義だけで証明はまだ示されていないが最強?
  • knotはグラフ理論では既約多項式とか言った気がする

既約多項式 - Wikipedia

  • 最後にknotの定義を調べてやろう、ということで神林さんが別室から鈍器のようなグラフ理論の本を持ってきた。

Java読書会「現場で役立つシステム設計の原則」を読む会 第1回に参加

本の概要

投票の結果こちらの本になりました。 ペース的に4回くらいになりそうです。 前回の投票でもKotlinの本に続き2位だったそうです。かなり人気な本だと思います。

正誤表:

サポートページ:現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法:|技術評論社

賛否

賛否のある本だと思いますが、思っていたより批判が激しい感じでした。

というのも「現場で役立つシステム設計の原則」というタイトルですが、現場として次のものが暗黙的に想定されていると思います

  • WEBシステムである
  • モダンなJavaでSpringのようなフレームワークが使える
  • バックエンドはRDBを使う
  • 国内向けのシステムで内製ができる(アジャイルが可能な現場)
    • 超大規模ではなく、国際化対応などはしなくて良い

著者が想定しきれていないほど多様な「現場」の方が参加されているので、業界が違う技術者に「原則」として著者の経験や考えが読み上げられてしまうと異論が湧き出るだろうなという印象です。

全体的にこのアマゾンレビューのスタンスな方が多い印象です。

この業界に溢れた、「オレのやり方がナンバーワンだ」の本の域からは出ていないように思います。「変更が容易」の判断基準や根拠が極めて定性的なまま、著者の考えにそぐわない既存の設計手法を「誤り」と断じる論調にはだんだん首をかしげてしまいました。 著者は『ドメインモデルのコードの中に、すべてのビジネスルールを凝集させてしまいたい』派なのだと思います。オブジェクトを第一に考えている、という点で確かにオブジェクト指向には違いないです。が、DBやUIとのミスマッチをどう埋めるか?という話に関しては論理に雑な印象を受けました。ストアドやリッチクライアントなど、ビジネスロジックを散在させる要素はそこかしこにあるわけで、「あくまでドメインモデルのコード内に閉じ込めるのが正しく、そうできないのはやりかたが悪いから」なスタンスは、現実から目をそらした排他主義に見えます。著者が「正しい」とする方法が、果たして本当にどこまで経済的にメリットのあるやり方なのかは疑問に感じました。 本書は「こういう考え方もあるのか」の視点で読める人には良書と思います。しかしながら、初心者が最初に読むには全くお勧めしません。著者の考えに強く賛同する人であったとしても、この本に「正しい」と書かれていることを妄信すべきではない、と思いました。

https://www.amazon.co.jp/gp/customer-reviews/R1SZREL1NO1RXE/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASIN=B073GSDBGT

一方的に批判がでる箇所もあり、主催の高橋さんがバランスを取って何度かフォローされていますが、DDD界隈の方が参加されていい感じの反論が聞けたら面白いのになと思っています。

感想

オブジェクト指向Javaを極めるとDDDに行き着くと思っていましたが、そうでも無いようです。実装が好きな人、モデルが好きな人、ドメインが好きな人、という感じに(もっと多いかも)枝分かれするようです。

ベテランの方が多いので、全員「ドメイン駆動設計」のファンだと思い込んで参加したのですが、そういう方がいなかったので衝撃的でした。*1

私自身はDDDの「プログラミングの対象領域を大事にする」という考え方は結構好きです。設計はあまりわかっていないので、勉強になるところも多かったです。

以下読書会ノートです。

オブジェクト指向とは何か

設計とプログラムどっちのことを言っているのかわからない

モデルとプログラムは一致するか?

  • 最近のシステム開発は紙ベースの業務を置き換えるだけではない
  • まだ世の中にないビジネス。どうやってITで作る?

文字数制限の話

文字数たくさん使ってわかりやすい単語で作れ的な本だが、 長い命名を使うことにも問題がある

ShipingCostの設計について

class ShippingCost {
    static final int minimumForFree = 3000;
    static final int cost = 500;
    int basePrice ;

    //メソッドの実装略
    ShippingCost(int basePrice);
    int amount();
}

int shippingCost(int basePrice) {
    ShippingCost cost = new ShippingCost(basePrice);
    return cost.amount() ;
}
  • 重複をなくすだけならstatic methodにくくり出すだけで良い
  • このクラス設計はいけてない。最近だとロジックとしてラムダ式とかを渡す?

計算の対象になる値が異なれば、別のオブジェクトに分けるのがオブジェクト指向らしい設計です

  • この意見は局面による
  • 直接newしたものを使うべきではない。(interfaceを使う例が後に出てくる)
    • 結果を知りたい側(amountが欲しい呼び出し側)がbasePriceを知らない場合は、こんな設計もあり
  • ライフサイクルが長いから送料クラスが意味を持つ
  • 好意的に解釈するとプライムな変数ではなく、独自定義型を使うというのはOK
  • intは使うな
    • int型の最大値は21億だが、その規模の額を扱わないシステムでもカードなどで端数の扱いの違いがあるのでintは危険
  • 国境を超えるシステムでは送料計算はめちゃくちゃ大変。
  • エリックエヴァンスの本に貨物を輸送する例が出てくる
    • DDD界隈の人達は貨物を輸送する例を好む傾向がある
  • 著者はDDD界隈の人
    • このへんからドメイン駆動設計への誘いを感じる。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

業務構造とプログラムを一致させる

業務の構造とプログラムの構造が一致していれば

int itemPrice = (basePrice + shippingCost) * taxRate();
  • 例えば上記は軽減税率などが入ると、税金計算の際に、料金だけでなく、明細が必要になってくる
    • そうなると破綻する
  • 一致させるのは大変難しい

DBとタイムゾーン

ロケールを明記していないと、タイムゾーン情報が消える(必ずUTCになるわけでない) この辺はサマータイム対応が難しい、ということになる。

可視性について

class Customers {
    List<Customer> customers;

    ...

    List<Customer> getList() {
        return customers;
    }
}
  • List<Customer> customers;はprivateスコープにすべきでは?とはという話が出た
  • DDDをする上で可視性などは些細な問題なのでは?

Listへの参照をそのまま外部に渡すと、要素の追加や削除がCustomersクラスの外部からできてしまいます。`

  • だとしてもこのような前提があるならば、可視性はこだわってサンプルコードを作成するべき
  • 可能な限りpackageスコープにする。という記述がある。その方針に沿っているのでは?

“関連性の強いクラスは同じパッケージに集めます。そして、クラスやメソッドのスコープ(参照範囲)は、可能な限りパッケージスコープにします。”

  • デフォルトで可視性をpublicにする風潮は如何なものかと思う*2
  • Java9で入ったモジュールは難しい
    • それだけを扱っている本が3冊くらい出ている

この著者は防衛プログラミング反対派

まだこの章に到達していませんが、懇親会などで結構話題に上がりました。

Javaの互換性

コレクションオブジェクト

class Customers {
    List<Customer> customers;

    ...

    Customers add(Customer customer) {
        List<Customer> result = new ArrayList<>(customers);
        result.add(customer);
        return new Customers(result);
        //return new Customers(result.add(customer));👈誤植間違い

    }
}
  • コピー先のCustomersでも参照先のcustomerオブジェクトは変更できるがいいのか?(シャローコピー、ディープコピーの話)
  • ここまでの文脈的にCustomerはimmutableなオブジェクトなのでは?
  • 少し後ろに説明が書いてある

“unmodifiableなListでも、個々の要素のオブジェクトの内容は変更できます。コレクションの要素を値オブジェクトにすればこの変更も防げます。”

  • コンストラクタの説明がない
  • list行間の下の...に書かれているのでは

Listオブジェクトのファクトリメソッドについて

  • List.of() 変更不可能なクラスを返す。可変長引数のみ。コレクションを引数を取ることはできない
  • Arrays.aslist() 固定サイズのlistを返す
  • Collections.unmodifiableList 変更不可能なクラスを返す
  • Collectors.toList() ストリームとかで使う?

技術者の人種

  • モデル屋さん

    • モデルを作るのが目標
    • 自動生成とかやりたがる
    • 別の設計関係の本の読書会で意見が合わないことがあった
  • ドメイン屋さん

  • 実装屋さん
    • この読書会の大半の人?

https://ja.wikipedia.org/wiki/モデル駆動型アーキテクチャ

  • プログラマ不要説は度々流行るが、結局流行らない
    • if文やfor文を違う表現で出してるだけ。
    • caseツール

※モデル屋さんとドメイン屋さんが来ておらず、偏った議論になっているのでは?と感じた。

インスタンスを返すメソッドの意味は?

以下のコード。なんの意味があるのか?という質問が出た

class Customers {
    List<Customer> customers;

    ...
    //👇これ
    Customers add(Customer customer) {
        List<Customer> result = new ArrayList<>(customers);
        return new Customers(result.add(customer));
    }
}
  • メソッドチェーンができる
  • 標準ライブラリのimmutableなオブジェクトも大体この仕様

サブストリングにおけるStringPoolの扱い

最近はやってない

Strng.Classのソースを確認したところ、確かに

  • substring(int beginIndex, int endIndex)
  • StringLatin1.newString(value, beginIndex, subLen)
    • new String(Arrays.copyOfRange(val, index, index + len), LATIN1)

こんな感じで新たなインスタンスが作られるようになっていました。

public String substring(int beginIndex, int endIndex) {
    int length = length();
    checkBoundsBeginEnd(beginIndex, endIndex, length);
    int subLen = endIndex - beginIndex;
    if (beginIndex == 0 && endIndex == length) {
        return this;
    }
    return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                      : StringUTF16.newString(value, beginIndex, subLen);
}
  • サブストリング側の生で親がGCされずに残る負荷が多い。
    • 親が消えても子供が残っているから親が消えない。(GC対象にならない)

なぜラッパークラスを使っているのか

  • Booleanではなくbooleanを使うべき
private Boolean isChild() {
    return customerType.equals("child");
}
  • Object.equalsはboolean型を返すが?
    • 無駄なBooleanクラスを使ってる
  • Booleanを使っている意図がわからない
  • 執筆にワードを使って書いてる説
  • customerTypeの値はメソッド内に持たなくて良いのか?
    • ちょっと変えてストラテジーパターンに落ち着くいつものパターンでは?

Reservationクラスは不変クラスにしなくていいのか

  • addFeeでオブジェクトの状態を変えられるけどそれはいいのか?
class Reservation {
    List<Fee> fees; //大人と子供の内訳は不明

    Reservation() {
        fees = new ArrayList<Fee>()
    }

    void addFee(Fee fee) { //大人でも子供でも追加できる
        fees.add(fee);
    }

    Yen feeTotal() {
        Yen total = new Yen() // 合計ゼロ円
        for( Fee each : fees ) {
            total.add( each.yen() );
        }
        return total;
    }
}
  • 現実的には徹底的にやる必要はない。どこかで線引きが必要
  • 徹底的にやると目指せhaskellになってしまう。

enum

  • enumは積極的に進めるべき
  • enumにメソッドを定義できるのはJavaの良い点
  • enumのメンバは大文字にすべきだが?
    • 小文字でMapにアクセスしたいので発想はわかる
enum FeeType {
    adult( new AdultFee() ),
    child( new ChildFee() ),
    senior( new SeniorFee() );

    private  Fee fee;
    // Feeインターフェースを実装したどれかのクラスのオブジェクト

    private FeeType(Fee fee) {
        this.fee = fee; // 料金区分ごとのオブジェクトを設定する
    }

    Yen yen() {
        return fee.yen();
    }

    String label() {
        return fee.label();
    }
}
  • enumをDBで管理したいという要望があると、このような設計はできない

型引数

Set nextStates = EnumSet.of(承認済,差し戻し中);
                                 // 状態のグルーピング
  • Setの宣言時に型引数を使うべき

日本語の定義名

  • Enumの定義名が日本語でわかりやすくて良い
enum State {
    審査中,
    承認済,
    実施中,
    終了,
    差し戻し中,
    中断中
}
  • Javaだとファイルにした時に問題が怒る
  • 「が」とかはmacOSでへんなバグりかたする
  • クラス名には使わない方が良い。
    • 日本語を使うのはメソッド名にまでに止めること

EnumMapを使うべきか?

class StateTransitions {
    Map<State, Set<State>> allowed;

    {
        allowed = new HashMap<>();

        allowed.put(審査中, EnumSet.of(承認済, 差し戻し中));
        allowed.put(差し戻し中, EnumSet.of(審査中, 終了));
        allowed.put(承認済, EnumSet.of(実施中, 終了));
        allowed.put(実施中, EnumSet.of(中断中, 終了));
        allowed.put(中断中, EnumSet.of(実施中, 終了));
    }

    boolean canTransit(State from, State to) {
        Set<State> allowedStates = allowed.get(from);
        return allowedStates.contains(to);
    }
}
  • HashMapではなく、EnumMapを使うべき
  • 「終了」をセットしていないので、終了で呼ぶとぬるぽで落ちる
    • バグでは?
    • EnumMapを使った場合もぬるぽが発生する
  • EnumMapを使うメリットは?
    • コンパイル時にKey側の要素数がわかるので、メモリ効率が良い
    • (パフォーマンス以外の)機能的な利点は特になさそう?

データクラスと機能クラス

UtilとかCommonな便利クラスを作るなという恒例のアレ

  • 悪い共通クラス
    • RXJavaが共通ライブラリの最たる例。
    • メソッドが多すぎる。それでも使うけど。

Javaのルーツ(ちょっとざわついた)

Javaは、C言語からの移行しやすさを重視して設計されました。

オブジェクト指向設計の基本

ロジックの置き場所やクラス名/メソッド名の改善を続け、より良い設計を見つけていくのが、オブジェクト指向設計の基本です。

凝集度

“このように、関連性の強いデータとロジックだけを集めたクラスを凝集度が高いと言います。”

  • モジュール設計のことを書いて凝集度を説明するのは、少し説明不足な気がする
    • そもそも凝集度なんかは本を読んでもよくわからない。
      • 実際に手を動かして作ってみるべき。

データと処理を同じクラスに置く(分離させない)

“データとロジックを一体にして業務ロジックを整理する”

  • この話はWEBアプリが流行り始めた頃からかなり議論されている
  • lombokhibernateのが許容できない感じの思想
    • hibernateのデータクラスもロジックをぶら下げることは可能

時間の流れ?

時間軸に沿った業務の基本の流れを軸に、業務ロジックの関係を整理します。

  • この手法は初めて聞いた
    • 注文と出荷は非同期。同期する必要はない(溜め込めばいい)

パッケージ間の参照

図3-3

出荷する時に顧客は見ないのか?

  • has a関係のようなものでは?
  • クラス単位の参照関係のようなものでは?
  • パッケージの関連では?

  • モジュラー屋さんはよくこのようなUMLを書く

    • 矢印がないからと言って、本当に参照しないという意味ではないのではないか。

技術選定などのフェーズが抜けている

  • Spring,Javaありきで技術選定などのフェーズが抜けている

    • グランドデザインができてしまっている
    • どうしてもWEBアプリケーションで成り立つ理論になる
      • 大企業の調達システムなどでは通用しない考え方
  • 動き(設計)を想定しながら分析する

    • 最初に習ったオブジェクト思考設計は確かにこんな感じ
  • 分析と設計と実装を一人の人間がやるのは小規模でないと難しい

  • この本は実装者に結構寄り添っている

  • モジュラー屋さんほどコードから離れていない
    • モジュラー屋さんはプログラミング実現性みたいなものを全く考えない。
  • ものごとは綺麗なツリーにはならないので、多重継承*3の形で分析することもある

次回Chapter 4の“なぜドメインモデルだと複雑な業務ロジックを整理しやすいのか”から

*1:いたらすみません

*2:この本の話では無い

*3:Javaではプログラミングで表現できない