文章を見直す、って何を見直せばいいのかまとめてみた

同僚の資料を添削する機会があって、添削と議論の中でわかったことをまとめておく。

NOTE: ここで資料とは、要件を議論するたたき台資料とか設計の選択肢の検討資料を指す。

正直自分も同僚と同じ年代の頃に「これロジカルじゃないよ」とか言われても、いや分からんなぁ...と思ってたし、その時にこんなことを分かってればもう少しうまいことやれたのかなぁ、と思いつつ。

自分が書いた文章を例文として拝借されることを快諾してくれた同僚氏、ありがとう。

資料を構成する要素を一つずつチェックする

資料を構成する一番小さな要素から順にチェックしていく。

資料は

  • 単語
  • 段落

が集まってできている。 チェックの観点は、これら一つ一つが日本語として正しいことと論理的に正しいこと。

単語

PCで書くなら関係ないけど、存在しない日本語を書いていないか調べる。 論理構造はないので調べない。

同音異義語を使い間違えていないか調べる。

破戒的変更を許容します。

日本語として「てにをは」が破綻していないか調べる。

この処理には外部アクセスに必要ない。
    -> 外部アクセス「は or が or ...」

主語と述語だけを読む。それだけでも主張がズレないかを調べる。

自分の意見は、S3にあるファイルをサーバーの特定の箇所に置くという処理です。
    -> 主語と述語だけを抜き取ると「意見は、処理です」となり、文章の意味が通らない。

ズレる場合、修正する。文を短く切るのが有効な場合があるので、検討する。

段落

ここらへんから日本語として正しいかを調べる必要がなくなってくるので、ある段落が論理構造的に正しいかを調べる。 あと、正直好みもある気がするからなんとアドバイスしてよいか悩ましい...。

段落の結論を述べている一文を探す。ない場合、作る。ある場合、それが1、2文目に入っていなければ移す。 2文目でも良い理由は、たとえば問いかけから始まったり、前の段落を受けての1文で始まったりする場合が考えられるから。 (というか本来は何文目でもいいはずだが、慣れないうちから変な場所に結論を書くのは筋が悪いと思う。)

次に、段落に見出しがある場合は、見出しだけを読んでみる。見出しだけを読んでその段落の主張が分かるか調べる。 わからない場合、わかるように書き直す。

## 自分の意見
自分の意見は、今回はS3のPUTイベントを使って処理を行うべきだと考えています。
==========================
## S3のPUTイベント通知を使うべき
自分の意見は...

文と文のつながりが破綻していないか調べる。 具体的には、ある文の主張と次の文の主張を比較して、その関係を表すのに適切な接続詞を使ったり使わなかったりする。

案としては、S3のPUTイベントを使うことが考えられます。さらに、バッチ処理で実装することも考えられます。
    -> 2つの文は内容は案の列挙。しかし接続詞の「さらに」は前の文に詳しい説明を追加する時に使う。よって不適。

また、段落の主張と、段落に含まれる文リストを比較して十分な論拠を示せているか調べる。 具体的には、今まで出会った中で一番意地悪な質問をする人を脳内でエミュレートしてその人にツッコませる。 ロジックツリーを書いても良いけど、どちらにせよセルフツッコみは必要。

文章

段落に対して行ったチェックを文章に対しても行う。

文章の結論を述べている一文を探す。ない場合、作る。ある場合、それが最初に書いていなければ移す。

文章には普通見出しがあるので、なければ作る。ある場合、見出しだけを読んでみる。見出しだけを読んでその文章の主張が分かるか調べる。 あるいは文章の見出しなら、主張ではなく目的がわかるだけでも良い気がする。 たとえば、以下の見出しはどちらでも良い気がする。

# ○○処理の自動化の実装方針を決める
==========================
# ○○処理の自動化をS3のPUTイベント通知をつかって実装したい

段落と段落のつながりが破綻していないか調べる。 こちらは段落内の文のチェックと違って、前後のつながりというよりは論理構成が正しいかを確認する。 具体的には、まず段落の見出しだけを読む。それを読んだだけで大筋は納得できるか調べる。

まとめ

まとめてみた。 文章の論理構造が正しいかのチェック方法を、まだうまいこと明文化できない。自分でもうまいことできてないんだろうな。

継承か保有か

public class Price {
    private Long price;

    public Price(Long price) {
        this.price = price;
    };

    public add(Price another) {
        return new Price(this.price + another.price);
    }
}

