🔧

「現場で役立つシステム設計の原則」要点

2022/05/15に公開

書籍

https://gihyo.jp/book/2017/978-4-7741-9087-7

1. 小さくまとめてわかりやすくする

設計とは

どこに何が書いているのかわかりやすくすること。
(そうすることで、変更や拡張が楽で安全になるコードを生み出すように導く。)

プログラムの変更が楽になる基本テクニック

  • わかりやすい名前を使う。(名前重要
  • 意味のかたまりごとに改行する。
  • 目的ごとに変更を用意する。(説明用変数の導入)
    • 1つの変数に使いまわさない。(= 破壊的代入をしない。)
    • 変更の影響範囲を小さくできる。
  • メソッドに切り出す。(メソッドの抽出
  • 異なるクラスの重複したコードを無くす
  • 狭い関心事に特化したクラスにする
  • メソッドは短くクラスは小さく

値オブジェクト

基本データ型の落とし穴

基本データ型では扱える範囲が広すぎて、アプリケーションにとって適切でない場合がある。
(例:intは、マイナス21億〜21億までの整数)
値を扱うためのクラス値オブジェクト)を定義して、範囲外の値は異常値としてエラーにする等、適切な範囲の値を取り扱う。

値オブジェクトは不変にする

  • インスタンス変数はコンストラクタで(インスタンス生成時に)設定。
  • インスタンス変数を変更するメソッド(setter)を作らない。
  • 別の値が必要になったら、別のオブジェクトを作成する

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

コレクション型のデータとロジックを特別扱いにして、コレクションを1つだけ持つ専用クラスコレクションオブジェクト)を作り、複雑さを閉じ込める。
(値オブジェクトと同じように、コレクションオブジェクトも不変にする。)

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

場合分けのコードは、プログラムを複雑にする。

else句を使わない

  • else句をなくすと条件分岐が単純になる。else句を使わず早期リターンする。(ガード節)
  • 複文は単文に分ける。else句を使わずに独立性の高い単文を書く。

インターフェース

区分ごとのロジックを別クラスに分ける。
インターフェースを用いてポリモーフィズム(多態性 = 別のクラスを同じ型として扱えるようにする)を実現する。
クラス間は、互いを知らないほど良い。結合が弱くなり(独立性が高くなり)、変更による別クラスへの影響が減る。

enum

列挙型(enum)を使い、区分定数の一覧を宣言する。
(ポリモーフィズムは、区分の一覧がわかりにくいという問題があるため。)

  • 区分オブジェクトenumを使って区分ごとのロジックをわかりやすく整理する方法。

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

ドメインオブジェクト

業務ロジックを、データを持つクラスに集める

↑はオブジェクト指向の基本。

  • 手続き型の設計:データクラス(データ構造)と機能クラス(処理手順)に分ける。
  • オブジェクト指向:データとロジックを1つのクラスにまとめる。コードの重複や散在を防ぐ。

メソッド

  • データを持つクラスのメソッドは、ロジックの置き場所
  • インスタンス変数を返すだけのgetterメソッドは(基本)書かない。
    こういうメソッドには何らかの計算/加工/判断をさせる任務を与えることを考える。
  • メソッドはインスタンス変数を必ず使う使わない場合はメソッド(ロジック)の置き場所を再検討すべき

クラス

  • クラスが肥大化したら、関連性の高いデータとロジックを抜き出して、新しいクラスに分けることを考える。
  • 凝集度が高いクラスを作ることが、オブジェクト指向の基本。
    独立性が高く、再利用性が高い。他のクラスへ影響を与えづらい(= 疎結合)。
    • 凝集:「切っても切れない」密接な関係。

パッケージ

クラスの数が増えたときの整理の手段として、パッケージがある。

  • 関連性の強いクラスは同じパッケージに集める。(= クラス郡を作る。)
  • パッケージ内のクラスやメソッドのスコープは、できる限りパッケージ内に留める。(= publicにしない。)

ドメインモデル

ドメインをモデリング(抽象化)したもの。
(ドメインモデルを実装したものが、ドメインオブジェクト。)
ドメインモデルを見れば、業務全体がどういった関心事から成り立っているのか理解できる。

