書籍概要

現場で役立つシステム設計の原則
〜変更を楽で安全にするオブジェクト指向の実践技法

著者
発売日
更新日

概要

「ソースがごちゃごちゃしていて,どこに何が書いてあるのか理解するまでがたいへん」「1つの修正のために,あっちもこっちも書きなおす必要がある」「ちょっとした変更のはずが,本来はありえない場所にまで影響して,大幅なやり直しになってしまった」といったトラブルが起こるのは,ソフトウェアの設計に問題があるから。日本最大級となる60万件以上の求人情報サイト「イーキャリアJobSearch」の主任設計者であり,システム設計のベテランである著者が,コードの具体例を示しながら,良い設計のやり方と考え方を解説します。

こんな方におすすめ

  • システム設計のポイントを知りたいエンジニア

著者から一言

ソフトウェアを変更するとき,こんな経験がありませんか?

  • ソースがごちゃごちゃしていて,どこに何が書いてあるのか理解するまでが大変だった
  • 1つの修正のために,あっちもこっちも書き直す必要があった
  • ちょっとした変更のはずが,本来はありえない場所にまで影響して,大幅なやり直しになってしまった

オブジェクト指向でソフトウェアを設計する目的は,こういう変更の大変さを減らすことです。どこに何が書いてあるかをわかりやすくし,変更の影響を狭い範囲に閉じ込め,安定して動作する部品を柔軟に組み合わせながらソフトウェアを構築する技法がオブジェクト指向設計です。
しかし,オブジェクト指向と聞くと「よくわからない」「今の自分の仕事には関係ない」「やってみたがうまくいかない」という人も多いようです。そうなってしまうのは,オブジェクト指向の説明が具体性に欠けていたり,例が単純すぎて現場の感覚には合わない内容が多いからです。
オブジェクト指向は理論ではありません。開発の現場で工夫されてきた設計のノウハウです。現場ですぐに役に立つ実践的なやり方と考え方です。
本書では,私が業務アプリケーションの変更に苦しんだ経験をもとに,オブジェクト指向設計のやり方と考え方,効果があった解決策を,具体的なソースコードを示しながら紹介していきます。特に「なぜそうするのか」を重視して説明します。
オブジェクト指向設計の考え方を理解し,実践で活用するポイントは2つあります。
第1のポイントは「体験」です。オブジェクト指向で設計する良さは,最初はなかなか理解できません。本書で紹介するオブジェクト指向設計のやり方を,現場の実際のコードで繰り返しているうちに,しだいにオブジェクト指向設計で変更が楽で安全になることが実感できるようになります。一度,その良さを体感できるとオブジェクト指向で設計することが当たり前になります。
第2のポイントは「発想の切り替え」です。オブジェクト指向の言語を使っているが,設計のやり方が手続き型のまま,というケースを私は何度も見てきました。特にJavaは手続き型の発想で書くこともできるプログラミング言語のため,そのようなケースが多いのが実情です。本書で紹介するオブジェクト指向らしい設計の考え方とやり方を理解し,発想を転換する手がかりにしていただければと思います。
オブジェクト指向の考え方は,要件定義や基本設計という,いわゆる上流工程でも威力を発揮します。本書では,要求分析やモデリングのやり方,アプリケーションアーキテクチャの考え方,フレームワークの活用についても,オブジェクト指向の観点から説明します。
また,業務アプリケーションではデータベース,画面インターフェース,外部システム連携も重要な設計課題です。これらについても,オブジェクトの設計と関連づけながら,変更に対応しやすい設計の考え方とやり方を説明します。
そして最後に,参考書籍を紹介しながら,オブジェクト指向設計の学び方/教え方についても取り上げます。
なお,本書のサンプルコードのプログラミング言語はJava,アプリケーションフレームワークはSpring FrameworkとSpring Bootです。ほかの言語やフレームワークを使っている方には,理解しにくい箇所があるかと思いますが,本書で説明する設計の考え方とやり方は,言語やフレームワークの違いを超えて活用できる部分があるはずです。
オブジェクト指向の開発は,分析から実装までが継ぎ目のない一貫した活動です。分析/設計/実装をうまく関係づけることで,ソフトウェアの変更を楽で安全にできるのです。ソフトウェアの変更が楽で安全になれば,開発のやり方が変わります。
みなさんが本書を手がかりに,変更が楽で安全になるオブジェクト指向らしい設計にチャレンジし,その効果を実感していただく助けになればうれしい限りです。
(本書「はじめに」より)