みたいな Price クラスがあった時に、

public class Yen {
    private Price price;
    public Yen(Price price) {
        this.price = price;
    };

    public Yen add(yen) {
        return new Yen(this.price.add(yen.price));
    };
}

//...

Yen yen = new Yen(new Price(10));
yen.add(anotherYen);

を定義するのか

public class Yen extends Price {
    // ...
}

// ...

を定義するか。

最初に考えたのは、「それぞれここがしんどいなー」だった

ここがしんどい継承

継承だと、子クラスのインスタンスを使う側は親クラスのドメインを知らないと結局どんなメッセージを打てるのかわからない。 複雑なアプリケーションならより一層、親子関係にあるすべてのクラスのドメインが果たす責務に詳しくならなければ...。

ここがしんどい保有

こっちがわりと有用なのではないかと思っていた。 親クラスのデフォルト実装を公開するのに、毎回関数を定義しなければいけないけど、そのかわり親クラスの振る舞いを一部しか知らなくて良い。

そもそも役割が違うのでは?

委譲か継承か、それが問題だ – Where is the mate to the Sock ?

やっぱりそうらしい。 ていうか社内の情報共有ツールで他の人が書いた日報のツッコミでこのブログを見つけた。弊社良いな。

いや、そもそも設計思想が違うのか??

Priceを継承したYenは、通貨単位の「円」はPriceの振る舞いを拡張したものだというドメインの理解を主張している。 一方でPriceを保有するYenは、通貨単位の「円」はPriceを自身の属性の一部だと主張している。

どっちがいいとかではなく、ドメインの理解が違うんだろうな。

Clojure/conj 2017 上映会 #1 に参加してきた

NOTE ただの参加ログです。雑な情報の再生産なのでちゃんとしたやつとか本家を読んでください。

clj-nakano.connpass.com

に参加してきた。 開催者様、会場の準備、ピザとコーラ、そしてそもそも開催自体していただいたこと、ありがとうございました! みたことある人がチラホラいる感じだった。

Rich Hickeyの話をちょっと再生しては訳が入る形式だったので、正直言ってRich Hickeyが何言ってるかは5%くらいしかわからなかったけど話についていけた。

Rich Hickeyについて

Rich Hickey、突然現代に現れた未来人だと思ってたけど大学卒業後18年間ふつうに(?)開発者やってた。 Rich Hickeyは 「Situated」なプログラムをイメージしている。ビジネス要件で簡単に自分が心血を注いだアルゴリズムが無意味になるのはよくあること。

現実世界は対応を迫られる不整合に満ちている

現実のアプリケーションでは純粋なアルゴリズムはほんのすこしで、残りはほとんど情報処理用のプログラムである。 しかもその純粋なアルゴリズムも、ビジネス要件で簡単に翻る。 情報処理というのは、たとえば外部ライブラリを使用したり、DBと通信したり、別システムと連携したりすることを指す。

これに時間軸を加えれば、システムが対応しなければいけない変化には「ライブラリがメンテされなくなった」「DB製品が変わる」「システムのI/Fが変わる」「要件が変わる」ことも含まれると分かる。

RichはORMでRDBMSプロトコルをラップしてあたかもプログラミング言語の世界のプロトコルであるかのように使えるのは努力の方向性が違うと言っている。 んー、Datomicならいいのか?どういうORMを想定しているんだろう。

現実の複雑さ

talk-transcripts/00.29.49.png at master · matthiasn/talk-transcripts · GitHub

上の図がわかりやすい。

Rich Hickeyは上の図で型を間違えることを些細な問題と表現しているが... Clojureを触っていてもMapがくるかListがくるかそれとは別の何かが来るか気にしなければいけない。 とはいえそれは瑣末な問題で、プログラミング言語のレイヤーで解決しないことにした、ということかな。 今仕事でRubyを触っているけど、Rubyはメッセージ中心だから型が何かは気にしない、という思想があるのに対してClojureはそもそもそんなところ瑣末な問題だから気にしないわ、と切り捨てているのが、設計思想の違いを感じて面白いと思う。

Pythonとかの他の動的型付け言語はどんな思想で作られてるんだろう。

Immutabilityの背景

RichはPLOP(PLace Oriented Programming)という言葉を出している。 PLOPはイミュータビリティと相反するプログラミング手法で、メモリやディスクのアドレスと結合したプログラミング。 しかしそれではイミュータブルにできない。メモリのアドレスの中身に入ってる情報は刻一刻と変わる。

