「現場で役立つシステム設計の原則」増田亨/技術評論社

Written by じび on 5月 29th, 2019

借りて読んだので、気になった箇所を簡単にまとめました。
基本的に Web アプリケーション向けの内容なのですが、随所に他のシステムでも役に立ちそうな Tips が入ってます。

第1章 小さくまとめて分かりやすくする

ValueObject

  • 電話番号なら String ではなく TelephoneNumber 型など業務専用の型を作って、長さなどの制限する。
  • 完全コンストラクタ(初期化時に値が決まり、以降変更できない。Immutable なもの)にすると、コードを読む時に値の変化を気にする必要がなくなる。
  • 金額と数量はどちらも Int で表せるが、それぞれ独自の型を作っておくと誤った代入をコンパイル時に検出できる。
  • typedef のような型を別名で定義するのと比べて、コンパイル時に上記のような型誤りの検出が可能。

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

  • List や Array のプロパティを1つだけ持ち、それの操作メソッドを持つ。
  • immutable にする
  • 操作の結果は、同じ型のコレクションオブジェクトとして返す。

第2章 場合分けのロジックを整理する

  • 早期リターンを使って if 文の else 節を排除すると、コードの見通しが良くなる。
  • インターフェイス宣言(Swift なら protocol)と区分ごとの専用クラスを組み合わせて、多態(Polymorphism)を実現する。
  • 区分オブジェクト:列挙型を使って業務で扱う区分の一覧を宣言する。

第3章 業務ロジックをわかりやすく整理する

  • ドメインモデルに業務データと業務ロジックを集める。
  • ドメインモデルは画面やデータベースの都合から独立させる。

第4章 ドメインモデルの考え方で設定する

  • 業務の関心ごとはヒト/モノ/コトで整理できる。
  • コトの整理を軸にする。
  • ドメインモデル設計のインプットは、業務の言葉の正しい理解。

第5章 アプリケーション機能を組み立てる

  • 業務的な判断、加工、計算の詳細はドメインモデルに集約する。
  • データベース操作はリポジトリクラスに持っていく。
  • サービスクラスは小さく分ける。
  • 小さく分ける基本は、まず登録系のサービスと参照系のサービスを分ける。
  • 小さく分けたサービスを組み立てる。
  • 必要に応じて、基本的なサービスクラスを組み合わせた複合サービスを提供するシナリオクラスを作る。

第6章 データベースの設計とドメインオブジェクト

  • カラム名は省略形にしない。
  • 全てのカラムに NOT NULL 制約を付ける。
  • NULL を入れる必要があるカラムが出てきたら、別テーブルに分けるサイン。
  • 一意性制約でデータの重複を防ぐ。
  • 外部キー制約でテーブル間の関係を明確にする。
  • 記録のタイミングが異なるデータはテーブルを分ける(NULLを防ぐ)。
  • UPDATE を使わずに、DELETE, INSERT を使う(訂正の記録を残せる)。
  • データベースの設計変更でカラムを追加する場合は、テーブルを追加して、そこに入れる。
    • 元のテーブルがそのまま利用できる。
    • 追加したカラムに NULL を入れなくて済む。
  • 基本は「コト」を記録するテーブルを作る。
  • 「コト」によって変化する「状態」などの派生的なでデータは別テーブルに記録する。
  • 銀行口座で例えるなら、「コト」は入金、出勤、「状態」は残高。
  • ただし、この方法では厳密な即時性には対応できない。
  • オブジェクトとテーブルは似てくるが、兼用させないで、違うものとして明示的にマッピングする。

第7章 画面とドメインオブジェクトの設計を連動させる

  • タスクごとに画面を分ける。
  • 画面表示ロジックに業務ロジックを書かない。
  • 画面表示で if 文を使っている場合は、その条件判断をドメインオブジェクトに移動する(特定の条件で赤字で表示、0件の場合の特別表示、など)。

第8章 アプリケーション間の連携

  • POST と PUT の違い。POST は識別子を指定しないで登録し、レスポンスで識別子を返す。PUT は識別子を指定して登録する。
  • Web API は、修正を繰り返すうちに何でも出来る肥大化した API になりがちだが、それだど修正や拡張が難しくなる。
  • 修正や拡張を重視する場合、組み合わせて使用するような適度な大きさの API に分割する(ただし、あまり細かくすると組み合わせるのが大変になる)。
  • 初期段階では単純な API だけを提供し、フィードバックを得ながら追加、変更して行く。
  • 単純な API は、登録と参照を分ける、リソースの単位を分ける、ことに気をつける。

第9章 オブジェクト指向の開発プロセス

  • 更新すべきドキュメント:利用者向けのドキュメント、画面や帳票、データベース仕様
  • 個人的にはAPI仕様も加えた方が良いかも

第10章 オブジェクト指向設計の学び方と教え方

  • 過激なコーディング規則
    1. 1つのメソッドにつきインデントは1段階までにすること(good)
    2. else 句を利用しないこと(good)
    3. 全てのプリミティブ型と文字列をラップすること
    4. 1行につきドットは1つまでにすること(too much)
    5. 名前を省略しないこと(good)
    6. 全てのエンティティを小さくすること
    7. 1つのクラスにつきインスタンス変数は2つまでにすること(too much)
    8. ファーストクラスコレクションを使用すること(good)
    9. getter、setter、ブロパティを使用しないこと(too much)
  • ↑ 「過激」と銘打っているだけあって、さすがにやり過ぎだと思う項目が・・・

参考文献の中で読んでみたいと思ったもの

 

アルゴリズムは使用するプログラミング言語に引きずられる