3層 + ドメインモデル

業務ロジックはドメインモデルにのみ記述。
業務的な知識が必要な判断/加工/計算について、3層はドメインオブジェクトに任せる。

  • 3層
    • プレゼンテーション層:外部との入出力。(UIなど)
    • アプリケーション層:マクロな手順。(業務フローなど)
    • データソース層:データの入出力。(データベースなど)
  • ドメインモデル:業務データと業務ロジックを表現したドメインオブジェクトの集合。

4. ドメインモデルの考え方で設計する

ドメインモデルを開発する工程

  • 分析人間のやりたいことを正しく理解する
    • 要求の聞き取り
    • 不明点を確かめるための会話
    • 図や表を使っての整理
    • 理解した結果を記録する文書の作成
  • 設計人間のやりたいことをソフトウェアとして実現する方法を考える
    • パッケージ構成と名前
    • クラス構成と名前
    • メソッド構成と名前

ドメインモデルの作り方

  1. 部分を作りながら全体を組み立てていく
    個々の部品(ドメインオブジェクト)を作り、それを組み合わせながら全体を作っていく。
  2. 全体と部分を行ったり来たりしながら作る
    部分だけに目を向けず、全体として間違った方向に進んでいないか確認しながら。
    全体を俯瞰するためのツールとして、パッケージ図・業務フロー図がある。
  3. 重要な部分から作っていく
    重要な部分とは、間違いなく必要な部分。
    オブジェクトは独立性の高い部品なので、単体として動作し、テストできる
  4. 独立した部品を組み合わせて、機能を実現する
    作ったドメインオブジェクトを組み合わせて、アプリケーション層で機能を実現

ドメインオブジェクトの見つけ方

業務の関心事を、ヒト/モノ/コトに3分類する。

  • ヒト:業務活動の当事者。行動する主体。
  • モノ:関心の対象。物理的なモノ + 概念的なモノ。
  • コト:事象。基本的にヒトの意思決定や行動の結果

コトに注目して整理すると効果的

  • コトは、ヒトとモノとの関係であるため、関心事を特定しやすくなる。
  • コトは、時間軸の前後関係を持つため、全体の流れや重要な前後関係が明確になる。

ドメインモデル活用の基本パターン

アプリケーション層のクラスには、業務的な判断ロジックが増えがち。(= 「ちょっとしたif文」を書いてしまいがち。)
これでは、手続き型のトランザクションスクリプトになってしまう。

こうなる原因は、どういうドメインオブジェクトがあれば楽にプログラムを書けるかというイメージが持てないため。
下記のパターンを体で覚えるのが近道。

<ドメインオブジェクトの基本の設計パターン>

ドメインオブジェクト 設計パターン
値オブジェクト 基本データ型(数値、文字列、日付)をラッピングしてロジックを整理する。
コレクションオブジェクト 配列やコレクションをラッピングしてロジックを整理する。
区分オブジェクト 区分の定義と、区分ごとのロジックを整理する。
enumの集合操作 状態遷移ルールなどをenumの集合として整理する。

<業務の関心事のパターン>

関心事のパターン 業務ロジックの内容
口座(Account)パターン 現在の値(残高)を表現し、 妥当性を管理。
期日(DueDate)パターン 約束の期日と判断を表現。
方針(Policy)パターン 色々なルールが存在する、複雑なロジックを表現。
状態(State)パターン 状態と、状態遷移を表現。

業務の言葉とコードを一致させる

  • 役立つドメインオブジェクトは、クラス名やメソッド名がそのまま業務の言葉と一致する。
  • ソースコードで業務の要求仕様を表現(= 自己文書化)することで、プログラムの変更を容易にする。

業務の理解

業務の基本知識を身につける方法

  1. マニュアルや利用者ガイドを読む。
  2. 一般的な知識を書籍などで勉強する。
  3. その業務で使われる画面やファイルを調べる。
  4. 形式的なドキュメントよりも、図(業務フロー図など)を使って関係性を整理する。
  5. 業務の専門家と会話する。
  6. 自分の理解が正しいか、業務の専門家に確認する。

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