Clojureを支えるイミュータブルなシーケンスが以下らしい。

clojure/PersistentHashMap.java at master · clojure/clojure · GitHub

全然知らなかった。

情報は変化し続ける

静的型付け言語では、情報を操作するのがめんどくさすぎる。 クラスはデータのコンテナが意味を持ってしまったものだが、これを使ってEffectiveなプログラムを書くには情報を一つ一つクラス化しなければいけない。 具象を必要以上に持ち込んでしまったのが問題だ。

うーん...。 ドメインが多くて複雑なら、その分だけ扱う意味が増えてクラスが増えるのは自然なことだと認識してたけど...。 ていうか意味のないデータ構造だけでは、結局プログラマーは巨大なシーケンスのネストしたハッシュマップを指差して「ここにはユーザーのデータを変換したものが入ってるんだよ」とか話すことになる気がする。

巨大なシーケンスだとしても、その連想配列のキーに適切な名前をつければいいじゃない、ってことか、な?

Parochialismへの批判

ある異なるドメイン領域でユーザーをなんと呼ぶかが一致しないことは問題だという。 それは当たり前では...?

いや、そういうことじゃないか。 あるデータが持つ意味をクラスとして表現するのではなく、データのキーとすればいいでしょ、という話か。

結局ネットワーク越しに他のシステムに届くのはクラスではなくコレクションオブジェクト。 じゃあ自分のソースコード内でもそれと同じように書けばいいでしょって話か。

まとまらない... 読み直さなきゃ... Rich Hickeyが何言ってるかわからないから反論もできないのが悔しすぎる。

参考

Rich Hickeyのプレゼンのスクリプトgithub.com 今回の勉強会で使われた、上記プレゼンテーションの訳。 https://gitpitch.com/k2n/effective-programs-ja#/

Node.jsの抽象構文木を操作してソースコードを生成する

ソースコード生成してみたい!けど、そういや1年くらい前にASTって言葉を知ってから何もしてなかったな」、と思ってASTを書いてそいつを食わせてソースを生成してみた。

もともとはさいきょうのスタブサーバーを作りたいねって話から始まったからそういう感じのリポジトリ名の以下リポジトリに実際に今夜書いたコードが乗っかってる。

github.com

構文木ってなに?

プログラムの構造を、(Javascriptの場合)JSON形式にパースしたもの。 何段階かに分かれているっぽい。

詳しくは以下のブログエントリーで。詳しい記事ありがとうございます。

efcl.info

どうやってASTを生成するの?

ASTの仕様を知ってそれを満たすJSONを構築すれば、それをASTからソースに変換するライブラリに食わせればコード生成できる。 ソースコード ⇔ ASTのライブラリはJS界にいくつかあるみたいで、今回はEsprimaを使うことにした。 Babelもトランスパイラー?だから同じような機構を持ってるみたいだけど、特に理由なくEsprimaにした。

JavascriptのASTの構造はestreeという標準があるらしい。

github.com

けど、それを読むより実際にソースコードから生成されるASTを見たほうが早いなってことで以下で試す。

http://esprima.org/demo/parse.html

こいつに

let express = require('express');

みたいなのを食わせると

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "express"
                    },
                    "init": {
                        "type": "CallExpression",
                        "callee": {
                            "type": "Identifier",
                            "name": "require"
                        },
                        "arguments": [
                            {
                                "type": "Literal",
                                "value": "express",
                                "raw": "'express'"
                            }
                        ]
                    }
                }
            ],
            "kind": "let"
        }
    ],
    "sourceType": "script"
}

みたいな木構造にバラしてくれる。 逆にこいつを、対応したコード生成機に食わせればソースコードが生成されるってわけよ。

github.com

これを使えば、カッコとかカンマとかシングルクォートとか改行とか気にせずに、オブジェクトをガツガツ組み立てていけば思うままのソースを生成できる。

これを世に出してGithubのStarを稼ぐんだ、という野望を胸に書くコードは、心なしかいつもよりギラギラして見えた。

3人チームで1日モブプロしたら、これはすごく圧縮された先行投資だとわかった

最近ペアプロの威力を実感する出来事があった。 2人の知識レベルが高まり合い、自分事感のあるソースコードが積み重ねられていくのを体験した。