目次

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

なぜソフトウェアの変更は大変なのか

  • ソフトウェアの変更に立ち向かう
  • 変更が大変なプログラムの特徴
  • 変更するたびに変更が大変になる

プログラムの変更が楽になる書き方

  • わかりやすい名前を使う
  • 長いメソッドは「段落」に分けて読みやすくする
  • 目的ごとに変数を用意する
  • メソッドとして独立させる
  • 異なるクラスの重複したコードをなくす
  • 狭い関心事に特化したクラスにする
  • メソッドは短く,クラスは小さく

小さなクラスでわかりやすく安全に

  • データとロジック
  • 基本データ型の落とし穴
  • 値の範囲を制限してプログラムをわかりやすく安全にする
  • 「値」を扱うための専用のクラスを作る
  • 値オブジェクトは「不変」にする
  • 「型」を使ってコードをわかりやすく安全にする

複雑さを閉じ込める

  • 配列やコレクションはコードを複雑にする
  • コレクション型を扱うコードの整理
  • コレクション型を扱うロジックを専用クラスに閉じ込める
  • コレクションオブジェクトを安定させる
  • コレクションオブジェクトは業務の関心事

第1章のまとめ

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

プログラムを複雑にする「場合分け」のコード

  • 区分や種別がコードを複雑にする
  • 判断や処理のロジックをメソッドに独立させる
  • else句をなくすと条件分岐が単純になる
  • 複文は単文に分ける
  • 区分ごとのロジックを別クラスに分ける
  • 区分ごとのクラスを同じ「型」として扱う
  • 区分ごとのクラスのインスタンスを生成する
  • Javaの列挙型を使えばもっとかんたん
  • 区分ごとの業務ロジックを区分オブジェクトで分析し整理する
  • 状態の遷移ルールをわかりやすく記述する

第2章のまとめ

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

データとロジックを別のクラスに分けることがわかりにくさを生む

  • 業務アプリケーションのコードの見通しが悪くなる原因
  • データクラスを使うと同じロジックがあちこちに重複する
  • データクラスを使うと業務ロジックの見通しが悪くなる
  • 共通機能ライブラリが失敗する理由
  • 業務ロジックをわかりやすく整理する基本のアプローチ
  • 【COLUMN】 データクラスが広く使われているのはなぜか

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

  • 業務ロジックを重複させないためにはどう設計すればよいか
  • メソッドをロジックの置き場所にする
  • 業務ロジックをデータを持つクラスに移動する
  • 使う側のクラスに業務ロジックを書き始めたら設計を見直す
  • メソッドを短く書くとロジックの移動がやりやすくなる
  • メソッドは必ずインスタンス変数を使う
  • クラスが肥大化したら小さく分ける
  • パッケージを使ってクラスを整理する

三層の関心事と業務ロジックの分離を徹底する

  • 業務ロジックを小さなオブジェクトに分けて記述する
  • 業務ロジックの全体を俯瞰して整理する
  • 三層+ドメインモデルで関心事をわかりやすく分離する

第3章のまとめ

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

ドメインモデルの考え方を理解する

  • ドメインモデルで設計すると何がよいのか
  • ドメインモデルの設計は難しいのか
  • 利用者の関心事とプログラミング単位を一致させる
  • 分析クラスと設計クラスを一致させる
  • 業務に使っている用語をクラス名にする
  • データモデルではなくオブジェクトモデル
  • ドメインモデルとデータモデルは何が違うのか
  • なぜドメインモデルだと複雑な業務ロジックを整理しやすいのか

ドメインモデルをどうやって作っていくか

  • 部分を作りながら全体を組み立てていく
  • 全体と部分を行ったり来たりしながら作っていく
  • 重要な部分から作っていく
  • 独立した部品を組み合わせて機能を実現する
  • ドメインオブジェクトを機能の一部として設計しない

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

  • 重要な関心事や関係性に注目する
  • 業務の関心事を分類してみる
  • コトに注目すると全体の関係を整理しやすい
  • コトは業務ルールの宝庫
  • 何でも約束してよいわけではない
  • 期待されるコト,期待されていないコト
  • 業務ルールの記述 ~手続き型とオブジェクト指向の違い

