Upgrade to Pro — share decks privately, control downloads, hide ads and more …

これだけは知っておきたいクラス設計の基礎知識

 これだけは知っておきたいクラス設計の基礎知識

JJUG CCC 2023 Spring 発表資料(ステップアップセッション)。
私がクラス設計をするときに重視している考え方とやり方を紹介。
主な内容
・クラス設計のスキル 3段階
・クラス設計の技能を習得するシナリオ
・7つの基礎知識
① 入出力と計算判断
② プログラムの中核と周辺
③ モジュラー性
④ データ抽象
⑤ カプセル化
⑥ 契約プログラミング
⑦ 不変(イミュータブル)

増田 亨
PRO

June 04, 2023
Tweet

More Decks by 増田 亨

Other Decks in Programming

Transcript

  1. これだけは知っておきたい
    クラス設計の基礎知識
    有限会社システム 増田 亨
    JJUG CCC 2023 Spring Step Up Session
    2023年6月4日

    View Slide

  2. アプリケーション開発者
    業務系アプリケーション
    ドメイン駆動設計/リファクタリング
    Java/Spring/IntelliJ IDEA/JIG
    著書『現場で役立つシステム設計の原則』
    ~変更を楽で安全にするオブジェクト指向の実践技法
    設計コミュニティ「現場から学ぶモデル駆動の設計」主催
    自己紹介
    2023/6/4 2

    View Slide

  3. ソフトウェアの設計
    2023/6/4 3
    プログラム
    永続化
    通信
    ⇒ クラス設計
    ⇒ テーブル設計
    ⇒ プロトコル設計

    View Slide

  4. ステップアップする
    2023/6/4 4

    View Slide

  5. クラス設計のスキル 3段階
    初級 クラス構文を使って読み書きができる
    中級 既存のクラス設計を参考に
    なんとなく真似ができる
    上級
    なぜそういう設計をするのか
    なぜそういう設計を避けるか
    複数の判断軸を持っている
    2023/6/4 5

    View Slide

  6. クラス設計 技能習得のシナリオ
    2023/6/4 6
    情報 ⇒ 知識 ⇒ 技能
    クラス設計の
    参考情報
    クラス設計の
    知識
    クラス設計の
    技能
    外部 自分
    今日の内容
    書籍
    ネットワーク
    外部から得た情報を
    自分の体験や知識と
    関連づける
    手を動かして習熟する
    繰り返して体で覚える
    理屈ではなく感覚

    View Slide

  7. プログラムの設計
    分けてつなぐ
    2023/6/4 7

    View Slide

  8. プログラムの設計 「分けて」整理する
    コードを整理する
    どうやって?
    クラス コード整理の基本単位
    メソッド 文(ステートメント)を集めて整理する
    パッケージ クラスを集めて整理する
    2023/6/4 8

    View Slide

  9. プログラムの設計 「つないで」動かす
    プログラムを動かす
    どうやって?
    メソッドを定義して公開する つないで動かす唯一の仕組み
    オブジェクトを生成してメソッドを呼び出す
    • メソッドを呼び出す側 利用者
    • メソッドを提供する側 提供者
    2023/6/4 9

    View Slide

  10. 分けてつなぐための7つの基礎知識
    ① 入出力と計算判断
    ② プログラムの中核と周辺
    ③ モジュラー性
    ④ データ抽象
    ⑤ カプセル化
    ⑥ 契約プログラミング
    ⑦ 不変(イミュータブル)
    2023/6/4 10
    この発表から入手した情報を
    まず自分の経験・知識と関連づける
    そのあと、手を動かして体で覚える

    View Slide

  11. 分けて整理する
    2023/6/4 11

    View Slide

  12. ① 入出力と計算判断
    ➢入出力と計算判断を分ける。これがクラス設計の鉄則。
    • 同じクラスに入出力と計算判断を混ぜない
    • 同じパッケージに入出力と計算判断をまぜない
    ➢入出力を特定範囲(クラス/パッケージ)に閉じ込める
    ➢入出力クラスは計算判断クラスを使ってよい
    ➢計算判断クラスは入出力クラスを使ってはいけない
    2023/6/4 12

    View Slide

  13. ① 入出力と計算判断 (続き)
    • 計算判断クラスの例
    • LocalDate
    • BigDecimal
    • String
    • ArrayList
    • 入出力クラスの例
    • PrintStream (System.out、System.err)
    • java.io.*, java.nio.*, java.net.*, java.sql.*, …
    • @Controller, @Repository, @Entity, …
    2023/6/4 13
    計算判断のための専用クラス
    (入出力から隔離する)
    入出力は特定のクラス/パッケージに
    分離して閉じ込める

    View Slide

  14. ② プログラムの中核と周辺
    2023/6/4 14
    業務視点の
    クラス群
    中核
    通信アダプター
    HTTP
    @Controller
    @RestController
    RestTemplate
    通信アダプター
    JMS
    @JmsListner
    JmsTemplate
    永続化アダプター
    JDBC
    @Repository
    @Entity
    周辺
    周辺 スケジュール
    アダプター
    @Scheduled
    周辺
    周辺
    関連する考え方:『ドメイン駆動設計』「ドメインを隔離する」; ヘキサゴナル(Ports & Adapters)アーキテクチャ
    アプリケーションの全体像

    View Slide

  15. ② プログラムの中核と周辺(続き)
    2023/6/4 15
    中核と周辺の分離シナリオ
    いろいろな
    関心事
    いろいろな
    関心事
    業務視点の
    計算判断クラス
    (業務ロジック)
    いろいろな
    関心事
    業務視点の
    計算判断クラス
    (業務ロジック)
    業務視点の
    入出力クラス
    記録/参照、通知/転送
    業務視点の
    計算判断クラス
    (業務ロジック)
    通信
    アダプター
    永続化
    アダプター
    設計 設計 設計
    業務視点の
    入出力クラス
    記録/参照、通知/転送
    中核 中核 中核
    中核 中核
    周辺 周辺 周辺
    ライブラリやフレームワークを
    アダプターの設計と実装に活用する

    View Slide

  16. ② プログラムの中核と周辺(続き)
    ➢書籍『ドメイン駆動設計』ドメインを隔離する
    • ソフトウェアの複雑さは対象となる業務活動に起因する
    • その複雑さをうまく扱う工夫の経験談
    ➢書籍『リファクタリング』
    • サンプルコードは(意図的に)業務視点のクラスになっている
    • 業務視点のクラスの不吉な臭いと改善方法の経験則
    ➢ヘキサゴナル(Ports & Adapters)アーキテクチャ
    • 業務視点のクラスをフレームワーク・UI・データベースから独立させる
    2023/6/4 16
    業務視点でクラスを設計する 関連する情報

    View Slide

  17. 分けてつなぐ
    2023/6/4 17

    View Slide

  18. ③ モジュラー性
    ➢いろいろな部品
    • パーツ 断片なども含む(部分)
    • コンポーネント 組み立て(一回の結合)を意図した部品
    • モジュール 「組み換え」を意図した部品
    ➢モジュラー性の特徴
    • 用途がわかりやすい(単体で独立した機能)
    • モジュールとモジュールをつなぎやすい
    • モジュールの修正や組み換えが楽で安全
    ➢モジュラー性を向上することがよいクラス設計の基本
    2023/6/4 18

    View Slide

  19. ③ モジュラー性(続き)
    2023/6/4 19
    モジュラー性の着眼点
    業務視点の
    計算判断クラス
    (業務ロジック)
    中核 ① 入出力と計算判断
    ② プログラムの中核と周辺
    この二つの分離原則が重なる場所
    ここのクラス設計をしっかりやると、
    プログラム全体をわかりやすく分けて整理できる
    業務視点の計算判断ロジックが断片化したり重複したりすると
    プログラムの見通しが悪くなり、変更がやっかいで危険になる
    ここのモジュラー性が、事業活動の変化への適応能力に直結する

    View Slide

  20. ④ データ抽象
    ➢データの型を操作(メソッド)の集合で定義する

    • int型 ==, !=, <, >, +,- , *, /, %
    • BigDecimal型 add(), devide(), multiply(), subtract(), …
    • LocalDate型 isAfter(), isBefore(), plus(), minus(), with(), …
    • DayOfWeek型 from(), valueOf(), plus(), minus(), …
    • String型 isEmpty(), length(), substring(), replace(), …
    2023/6/4 20
    計算判断クラスを設計する基本知識
    この考え方とやり方を体で覚える

    View Slide

  21. ④ データ抽象 (続き)
    ➢業務視点でデータ型を発見する(業務知識から設計する)
    • 金額
    • 数量
    • 比率
    • 日付、日数
    • 時刻、時間
    • 範囲 (金額範囲、数量範囲、日付範囲、時刻範囲、… )
    • 区分 (分類区分、状態区分、適用区分、…)
    • 多値 (金額リスト、数量リスト、範囲セット、区分セット、… )
    2023/6/4 21
    計算判断クラスを設計する基本知識

    View Slide

  22. ④ データ抽象 (続き)
    ➢単一値(金額、数量、日付、時刻など)のメソッド候補
    • 同値の判定 isSame()
    • 比較 isGreaterThan()/isLessThan(), isAfter(), isBefore()
    • 加算・減算 add(), plus(), subtract(), minus()
    • 掛算・乗算 multiply() (整数倍と単位付き数量倍の違い)
    • 割算・除算 devide() (整数除算と単位付き割算の違い)
    • 最大・最小 MAX, MIN (有効な範囲)
    • 基本型の変換 toString()/parse(), intValue()/of()
    2023/6/4 22
    計算判断クラスの設計パターン
    LocalDate, BigDecimalなど既存のクラスを参考にする
    業務の関心事を特定し、それらしい名前をつける(日本語推奨)
    手を動かして習熟する

    View Slide

  23. ④ データ抽象 (続き)
    ➢範囲のメソッド候補
    • 範囲の判定 範囲の重なり・連続、前後、包含、…
    • 範囲の合成 二つの範囲を一つにまとめる
    • 部分範囲 一つの範囲の一部を取り出す
    ➢区分のメソッド候補
    • 区分への変換 from(), of(), parse(), …
    • 区分の判定 for()
    • 移動 next(), previous()
    2023/6/4 23
    計算判断クラスの設計パターン
    自分の知識・経験と関連づける
    手を動かして体で覚える

    View Slide

  24. ④ データ抽象 (続き)
    ➢多値(配列や順序)のメソッド候補
    • 絞り込み filter()
    • 変換 map() 多値から別の多値への写像
    • 集約 reduce()
    ➢値(集合)のメソッド候補
    • 和(足し算)
    • 差(引き算)
    • 共通部分
    • 要素を含む
    2023/6/4 24
    計算判断クラスの設計パターン
    自分の知識・経験と関連づける
    手を動かして体で覚える

    View Slide

  25. ⑤ カプセル化
    ➢データ抽象(データ型を操作の集合で定義)をクラスとして実装する
    ➢基本アイデア
    • 値を表現するデータ構造(フィールド一式)を公開メソッドで囲む
    • 実装(ソースコード)は公開する(隠蔽しない)
    • 実装に依存したプログラムを書けないメソッドだけを提供する
    ➢カプセル化の成功例
    • Java 9 でStringクラスの実装は全面的に書き換えられた
    • Stringクラスを使うプログラムに影響はなかった
    2023/6/4 25

    View Slide

  26. つないで動かす
    2023/6/4 26

    View Slide

  27. ⑥ 契約プログラミング
    ➢基本アイデア
    • モジュールをつなぐ唯一の手段はメソッドの公開
    • メソッドの仕様を明確にするとモジュラー性が改善する
    ➢メソッドの仕様を明確にする方法
    • メソッドの利用者と提供者の約束事を明示する
    • 事前条件(利用者の義務) メソッドの引数の型と数
    • 事後条件(提供者の義務) メソッドの返す型
    • 不変条件( 同上 ) インスタンスの状態の整合性の保障
    2023/6/4 27
    モジュラー性を改善する考え方とやり方
    メソッドを契約プログラミングの視点で設計する
    既存のクラス(LocalDate, BigDecimal, Stringなど)のメソッドをこの視点から評価してみる

    View Slide

  28. ⑥ 契約プログラミング(続き)
    ➢金額#割る(数量) ⇒ 単価
    • 端数の処理は型からは判断できない(単価型で有効桁数は定義可能)
    ➢金額#割る(整数) ⇒ 金額
    • ゼロ除算が起きる可能性がある
    • 負の金額になる可能性がある
    • 端数の処理は型からは判断できない
    ➢金額#割る(自然数) ⇒ 金額
    • 端数の処理は型からは判断できない
    2023/6/4 28
    事前条件(引数の型)と事後条件(返す型)の例:金額の割算
    引数の型(事前条件)と返す型(事後条件)
    で計算判断ロジックの仕様を表現する
    おまけ:金額#割る(金額) ⇒ 比率

    View Slide

  29. ⑦ 不変(イミュータブル)
    ➢実行中にモジュールの状態が変わると、使う側の負担が大きい
    • 挙動が不安定になる
    • 防御的なコードが不必要に増殖する
    ➢基本アイデア
    • 一度作ったインスタンスは状態を変えない
    • 状態の変更の代わりに同じ型の別のインスタンスを生成する
    2023/6/4 29
    モジュールのつなぎ方を安定させる
    不変方式のクラスの使い勝手を評価する(String, BigDecimal, LocalDate)
    可変方式のクラスの使い勝手を評価する(StringBuilder, ArrayList, HashSet)

    View Slide

  30. 本日のまとめ
    2023/6/4 30

    View Slide

  31. クラス設計 技能習得のシナリオ
    2023/6/4 31
    情報 ⇒ 知識 ⇒ 技能
    クラス設計の
    参考情報
    クラス設計の
    知識
    クラス設計の
    技能
    外部 自分
    今日の内容
    書籍
    ネットワーク
    外部から得た情報を
    自分の体験や知識と
    関連づける
    手を動かして習熟する
    繰り返して体で覚える
    理屈ではなく感覚

    View Slide

  32. クラス設計 7つの基礎知識
    ① 入出力と計算判断
    ② プログラムの中核と周辺
    ③ モジュラー性
    ④ データ抽象
    ⑤ カプセル化
    ⑥ 契約プログラミング
    ⑦ 不変(イミュータブル)
    2023/6/4 32
    この発表から入手した情報を
    まず自分の経験・知識と関連づける
    そのあと、手を動かして体で覚える
    Any questions?

    View Slide