サービスクラス

  • 3層 + ドメインモデルの設計において、アプリケーション層はサービスクラスと呼ばれる。
  • サービスクラスの記述はごちゃごちゃしがち。そうならないように、下記を徹底する。
    • 業務ロジックは、サービスクラスに書かずにドメインオブジェクトに任せる
      サービスクラスで判断/加工/計算しない
    • 画面の複雑さを、サービスクラスに持ち込まない。
    • データベースの入出力の都合から、サービスクラスを独立させる。
  • 意味のある最小単位かつ単独でテスト可能な単位に、メソッドを分割するのがサービスクラス設計の基本。

シナリオクラス

  • 複数の処理を一連で行いたい場合、小さく分けたサービスクラスを組み合わせて、シナリオクラスを作る。(サービスクラスのみでは実現できないため。)
    • 【例】預金を引き出す機能
      ①残高が不足していないことを確認。
      ②残高を更新。
      ③更新後の残高を照会する。
  • 小さく分かれているサービスクラスは、それ単独でも使用できるため、利用性が高い。
    (残高を照会する機能だけを使う 等)

契約による設計

  • サービスを利用する側と提供する側とで、サービス提供における約束事を決め、設計をシンプルに保つ技法
    • 対照的な技法:防御的プログラミング
      利用する側が何をしてくるかわからないという前提で、様々な防御的なロジックを書く手法。
  • 基本的な約束事は下記。
    • nullを渡さない/返さない
    • 状態に依存する場合、使う側が事前に確認する。
    • 約束を守った上でさらに異常が起きた場合、例外で通知する。

リポジトリ

  • データの入出力はアプリケーションの重要な部分。
    ただし、データベース操作の手順と業務ロジックは別物であり、データベースの入出力手順(手続き)を並べるプログラムは、業務的な意図が読み取りにくくなる
  • 情報の記録と参照(※)は、業務の関心事
    データベースの単なるCRUD操作は、業務の関心事ではない。
  • ※を実現するドメインオブジェクトの保管と取り出しができる(仮想の)収納場所を、リポジトリとして宣言する。
  • リポジトリを使うことで、データベース操作の詳細をサービスクラスに意識させなくて済む。
    • 【例】注文を記録する機能
      注文テーブルと注文明細テーブルにINSERTが必要だとしても、注文を記録するという業務の関心事とは関係ない。
      こういったテーブル設計に依存する心配事は、業務機能を記述するサービスクラスには不要。
    • リポジトリインターフェースの背後に隠すことで、サービスクラスは業務の関心事だけに専念できる。(= シンプルな記述にできる。)

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

悪いテーブル設計

プログラムが分かりにくく複雑になっている原因が、データベース設計やデータ内容の問題であることがよくある。

  • データの妥当性を判定したり、例外的なデータを扱うための処理などにif文が増える。
  • テーブル定義やデータ内容に現れない暗黙の知識が大量に必要になる。
  • SQLが複雑になりがち。

用途がわかりにくいカラム

  • カラム名が省略形
  • NULLが入っているカラム
  • 他のカラムの値によって値の意味が変わるカラム
  • カラムから取得した値を、プログラムで分解する必要がある
  • 意味が読み取れないコード(1, 2, 3...などのマジックナンバー)

色々な用途に使う巨大テーブル

カラム数が多い巨大なテーブル。

  • 似たようなカラムが多く、使い分けがわからない
  • NULL値が多い

テーブルの関係がわからない

  • 外部キー制約がない
  • キーとなるカラム名に一貫性がない

良いテーブル設計

名前を省略しない

意味の明確な、共通の単語を使う。

適切なデータ型を使う。

データ型と桁数を適切に制限する。

制約を必ず使う

NOT NULL制約

NOT NULL制約で、良いテーブル設計へ導く。

  • カラムにはNULLを含めないのが、データベース設計の基本
    (NULLは演算不能を意味する。SQLやプログラムでNULLを想定しないといけなく、意図しない挙動になる or 複雑になる。それらがバグの原因になるため。)
  • カラムは全てNOT NULLにする
    どうしてもNULLが必要であれば、別のテーブルに分けることを考える。こうすることで正規化が進む