業務の関心事の基本パターンを覚えておく

  • ドメインモデルで開発してもトランザクションスクリプトになりがち
  • 業務ルールを記述するドメインオブジェクトの基本パターン

ドメインオブジェクトの設計を段階的に改善する

  • 組み合わせて確認しながら改良する
  • 業務の言葉をコードと一致させると変更が楽になる
  • 業務を学びながらドメインモデルを成長させていく

業務の理解がドメインモデルを洗練させる

  • 業務知識を取捨選択し,重要な関心事に注力して学ぶ
  • 業務知識の暗黙知を引き出す
  • 言葉をキャッチする
  • 重要な言葉を見極めながらそれをドメインモデルに反映していく
  • 形式的な資料はかえって危険
  • 言葉のあいまいさを具体的にする工夫
  • 基本語彙を増やす努力
  • 繰り返しながらしだいに知識を広げていく
  • 改善を続けながらドメインモデルを成長させる

第4章のまとめ

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

ドメインオブジェクトを使って機能を実現する

  • アプリケーション層のクラスの役割
  • 三層+ドメインモデルの構造をわかりやすく実装する
  • サービスクラスの設計はごちゃごちゃしやすい

サービスクラスを作りながらドメインモデルを改善する

  • 初期のドメインモデルは力不足
  • ドメインモデルを育てる

画面の多様な要求を小さく分けて整理する

  • プレゼンテーション層に影響される複雑さ
  • 小さく分ける
  • 小さく分けたサービスを組み立てる
  • 利用する側と提供する側の合意を明確にする
  • シナリオクラスの効果

データベースの都合から分離する

  • データベースの入出力に引っ張られる問題
  • データベース操作ではなく業務の関心事で考える
  • 実際のデータベース操作とリポジトリを組み合わせる
  • サービスクラスの記述をデータベース操作の詳細から解放する

第5章のまとめ

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

テーブル設計が悪いとプログラムの変更が大変になる

  • データの整理に失敗しているデータベース
  • 用途がわかりにくいカラム
  • いろいろな用途に使う巨大なテーブル
  • テーブルの関係がわかりにくい

データベース設計をすっきりさせる

  • 基本的な工夫を丁寧に実践する
  • NOT NULL制約が導くテーブル設計
  • 一意性制約でデータの重複を防ぐ
  • 外部キー制約でテーブル間の関係を明確にする

コトに注目するデータベース設計

  • 業務アプリケーションの中核の関心事は「コト」の管理
  • ヒトやモノとの関係を正確に記録するための3つの工夫

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

  • コトの記録に注力したテーブル設計の問題
  • 状態の参照
  • UPDATE文は使わない
  • 残高更新は同時でなくてもよい
  • 残高更新は1ヵ所でなくてもよい
  • 派生的な情報を転記して作成する
  • コトの記録から状態を動的に導出する

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

  • オブジェクトとテーブルは似てくる
  • 違うものとして明示的にマッピングする
  • オブジェクトはオブジェクトらしく,テーブルはテーブルらしく
  • 業務ロジックはオブジェクトで,事実の記録はテーブルで

第6章のまとめ

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

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

  • 画面にはさまざまな利用者の関心事が詰め込まれる
  • 画面に引きずられた設計はソフトウェアの変更を大変にする
  • 関心事を分けて整理する

画面の関心事を小さく分けて独立させる

  • 複雑な画面は異なる関心事が混ざっている
  • 小さな単位に分けて考える
  • 画面も分けてしまう
  • タスクベースのインターフェースが増えている2つの理由
  • タスクベースに分ける設計が今後の主流

画面とドメインオブジェクトを連動させる

  • 画面もドメインオブジェクトも利用者の関心事のかたまり
  • ドメインオブジェクトと画面の食い違いは設計改善の手がかり
  • ドメインオブジェクトに書くべきロジック
  • HTMLのclass属性をドメインオブジェクトから出力する

画面(視覚表現)とソフトウェア(論理構造)を関係づける

  • 項目の並び順とドメインオブジェクトのフィールドの並び順
  • 画面項目のグルーピング
  • 画面のデザインとソフトウェアの設計を連動させながら洗練させていく
  • 画面以外の利用者向けの情報もソフトウェアと整合させる

第7章のまとめ

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

アプリケーションとアプリケーションをつなぐ

  • ほかのアプリケーションとの連携がアプリケーションの価値を高める
  • アプリケーションを連携する4つのやり方