そこで次はモブプロもやってみたいなぁと思っていたが、仕事で無理に導入しても自分事感の薄い対象を直す会になっちゃうしなぁ... と思っていた。 そんな今日このごろ、今のプロジェクトチームの3人のエンジニアが全員使わなきゃいけない、そこそこゴツいクラスがまだ存在しないということに気づき、3人で設計することに。

ここぞ、ということで最初2人で、その後すぐに3人で、ほぼ1日中ドライバーを交代しながらソースコードを書いてみた。 そんな1日の記録。

ルール

  • 3人でやる。
  • 他プロジェクトのチームメンバーにも声をかけて、よかったら見に来てねというオープン型。
  • 15分でドライバーを交代する。
  • 大きなディスプレイの横にホワイトボードも置いておき、必要ならそれを使って議論する。

やってみたら楽しい〜〜〜!でもめちゃめちゃ疲れる!

とにかくずっと考え続けることになる。 たとえナビゲーターだったとしても全く楽ではない。なんならむしろしんどい。 ナビゲーターは、ドライバーが進むべき道を考えて、他のチームメンバーが気分を害さない言葉で、論理的に自分の意図を説明しなければいけない。 他のメンバーが喋った内容を自分の考えと論理的に突き合わせて、より良い選択肢を選ばなければいけない。

今日のモブプロの間は必死過ぎて、メールもチャットも、ましてやTwitterも全く見なかった。 なんならトイレもちょっと我慢してた。 それくらい集中してたから、普段一人で黙々とやってる仕事を数時間に圧縮したような時間だった。

それは他のメンバーもそうだったみたいで、1、2時間経った時点で休憩をとったら全員ぐったりしてた。

うまくいったのか?

いったと思う。 1人で考えるには複雑すぎるし、そもそも3人ともが必要な機能なので全員で認識をあわせながらすすめておけば、今後それぞれが機能開発をする時に全く意図のブレがない状態でコードを書けるはず。そういう先行投資になったと思う。 ただ、1日の終わりにスタンディングKPTをやった感じ、今後すべてをモブプロでやる必要はなさそうだね、という話になっている。 つまり設計みたいなアウトラインを決める大事なところはみんなでやって、要求が明確だけど実装が難しいところはペアプロで、単に書けば良いだけのところは1人でやると良さそう、という話だ。

たしかにそうだと思う。

今後直した方が良いところは?

15分は短かったわ。 あとは、方針を見失うと全員が路頭に迷うから、「今はこのクラスのこの関数のI/Oを議論する時間です」とか「今はテストでI/Oを表現する時間です」とかの、「今何の時間だっけ?」を決して忘れてはいけないと思った。 そしてその「今この時間」を定義するのがカンバンなのであった。 終わってから「カンバンのチケット用意しませんか?」って話になって用意したら、実際何をやればいいか明確に定義できた。 良い。

「ドメイン駆動設計のためのオブジェクト指向プログラミングハンズオン」第2回に参加した結果、増田さんからPRにコメントをもらえた

f:id:blackawa:20171027181710p:plain

お忙しいところ、増田さんのご厚意に甘えて図々しくPRを見てもらえないかお願いしたら、ちゃんとコメントをもらえた。 勉強になった。

対象のプルリクは以下。

github.com

あらかじめ、書いていて疑問に思った箇所をこちらで注記してあって、それに対する返答という形でコメントをいただけた。

ドメインオブジェクトがことごとく toString() を実装することになる(がそれでいいのか...?)

良い。

自身を文字列表現するために、toString()を持つことが多い

たしかに、必要ないけどとりあえず用意したものは一つもなかった。 今まであんまり要らなかったのは、賢いオブジェクトを作り込むような設計ができていなかったからなのかな。

assertってアリなのか?

アリ。

メッセージは今回、

assert price.compareTo(BigDecimal.ZERO) >= 0 : "price is too small.";

みたいなものにしていたけど、本来はもう少し雄弁にした方が良いとのこと。少なくとも渡された値を表示はした方が良さそう。

計算結果ドメインオブジェクトを作って、そのコンストラクタに計算を任せる設計はアリなのか?

悩ましい。(が、ナシではないという意味だと解釈した)

今回のアプリケーションのクラス関係では、 Gross と GrossPerSubscriptionが、双方向に参照してしまっている。 GrossPerSubscription のコンストラクタには、Gross ではなく、Yen を渡すと解消できそうだとコメントいただいた。

ドメインオブジェクトのプロパティを文字列として返却する、ビュー用の関数が生える(のはアリか?)

