More Related Content
Similar to ドメインオブジェクトの見つけ方・作り方・育て方 (20)
ドメインオブジェクトの見つけ方・作り方・育て方
- 12. 機能分割:機能クラスとデータクラス
• C言語や COBOLの設計スタイルを、Java に流用
• JavaBeans getter/setter
– データの入れ物クラスの設計パターン
– get する側のクラスに機能を記述する
• JPA @Entity
– Java Beans をテーブルとのマッピングに利用
(データモジュール)
– 機能は、ロジッククラスに記述する
(機能モジュール)
• トランザクションスクリプト
• ファットコントローラ/ファットサービス
- 14. 抽象データ型の例
内部のデータ表現 公開メソッド(やってほしいこと)
String char[] value String substring()
String[] split()
String replace()
…
BigDecimal BigInteger intVal ( ⇒ int[] )
int scale
BigDecimal add()
BigDecimal[] divideAndRemainder()
BigDecimal setScale()
…
LocalDate int year
short month
short day
LocalDate plusDays()
LocalDate minusMonths()
boolean isBefore()
…
ArrayList Object[] elementData
int size
boolean add()
boolean contains()
…
- 18. オブジェクト指向エクササイズ
1. 1つのメソッドにインデントは1段階まで
2. else 句は使わない
3. すべての基本データ型をラップする
4. 1行につきドットはひとつまで
5. 名前を省略しない
6. 50行を超えるクラスを作らない
7. 1つにクラスはインスタンス変数は2つまで
8. ファーストクラスコレクションを使う
9. getter / setter を使わない
このガイドラインにそってコードを書けばモジュール性を向上できる
- 19. リファクタリング いやな臭い
1. 重複したコード
2. 長いメソッド
3. 大きなクラス
4. 多すぎる引数
5. 基本データ型への執着
6. データクラス
7. switch 文
8. コメント
…
モジュール性が悪くなっている明らかな兆候
リファクタリングによる改善
・メソッドの抽出
・クラスの抽出
・メソッドの移動
・メソッドオブジェクト
一つのメソッドにインデントは1段階まで
50行を超えるクラスを書かない
ひとつのクラスのインスタンス変数は2つまで
すべての基本データ型をラップする
getter/setter を書かない
ファーストクラスコレクションを使う
else句を使わない
- 20. ドメイン駆動設計 しなやかな設計
1. 意図の明確なインタフェース
2. 副作用のない関数
3. 表明
4. 概念の輪郭
5. 独立したクラス
6. 閉じた操作
10章の6つのパターン
わかりやすい
組み立てやすい
分けやすさ
保護性
わかりやすい
連続性(モデルと実装の構造の一致)
モジュール性を改善するための基本テクニック
5章の 値オブジェクトの設計パターンでもある
- 34. 関心事の種類と見つけ方
Aspect
知りたいこと
それが何であるか
which, what, when, where
画面や帳票から具体的に見つけやすい
Why
6つの関係
約束と履行
方針と規則
判断や行動の理由づけ
業務知識の中核
見つけにくい
ここを深く理解し、コードでうまく表現するこ
とがドメイン駆動設計の勝負どころ
How
記録と参照
約束と履行の監視 業務の基本
要求は表面的
より突っ込んだ設計が必要
勘定パターンを参考に
通知 受信と送信
業務の流れ
具体的な要求として見つかりやすい
whyの
実行手段
性状
枠組み
制約
- 35. 性状(aspect) : 知りたいこと
which 識別
Identity 名前、番号
Category 名前
Type 名前
what 値
数量、金額、率
説明、注釈
状態
when 時
日付
期間
where 位置
Address
Location
Area
物理、論理、仮想
知りたいことに「名前」をつけ
基本データ型(文字列型、数値型、日付型)で表現する
それが、値オブジェクト
絶対/相対
- 41. オブジェクト指向エクササイズ
1. 1つのメソッドにインデントは1段階まで
2. else 句は使わない
3. すべての基本データ型をラップする
4. 1行につきドットはひとつまで
5. 名前を省略しない
6. 50行を超えるクラスを作らない
7. 1つにクラスはインスタンス変数は2つまで
8. ファーストクラスコレクションを使う
9. getter / setter を使わない
このガイドラインにそってコードを書けばモジュール性を向上できる
- 42. getter を使わない
• getter がないということは、データを持つオブ
ジェクトに仕事を依頼するしかない
– get して自分で判断・加工・計算するのはNG
• 基本データ型はすべてラップする
• クラスは、最大2つのインスタンス変数しか持
ていない
つまり
• 基本データ型を1つか2つもった、50行以内
のクラスに、業務ロジックを書く
- 45. ドメイン駆動設計 しなやかな設計
1. 意図の明確なインタフェース
2. 副作用のない関数
3. 表明
4. 概念の輪郭
5. 独立したクラス
6. 閉じた操作
値オブジェクトの設計パターン
判断・加工・計算を依頼する
メソッド名
言語の標準ライブラリのみ依存
業務の用語と値オブジェクトの名前を一致させる
完全コンストラクタ
不変オブジェクト
契約による設計
メソッドが返す型、受け取る型を
自分と同じ型にする
BigDecimal, LocalDate, String は、ある程度、この条件を満たしている
ただし、扱えるデータ範囲が広すぎ
クラス名やメソッド名が漠然としていて意図が不明確
- 46. ドメインオブジェクトの作り方
• String, BigDecimal, LocalDate を一つか二つ持った
「値オブジェクト」を作る
– クラス名で役割を明確に
– 公開メソッドを少数に限定して意図を明確に
– メソッド名で意図を明確に
– 内部で保持するデータの範囲を限定して意図を明確に
– 事前条件、事後条件、不変条件を明確に
• null を渡さない、戻さない
• ゼロ割り算をしない
• 表明 and/or 例外のスロー
- 51. public class UserIdentity {
@NotBlank(message = "メールアドレスを入力してください")
@Email(message = "メールアドレスが正しくありません。")
String mail;
public UserIdentity(@NotNull String mail) {
this.mail = mail;
}
public String mail() {
return mail; // ローカル部(@の前) だけ返すとか、さんづけとか…
}
@Override
public String toString() {
return mail;
}
}
Mail に型づけされた文字情報
データ表現として、InternetAddress を使うのも選択肢
- 52. public class DateOfBirth {
@NotNull(message = “誕生日を入力してください。”)
LocalDate date ; // データ表現として 文字列ではなく、LocalDate型を使う
String source; // 入力値の保持
boolean valid = true; // 入力値の有効性
public DateOfBirth(@NotNull String source) {
this.source = source;
try { LocalDate.parse(source); }
catch ( DateTimeException exception) { valid = false; }
}
public Age age() {
return new Age(this.date); // 年齢計算や表記は Age にまかせる
}
@AssertTrue(message = "日付が正しくありません。")
public boolean isValid() {
if(source.equals("")) return true;
return valid;
}
@Override
public String toString() {
return source;
}
}
- 61. フレームワークの都合を排除する
• O-R マッピング
MyBatis SQL Mapper
– ダイレクトフィールドアクセス ( getter/setter 不要)
– 構造変換は、path 記法 ( plan.location.primary )
– null はマッピングされない
• フォームバインディング
Spring MVC DataBinder
– ダイレクトフィールドアクセスに設定 (getter/setter を使わない)
– 構造変換は、path 記法
– nullはバインドの対象外にする
• O-JSON マッピング
Jackson ObjectMapper
– ダイレクトフィールドアクセスに設定
– 構造変換は、View クラスにやらせる
• ドメインオブジェクトはどのような構造に変換されるか知らない
- 62. public class CustomObjectMapper {
public ObjectMapper ofDirectFieldAccess() {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD,JsonAutoDetect.Visibility.NON_PRIVATE);
mapper.setVisibility(PropertyAccessor.GETTER,JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.SETTER,JsonAutoDetect.Visibility.NONE);
return mapper;
}
}
@ControllerAdvice
public class BinderAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.initDirectFieldAccess();
binder.registerCustomEditor(Object.class, new StringTrimmerEditor(true));
// ブランク文字列は、null とする
// バインドの対象外となる
}
}
ダイレクトフィールドアクセスの設定
- 65. 育て方のシナリオ
1. 最初は器だけのクラスを作る
– とりあえずのひな型8点セット
– メモ代わりに作られたクラス名だけのクラス
– コンストラクタと toString() だけの値オブジェクト
・・・
2. 動かすために判断・加工・計算ロジックを書いていく
– 理想:よく置き場所を考えて書く
– 現実:てっとりばやく動かせる場所に書く
3. コードが増え、ごちゃごちゃしてくるので整理する
– ドメインオブジェクトの持つデータを使って、判断・加工・計算してい
るロジックを見つける(一行の式かもしれない)
– そのロジック(計算・加工・判断)に名前をつける
– 適切な場所にロジックを移動する(リファクタリング)
- 79. 区分オブジェクト
• 振る舞いを持った Enum
• 区分ごとのロジックを別クラスに記述
• Java言語使用に組み込まれた
Strategy/Stateパターン
説明とコード例は、googleで
「場合わけの書き方あれこれ」で検索。
または「オブジェクト指向をきちんと使
いたいあなたへ」
場合ごとのビジネスルールの表現
79
- 80. 区分オブジェクトの効果
• 関心事の明示的なコード表現
• 複雑な if文記述の解消
– 区分ごとの分岐記述( if文 or switch文 )は、一箇所
になる
– 場合によっては、まったく書かなくてよくなる
• どこに何が書いてあるかわかりやすくなる
– 区分ごとの業務ルールや知識のロジックの置き場所
をクラス単位で分離
• 区分の追加や削除をした時の、変更の副作用が
少ない
80
9章 概念を掘り出す
10章 概念の輪郭
- 82. 振る舞いを豊かにするメソッド候補
視点 例
文字列で表現する
show(), asText(), inThousand(),
shortName() , fullName(), …
文字列から生成する parse(), of(), from(), …
同一性の判定 isSame(), equalsIgnoreCase(), …
比較・順序づけ compareTo(), before(), after(), …
計算 plus(), minus(), next(), previous(), with(), …
列挙 values(), contains(), valueOf(), …
上限・下限 max(), min(), within(), ….
元ネタは Haskell の組み込みの型クラス