Web APIのしくみを理解する

  • HTTP通信を使ったアプリケーション間の連携の4つの約束事
  • 要求の対象を指定する
  • 要求の種類を指定する
  • エラー時の約束事

良いWeb APIとは何か

  • 使いにくいWeb API ~大は小を兼ねるのか?
  • アプリケーションを組み立てるための部品を提供する

発展性に富んだAPI開発のやり方

  • 単純なことをかんたんにできるAPIの提供から始める
  • 動かしながら設計を発展させていく
  • APIを利用する側とAPIを提供する側の共同作業の環境を整える
  • 中核となるAPIのセットを設計する
  • Web APIのバージョン管理
  • APIを複合したサービスの提供

ドメインオブジェクトとWeb API

  • データ形式とドメインオブジェクトを変換する際に起こる不一致
  • 導出結果か生データか

複雑な連携に取り組む

  • 共通部分と個別対応部分を明確にする
  • APIを進化させる
  • 小さなアプリケーションに分けて組み合わせる
  • 複雑なデータの交換
  • 非同期メッセージングを使ったアプリケーション間連携

第8章のまとめ

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

開発の進め方はオブジェクト指向で変わったのか

  • 開発の基本はV字モデル
  • 短期間で開発し修正と拡張を繰り返すことが重要になった
  • オブジェクト指向の開発はうまくいっているのか
  • どちらのやり方でも変更がやっかいなソフトウェアが生まれやすい

ドメインモデルを中心にしたソフトウェア開発の進め方

  • 業務ロジックに焦点を当てて開発を進める

ソースコードを第一級のドキュメントとして活用する

  • 多くのドキュメントは不要になる
  • 重要になる活動
  • 更新すべきドキュメント
  • 全体を俯瞰するドキュメントを作成して共有する
  • 技術方式のドキュメントもソースコードで表現する
  • 非機能要件はテストコードで表現する

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

  • 見積もりと契約
  • 進捗の判断
  • 品質保証
  • 要員と体制

第9章のまとめ

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

オブジェクト指向を学ぶハードル

  • オブジェクト指向の説明は意味が不明
  • なぜオブジェクト指向で設計すると良いのかがわからない
  • オブジェクト指向をどうやって学ぶか

既存のコードを改善しながらオブジェクト指向設計を学ぶ

  • 実際のコードで設計の違いを知る
  • 重複したコード
  • 長いメソッド
  • 巨大なクラス
  • リファクタリングは部分的に少しずつ
  • 組み立てやすい部品に改善する
  • 設計は少しずつ改良を続ける

オブジェクト指向らしい設計を体で覚える

  • 古い習慣から抜け出すためのちょっと過激なコーディング規則

オブジェクト指向の考え方を理解する

  • 『実装パターン』
  • 『オブジェクト指向入門』
  • 『ドメイン駆動設計』

第10章のまとめ

  • 参考文献一覧
  • 索引

サポート

正誤表

本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。

(2023年11月21日最終更新)

P.57 「リスト 子供連れの団体の料金の合計」を下記に差し替え

class Reservation {
     List<Fee> fees; // 大人と子供の内訳は不明

     Reservation(List<Fee> fees) {
         this.fees = fees;
     }

     Reservation addFee(Fee fee) { // 大人と子供を意識しない
         List<Fee> result = new ArrayList<>(fees);
         return new Reservation(result.add(fee));
     }

     Yen feeTotal() {
         Yen total = new Yen(0); // 合計ゼロ円
         for( Fee each : fees ) {
             total = total.add( each.yen() );
         }
         return total;
     }
}

(以下2023年4月3日更新)

P.42 上から4~5行目にかけて

~元のメソッドの記述がシンプルなります。
~元のメソッドの記述がシンプルなります。

(以下2020年3月26日更新)

P.124 下から3行目

業務業務ロジック
業務ロジック

「業務」が重複していました。

(以下2019年10月23日更新。これ以下は第3刷にて反映済み)

P.11 「第8章のまとめ」の2行上

複雑なデータの交換
構造が複雑なデータの交換をどうするか

P.215 上から10行目

ドメインオブジェクト側にclass属性を返すメソッドを用意するやり方で、~
ドメインオブジェクトが状態を表す情報を返し、それをclass属性で利用するやり方で、~

P.215 リスト「ドメインオブジェクトが論理的な属性を返す」の3行目