UNIQUE(一意性)制約

UNIQUE制約で、データの重複を防ぐ
UNIQUEPRIMARYは似ているが別物。)

外部キー制約

外部キー制約で、テーブル間の関係を明確にする。

正確に記録するための3つの工夫

  1. 記録のタイミングが異なるデータはテーブルを分ける
    NULL可能なカラムが必要になってしまうため。
  1. 記録の変更を禁止する
    UPDATE文を使わない。
  • 方法1: 過去データは残す。取り消しデータと新データをINSERT。
  • 方法2: 過去データはDELETE。新データをINSERT。
  1. カラムの追加はテーブルを追加する
    そのカラムには過去のデータが存在せず、NULLを許容しないといけないため。

参照をわかりやすくする工夫

状態テーブル

  • 銀行の残高など、理論的に導出できる値であっても、その都度算出するのはロジックが複雑になり性能面でも問題となることがある。
    これは、状態テーブル(= この例で言うと残高テーブル)を追加することで解消できる。
    (DBのインデックスは、これと同じようなことをしている。2次的なインデックス情報を生成して、検索性能を向上させている。)
  • この残高の更新は同時でなくても良い。(= 同一トランザクションで処理しなくていい。)

イベントソーシング

コトの記録を唯一の情報源(トリガー)として、そこから派生する様々な情報を目的別に記録する方針。
(非同期メッセージングで、分散型で非同期的に処理する。)

  • システム間の連携を疎結合にしやすい。
  • 個々の機能は、自分の担当領域だけを処理するシンプルなプログラムにできる。
  • 厳密な即時性や、データ間の整合性を保証するには、それなりの仕組みが必要になる。
    非機能要求な運用面からは、検討すべき課題が多いのが実情。

【例】

  • コトの発生を顧客管理サーバに通知すると、顧客管理サーバは顧客単位の残高を更新する。
  • コトの発生を営業管理サーバに通知すると、営業管理サーバは売上高を更新する。

オブジェクトの設計とテーブルの設計

  • オブジェクトとテーブルは似てくるが、似ているだけで別物
    設計のアプローチや、設計を変更する動機が本質的に異なる。
    • 設計のアプローチとして、オブジェクトは部分から全体に、テーブルは全体から部分を考えて設計する。
  • オブジェクトとテーブルは別々に設計し、業務の関心事の表現として正しくマッピングすることが大切。

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

画面アプリケーションの開発の難しさ

  • 画面にはさまざまな利用者の関心事が詰め込まれる。
  • 画面単位でロジックを埋め込む、画面単位のプログラム(画面に引きづられた設計)は変更を大変にする。
    • 表示のためのロジックと業務ロジックが混在してしまう。
    • 複数の画面に同じコードが重複してしまう。
  • 複雑で変更がやっかいになる理由
      1. 画面そのものが複雑
        何でもできる汎用画面でなく、用途ごとのシンプルな画面に分ける
        タスクベースのインターフェース:用途を特定した小さな単位の画面)
      1. 画面の表示ロジックと業務ロジックが分離できていない。
        → 画面まわりのロジックから業務ロジックを分離する。

画面とドメインオブジェクトの連動

タスクベースのインターフェースの場合

  • ドメインオブジェクトをそのまま使う。
  • 画面の関心事ドメインオブジェクトで表現する関心事は一致するのが基本。
    • 一致しない場合、ドメインオブジェクトの設計 or 画面のデザイン を見直す必要があるかもしれない。

複数の関心事が混在している「何でも画面」の場合

  • ビュー専用のオブジェクトを、プレゼンテーション層に用意。
    ビュー専用のオブジェクトの中で、複数のドメインオブジェクトを組み合わせる。

ビューとモデルの分離

1. 論理的な情報構造は、ドメインオブジェクトで表現する

ビューに書くべきこと

  • 物理的なビュー : 画面を表示する技術方式に依存したビューの表現。(物理的な手段)
    • 例:HTMLタグ改行コード など