一方、ドメインオブジェクトの設計が、(無意識に)ビューに引きずられがちになるという、ちょっといやな臭いを感じます。

たしかに!!!! 本当はもっとオブジェクト同士の関係を表す関数を生やしていきたかったが、うまいこと発想を膨らませられずビュー用のロジックばかり生やすことになってしまった。

DDD本、読む機運が今までになく高まっている。

「ドメイン駆動設計のためのオブジェクト指向プログラミングハンズオン」第2回でユーザー定義型を自分で作ってみた

ちょうど弊社内の同僚と輪読会をしていた書籍の著者であるところの増田さんがやるハンズオンがあると聞いて行ってきた。

第一回はこちら。動画で見てもなかなかおもしろかった。

youtu.be

アソビューについて

www.asoview.com の運営会社。

椅子がキャンプ用のやつで、会社特有感出てて面白い。

テーマは「型」

型とは、オブジェクトにおいて有効な値および演算を定義するもの = ストラウストラップ先生の受け売り。

ユーザー定義型を作って、モデルと実装を一致させるハンズオン

前説自体は、よくあるオブジェクト指向の概念の説明。 コードの臭いとか、リファクタリングの話とか。

ネタは「購入の平均単価」。 f:id:blackawa:20171018202021j:plain

このビューを表現するユーザー定義型を実装しよう。 以下は、やりながら感じたことの箇条書き。

自分がどんなロジックで生成されるかは、誰が知るべき?

売上高 / 購入量 

で導出される「平均単価」のインスタンス生成方法は、誰が知るべき? コンストラクタの引数か?

だとすると、

売上高.割る(購入量);

するための divide メソッドの中身は、ただのインスタンス生成になってしまう...。 その場では、「割り算処理とはAveragePriceを返すことである」と表現できるかなと思って、それで終わりにした。

とはいえ計算には基本データ型が必要になる

Price + Price

とは書けないので足し算を実装する必要があるが、そのためにはせっかくユーザー定義型を作ったのに基本データ型に変換する必要が出てきてしまう。 それではせっかくラップした基本データ型が外部に露出してしまって、隠蔽した意味がなくなってしまう。 結局内部のデータ型が変更されたら変更が広がってしまう...。

結局この疑問はハンズオン中に解消できなくて、最後に増田さんに質問しに行った。 そしたら

  • 「自分と同じクラスのインスタンスを引数に取るなら、引数インスタンスのprivateなプロパティにアクセスできるから、その性質を利用して計算する」
  • 「あるオブジェクトをIntegerやLongに直す関数は、Javaのお作法といえるくらい一般的な手法だから、そういうメソッドを生やしてしまう」

とのことだった。1つめの、同じクラス内でならprivateプロパティにアクセスできる、というのは今まで知らなかった! privateという言葉の直感には反する挙動だけど、基本データ型を露出しないというルールを実現するには筋の良い手段だと思った。

同じ机の人が全員違うドメインロジックを想像する、という事実自体が大事

クラス数が3個の人もいれば15個の人もいる。 しかもそれらは比較的小さなクラス。

すると、それぞれのメンバーがどれくらい違うドメインモデルを想像しているのかが明確になった。 増田さんいわく、それらはどれが正解ってわけではなくて、理解が異なっていることが分かることが大事だという。

たしかに、実プロジェクトでこれくらい小さなクラスとパッケージの集合を作れたら、仕様の認識合わせがすごく楽そうだ。

懇親会

懇親会にもお邪魔して、アソビューの社員の方に話を伺えた。 Java(SpringBoot)で増田さん流のDDDを実現しようとしている会社として、どんなことをしているのか気になったので質問してきた。

アソビューは最初からJavaだったのか?どのタイミングで事業の開発スピードとDDDを両立したのか?

最初からJavaだったけど、お金になるとわかった時点でDDD化を検討しはじめて、今粛々と書き直しているところ。 いうても巨大なシステムなので、まだまだ道半ばではあるが、すでにDDD化されたサブシステムの変更コストが軽くなっているのは実感している。

フロントとバックで共通のドメインが出てくるんじゃないかと思うが、共通モジュール化する?

個人ユーザー向けシステムと出展企業向けシステムで同じ言葉を使うとしても、実態は異なるドメインなので、ドメインは共通化させないで始めると思う。 もし、「これだけは確実に同じドメインでしょ」というようなものが出てきたら、その時に初めて共通化を検討する。