return "";


return "read";

P.215 リスト「ドメインオブジェクトが論理的な属性を返す」の最終行


// <p class="${mail.readStatus}">


// <p class="${mail.readStatus()}">

P.215 下から7行目

ドメインオブジェクトがHTMLのclass属性の値を返すこのやり方は、~
ドメインオブジェクトの返す情報をclass属性として利用するこのやり方は、~

P.264 上から9行目

複雑なデータの交換
構造が複雑なデータの交換をどうするか

P.264 上から16行目(箇条書きから3行上)

JSONよりもXMLが有力な選択肢となります。
JSONだけでなくXML選択肢となります。

P.265 上から6行目

そもそも、オブジェクト指向設計の観点から言えば、基本の選択肢はJSONではなくXMLです。設計が変化してくことも重視すると、JSONよりは、XMLのほうが、対応が楽で安全です。
複雑な構造のオブジェクトをそのまま表現することを重視すれば、基本の選択肢はJSONよりもXMLです。構造が変化したときに、JSONよりは、XMLのほうが、対応が楽で安全です。

(以下2018年11月6日更新)

P.203 表「小さな単位に分ける」,ドメインオブジェクト項の下から2番目

ConatctTo
ContactTo

(以下2017年10月25日更新)

P.37 リスト「独自の型を使って意図を明らかにする」の2行目


if(quantity.isDiscountable)


if(quantity.isDiscountable())

P.58 リスト「if文を使わずに区分ごとのオブジェクトを生成するやり方の例」のstatic{}内


static
{
     types.put( "adult", new AdultFee());
     types.put( "child", new ChildFee());
}


static
{
     types = new HashMap<String, Fee>();
     types.put( "adult", new AdultFee());
     types.put( "child", new ChildFee());
}

P.111 上から4行目

判断/加工/処理
判断/加工/計算

P.144 下から9行目

判断/加工/計算処理
判断/加工/計算

(以下,2017年10月23日更新)

P.31 リスト「正しい数量を扱うための独自クラス(Quantityクラス)を定義する」内,31ページ上から5行目


throw new IllegalArgumentException("不正:合計が" + 100 + "以上");


throw new IllegalArgumentException("不正:合計が" + MAX + "");

P.43  リスト「コレクション操作の結果を同じ型のコレクションオブジェクトを作って返す(良い例)」の6行目


return new Customers(result.add(customer));



result.add(customer);
return new Customers(result);


(以下2017年8月10日更新)

P.34 上から8行目

~特定にクラスに集めて整理しておけば、~
~特定クラスに集めて整理しておけば、~

P.66 上から3行目

次のようなルール宣言的に~
次のようなルール宣言的に~

P.69 表「データクラスのいろいろな呼び方」内,Formクラス項目の説明

画面とデータとやりとりするための~
画面とデータやりとりするための~

P.165 下から9行目

契約による設計と対象的な技法が~
契約による設計と対的な技法が~

P.166中段 上から14行目

~設計が重要なテーマです。
~設計重要なテーマです。

P.170 下から4行目

~具体的にどやってデータベースで実現するかは、~
~具体的にどやってデータベースで実現するかは、~

P.171 「第5章のまとめ」の5行目

~シンプル保つための設計の徹底が重要
~シンプル保つための設計の徹底が重要

P.181 下から12行目

一意性制約とNOT NUL制約を徹底すれば、~
一意性制約とNOT NULL制約を徹底すれば、~

(以下,2017年7月26日更新。これ以下は第2刷にて反映済み)

P.17 表「名前の違い」の3行目

unitPrice;
int unitPrice;

P.148 「第4章のまとめ」の5行目

起きてよいこと/起きてはいけなくこと
起きてよいこと/起きてはいけなこと

P.215 リスト「ドメインオブジェクトが論理的な属性を返す」の最終行


// <p class="${mail.readStatus}>


// <p class="${mail.readStatus}">

P.234 リスト「GETリクエストで返されるレスポンスデータの例(JSON形式)」の2行目


"id" : "1234"


"id" : "1234",

P.245 リスト「単純な用途を実現するAPI」の5行目

"name":" 田中太郎"


"name":" 田中太郎",

(2017年7月6日更新)

P.63 図2-2

誤

正

「審査中」と「差し戻し中」の間の,「再申請」に相当する左向き矢印(赤丸で囲った部分)が抜けていました。

商品一覧