ドメインオブジェクトに書くべきこと

  • 論理的なビュー : 技術方式に依存しない概念的な構造。
    • 例:段落がいくつあるか文字数長文は最初の20文字だけ表示する千円単位でカンマを付ける など

2. 場合ごとの表示の違いは、ドメインオブジェクトで出し分ける

  • 画面表示でif文を使っている場合は、それをドメインオブジェクトに移動できないか検討する。
    それを修正するときに多数の修正が必要になるため。1箇所に集約し、閉じ込めやすくなる。

視覚表現

画面項目の並び順

画面での項目の並び順と、対応するドメインオブジェクトのフィールドの並び順は一致させる。

画面項目のグルーピング

原則 説明 ドメインオブジェクトとの連動
近接 関係のある情報を近づける。 画面デザイン上、離してある情報が、1つの
ドメインオブジェクトにまとまっているのは問題。
整列 同じ意味なら同じラインに揃える。
異なれば異なるラインに揃える。
インデントされている場合は、
意味として異なるということ。
対比 意味の重みの違いを
文字の大きさや色で区別する。
重要な項目はクラス内の上部で宣言する。
弱く表現されている項目は別のクラスを作って隠蔽するのも検討。
反復 同じ意味は
同じパターンで視覚化する。
同じ型のオブジェクトで表現する。
・1つのクラスの別々のオブジェクト
・インターフェースで同じ型で宣言されている、複数のクラスのオブジェクト
(画面デザインの4原則 参考サイト:https://umuco.jp/design-4/)

利用者向け情報

(画面と同じく)利用者向け情報も、利用者の関心事を表現している。

  • プレスリリース:ソフトウェアの特徴、セールスポイント が記載されている。
  • リリースノート:変更点 が記載されている。
  • 利用者ガイド:開発時に作られた仕様書よりも、利用者ガイドはメンテ・改善されることが多く、ソフトウェアの仕様を正しく表現している可能性が高い

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

アプリケーションを連携する4つの方法

  1. ファイル転送
  2. データベース共有
  3. WebAPI
  4. メッセージング:メッセージング基盤を使って非同期にデータを送る

WebAPI

標準

  • データ形式:JSON or XML
  • 文字コード:UTF-8

PUT, DELETEはPOSTに

  • PUT, DELETEは、できるだけPOSTに置き換えて、GETと組みあわせるほうが良い。
    (データの登録と相手の状態を同時に扱うPUT → POSTによる登録 + GETによる状態の取得)
    • PUT, DELETEは、APIを使う側が、APIを提供する側のリソースの識別体系を事前に知っている必要があるため。
      (アプリケーション間の依存性を強くし、密結合になってしまう。)

エラー時の約束事

  • 500番台のエラーの場合、エラー内容は詳細にすべきではない。
    • APIを使う側にとって不要な情報なため。
    • セキュリティ的に保護すべき内容が含まれるリスクがあるため。

良いAPIとは

APIで重要なのは、色々なアプリケーションを組み立てるために役立つこと。
組み立てやすく変更しやすい、適度な粒度に分割されている部品(API)が使いやすい。

APIの設計原則

登録と参照は別のAPIにする

【例】何かを予約するAPI

  • ✕ POSTのレスポンスとして、予約内容の詳細を返す
    • WebAPIでなく、Webサービスの発想。
  • ◯ POSTのレスポンスとして、予約番号だけを返し、予約内容はその予約番号を使って別途GETする
    • 柔軟なアプリケーションを組み立てられる。

リソースの単位を分ける

より小さな単位のデータを取得・登録できるAPIを提供する方法もある。

【例】

  • 名前だけを取得:GET members/1234/name
  • 名前だけを更新:POST members/1234/name

バージョン管理

古いAPIを廃止する場合、一定の移行期間を設けて段階的に廃止する。

【例】

  1. 新しいAPIを追加。互換性のため、古いAPIも提供を続ける。
  2. 古いAPIでは303 See Otherを返すよう変更。
  3. 古いAPIでは404 Not Foundを返すよう変更。
  4. 古いAPIを削除。

APIを複合したサービス

複合サービスの提供は、アプリケーション間の結合度を上げてしまい、お互い(利用者・提供者)のアプリケーションの成長や、APIの修正・拡張の障害になる。
そのため、可能な限り、複合サービスはAPIを利用する側が開発すべき。

ドメインオブジェクトとWebAPI

ドメインモデルで設計した場合、WebAPIの役割は、ドメインオブジェクト→JSONなどへの変換。
ただし、単純に変換するだけでは不都合な場合がある。

データ構造の不一致

ドメインオブジェクトのロジックの整理を軸に分けられている階層構造が、データだけが関心事であるAPI利用者にとって不便な場合。

関心事の不一致

  • 利用者:APIから、不要な情報まで返ってくる。
  • 提供者:期待するデータ項目がPOSTされない。 など

互いの関心事のズレが大きい場合は、変換用の中間オブジェクトをプレゼンテーション層に用意したほうが良い。

  • リクエスト:POSTされたデータ→リクエスト(中間)オブジェクトにマッピング→ドメインオブジェクトに変換
  • レスポンス:ドメインオブジェクト→リクエスト(中間)オブジェクトを生成→データ返却

導出結果か生データか

提供側で加工・判断した結果のデータを返すか、元データのままかという判断は難しい。

  • マスタ項目の コードと名称(例:職種コードなど)
    • コードのみ:コードから名称を取得できるAPIを別途準備。マスタ情報をAPIを使って共有する必要がある場合。ただし、密結合になるため基本的に避けたい。
    • 名称のみ:名称の重複の可能性があるため、厳密さに欠ける。ただし、名称のみで十分な場合は良い。(都道府県名など)
    • コードと名称の両方:名前の重複を考慮する場合。
  • 導出可能なデータ(例:誕生日など)
    • 基本は、計算のロジックをどちらのアプリケーションが管理すべきか で判断する。
    • 業務ルールと呼べないような単純な計算の場合、基本はAPI提供側で計算。
  • 日付データの形式
    • 問題は、タイムゾーン。(ISO表記:2022-05-11T12:34:23+09:00
    • 現地時間or標準時間、どちらで表記するかはAPIの約束事として決めておく。
    • 人間にとって通常の表現が、間違いが起こりづらい。(ISO表記はプログラムにはわかりやすいが、人間にはわかりづらい。)

複雑な連携

APIの利用者が複数になると、設計と変更は難しくなる。
APIを次の3種類に分けて考えて、見通しを良くする。

  • (コアとなる)基本API:最小単位のAPI。どの利用者にも共通に使えるものだけ。
  • 拡張API:基本APIを組み合わせた複合API。どの利用者にも共通に使えるものだけ。
  • 個別対応API:特定の利用者のニーズを満たすためのAPI。

個別対応APIが増えると、開発・運用に手間が増えるため、下記方針でAPIを都度整理する。

  • 複数の利用者に同じような個別対応をしている場合:基本APIや拡張APIに移動。
    (個別対応APIより、基本API・拡張APIのほうがメンテされるため、利用側としてもそのほうが良い。)
  • 特定の利用者だけが使っている基本API・拡張APIがある場合:個別対応APIに移動。

マイクロサービス

1つのアプリケーションとして開発してきた内容を、複数の小さなアプリケーションに分割して、それを連携して全体の機能を実現させる方式。

JSON, XML

データ項目が多く、データ構造が複雑になる場合は、XMLを選択したほうが良い。

  • JSON : プログラム言語の基本データ型だけを使ったデータ交換を意図している。
  • XML : JSONより複雑な情報を表現することを意図している。

非同期メッセージング

アプリケーション間のデータのやり取りを「メッセージング」を送ることで実現。

  • アプリケーション間の独立性が高い。
    • 送信元アプリケーションからメッセージング基盤に送信し、メッセージング基盤から送信先アプリケーションに送信する。
  • 大量のデータを高速に処理しやすい。
    • 相手のアプリケーションの稼働状況とは無関係に(メッセージング基盤に)メッセージを送り出せる。
  • 開発・テストしやすい。
    • 個々のアプリケーションは、メッセージング基盤とのインターフェースだけを意識して、開発・テストを進められる。
  • 共通の中間加工をしやすい。
    • メッセージング基盤が、アプリケーション間のデータ変換などの処理を行うため、個々のアプリケーションは変換を意識しないで済む。
    • 共通の処理は、メッセージング基盤に用意できる。

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

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

V字モデルと異なり、要求の分析と設計を一体で進める。
大量にドキュメントを作ってからプログラムで書き換えるような無駄はせず、分析しながら理解した内容を直接ソースコードとして記録し確認する。(多くのドキュメントが不要になる。)

重要な活動

  • 対面の質疑応答
    要求を分析する基本手段。利用者や要求者との会話。
  • 質疑応答とその記録
    メール、チャット、ToDo管理ツールなどのコミュニケーションツール。
  • ラフスケッチ
    ホワイトボードを撮影して共有すれば効率的。

更新すべきドキュメント

ソースコードは技術者なら理解できるが、それ以外の人と情報を共有するために、下記ドキュメントは更新すべき。

  • 利用者向けのドキュメント
    利用規約やユーザガイド。外部仕様書の役割を果たす。
  • 画面や帳票
    利用者が業務で使っている画面や帳票は、詳細な要求の実体。要件定義書の役割を果たす。
  • データベースのテーブル名・カラム名とコメント
    データベースのコメントは生きた仕様書になる。
カラムへのコメント (Rails)
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name, comment: "ニックネーム"
      t.string :first_name, comment: "名(下の名前)"
      t.string :last_name, comment: "姓(名字,上の名前)"

      t.timestamps
    end
  end
end

分析と設計が一体となった開発のやり方をマネジメントする

進捗の判断

オブジェクト指向で分析・設計し、業務の用語の単位でパッケージ・クラス・メソッドが作成されていると、小さな単位、かつ業務の関心事の単位で、進捗を判断できる。

品質保証

分析・設計・実装を同じ人が行った場合、品質の判断をするもっとも簡単な方法は、その技術者と会話すること。
業務の言葉で、開発している内容を説明できれば、品質は高いはず。
(逆に、業務の言葉でうまく喋れていなかったり、会話がぎくしゃくしている場合は要注意。)

要員と体制

プログラミングが一定レベルででき、業務要件に関心を持つ人間を選び、業務要件をソースコードに表現できる人材を評価することで、業務アプリケーションの開発を担う人材を確保・育成できる。

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

筆者おすすめの学び方

  1. 既存コードを改善しながら学ぶ(= 実務)
  2. やや極端なコーディング規則を使って、体で覚える。

2.やや極端なコーディング規則 について

  • 1つのメソッドのインデントは1つまで
  • else句を使わない
  • すべての基本データ型と文字列をラップする(= 値オブジェクトを使う)
  • 1行につきドットは1つまで
    • ドットで複数のメソッドをチェーンした文は、意図がわかりにくくなる。
    • 同じ処理があちこちに重複しがち。チェーンの途中で切って、一部だけを他の場所で再利用できないため。
  • 名前を省略しない
  • 1つのクラスのインスタンス変数は2つまで
    • インスタンス変数とメソッドの関連づけを徹底するためのルール。
    • インスタンス変数が増えるとクラスの意図がぼやけ、ゆくゆくは巨大なクラスになってしまう。
    • インスタンス変数とメソッドが密接に結びついたクラスは、目的が単純で意図が明確になる。
  • コレクションオブジェクトを使う
  • getter, setter, プロパティを使わない
    • getter:メソッドは何らかの判断・加工・計算をしないといけないため、インスタンス変数をそのまま返すだけのgetterは書いてはいけない。
    • setter:インスタンス変数を書き換えるのでなく、不変にするのが良い設計。(別オブジェクトを用意して、完全コンストラクタにする)
    • プロパティ:getter/setterそのもの。使うべきでない。
  • すべてのエンティティを小さくする
    • こうすることで、かなりの数のメソッド・クラス・パッケージに分けることになる。
      その分、名前を考える必要があり、それが設計を良くする
対象 ガイドライン
メソッド 目標 3行。
クラス 目標 50行。100行以上は不可。
パッケージのファイル数 10ファイル以下。

Discussion