Written by じび on 10月 9th, 2018

某所で5分以内にプログラムを考えるという課題がありました。

最初にプログラミング言語を選択するのですが、最近は swift ばかりなので swift を選択しました。

その後に出たお題は「文字列を逆順にする。ただし、プログラミング言語にある逆順にする命令などは使わないこと」というものです。

そこで私の思考は、以下のようになりました。

  1. 文字列は文字の配列
  2. 配列を処理するならイテレータ
  3. 先頭から文字をとっていき、新しい配列の先頭にインサートしていけばいける

IDEなしだったので、仮想コードですが、以下のような感じに書きました。

let src = "hogemoge"
var dest = ""

for char in src {
dest.insert(0, char)
}

しかし、帰りの電車で、C言語ならもっと効率良くできることに気づきました。

  1. 最初と最後の文字をスワップ
  2. 最初+1と最後-1の文字をスワップ
  3. 上記の処理を「文字数 / 2」回繰り返す
unsinged char *src = "hogemoge";
unsigned char swap;

for (int i = 0, int last = strlen(src) - 1; i < strlen(src) / 2; i++, last--, ) {
swap = src[i]
src[i] = src[last]
src[last] = swap
}

なぜ、思いつかなかったのだろうと考えたのですが、最初に言語を選択していたため、そこでよく使う手法に囚われていたのです。swift だと文字列を文字単位で扱うことはあまりないため、配列として処理しようと考えてしまったのです。それに対してC言語はビット単位やバイト単位で扱うことが多いので、すんなりと効率が良いアルゴリズムに導かれたのでした。

「オブジェクト指向プログラミング入門 第二版 (ティモシイ・A. バッド)」の「1.2 言語と思考」にも、APLのプログラマが行列のソートで解決した話が載っていましたが、まさにその通りのことを身を以て体験したのでした。

 

[shell][iOS] クローンからシミュレーターでの実行までやってくれるスクリプト

Written by じび on 10月 5th, 2018

大きめの iOS アプリの開発をやっていると、クローンもライブラリのダウンロードもビルドも全てにおいて時間がかかるようになります。
それぞれを個別に人間が実行すると面倒なので、Git のクローンからシミュレーターでの実行まで、連続してやってくれるシェルスクリプトを作りました。

#!/bin/sh -e

if [ $# -ne 1 ]; then
    echo "Usage:runMyProject branch" 1>&2
    exit 1
fi

Repository="https://github.com/UserName/MyProject"
ProjectFile="MyProject.xcodeproj"

echo "===== git clone ====="
git clone -b $1 ${Repository} .

echo "===== carthage bootstrap ====="
carthage bootstrap --platform ios --no-use-binaries --cache-builds

echo "===== open Xcode ====="
open ${ProjectFile}
sleep 20

echo "===== Xcode run ====="
osascript -e 'tell application "Xcode"
    activate
    run active workspace document
end tell'
  • ファイル名やシェル変数の Repository および ProjectFile は、各自の環境に合わせて変えてください。
  • 最後に iOS Simulator での実行までやってますが、ビルドで止めたいときは、run の部分を build に変えてください。

実行時は、まずディレクトリを作って、その中に入り、第一パラメーターにクローンしたいブランチ名を指定して起動します。
$ mkdir hoge
$ cd hoge
$ runMyProject develop

改良したい点としては、

  • Xcode を起動後、20秒待つのではなく、立ち上がり完了を検知するようにしたい。
  • iOS シミュレーターの種類を指定できるようにしたい。
 

日和号弐号機の設計

Written by じび on 10月 4th, 2018

我が家にはソーラーパネルで発電し、バッテリーに電気を貯める、ポータブル太陽光蓄電システム「日和号改」があります。

今年もキャンプに持って行ったのですが、バッテリーがへたってしまい、あまり電気を貯められなくなりました。
バッテリーだけ買い換えても良いのですが、設計から4年も経ち、電力の使用事情も変わって来ました。
そこで増えた電力量を計算の上、弐号機を設計することにしました。

1日に使用する電力量

iPhone のバッテリー 約3,000mAh * 3.7V -> 11.1Wh -> 12Wh
変換時のロスを20%として 12 * 1.2 -> 14.4Wh -> 15Wh
一日に5台充電するとして、15 * 5 -> 75Wh

必要なソーラーパネル

1日の発電量は、ソーラーバルのW数 * 3h
必要なソーラーパネルの発電能力は、75 / 3 -> 25wh

必要なバッテリー

1日で消費される電力量が、バッテリーの25%までになるように設計する。
75Wh * 4 -> 300Wh
12Vバッテリーの場合、300Wh / 12V -> 25Ah

初号機は不要になるので、使える部品は再利用します。
コントローラー、シガーソケット、スイッチなと。

ソーラーパネルは30wのものがあるので、それを使います。
バッテリーは、これくらいの容量になると車用のが安いのですが、屋内に設置するので、ガスが出ない通信機器用の密閉型にします。
バッテリーは25Ahのだと割高なので、思い切って50Ahのにしましょう。

 

iPhone 5S フロントパネル交換

Written by じび on 9月 17th, 2018

iPhone 5S のフロントパネルをホームボタンごと交換しました。
しばらく前にバッテリーを交換したのですが、フロントパネルと液晶パネルの接着剤が剥がれていました。
そのまま取り付け取り外しをしていたら、ホームボタンまで効かなくなったのて、丸ごと交換しました。

開けて見たところ、ホームボタンのリボンケーブルが千切れていました。

新しいタッチパネルはちょっと動きが悪いのと、ボタンの押し込みが純正よりも重いのですが、概ね好調です。