Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
2025-05-28 社内勉強会 SOLID原則ではじめるよりよい設計の第一歩 / The F...
Search
Kentaro Abe
May 28, 2025
Programming
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
2025-05-28 社内勉強会 SOLID原則ではじめるよりよい設計の第一歩 / The First Step to Better Software Design with SOLID Principles
Kentaro Abe
May 28, 2025
More Decks by Kentaro Abe
See All by Kentaro Abe
2025-08-27 社内勉強会 ソフトウェアテストの基礎 / Basics of Software Testing
abekem
0
31
2025-08-06 社内勉強会 Gitを知る頃 / When You First Know Git
abekem
0
63
2025-07-02 社内勉強会 SQLに親しむ / Getting to Know SQL
abekem
0
71
2025-04-23 社内勉強会 デザインパターン概論 / Overview of Design Patterns
abekem
0
66
2025-03-26 社内勉強会 オブジェクト指向入門 第二部 / Introduction to Object-Oriented Part2
abekem
0
57
SAP Event Meshで始めるイベント・ドリブン・アーキテクチャ / Getting Started with Event-Driven Architecture Using SAP Event Mesh
abekem
0
200
2025-02-27 社内勉強会 オブジェクト指向入門 / Introduction to Object-Oriented
abekem
0
110
Other Decks in Programming
See All in Programming
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
400
Composerを使ったサプライチェーン攻撃の様子を眺めてみる #phpstudy
o0h
PRO
2
250
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.5k
JavaDoc 再入門
nagise
1
340
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
7
4.4k
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
130
New "Type" system on PicoRuby
pocke
1
920
Oxcを導入して開発体験が向上した話
yug1224
4
310
IBM Bobを活用したレガシーアプリの最新化
oniak3ibm
PRO
1
200
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
10
4k
タクシーアプリ『GO』の バックエンド開発のおける AI利活用と若者のすべて
pyama86
3
2k
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
330
Featured
See All Featured
The Curious Case for Waylosing
cassininazir
1
390
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
200
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
290
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.5k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
530
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
320
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
620
The Curse of the Amulet
leimatthew05
1
13k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
410
Transcript
2025/05/28 社内勉強会 SOLID原則ではじめる よりよい設計の第一歩 1
2 • オブジェクト指向でよりよい設計を目指す • 基本方針としてSOLID原則の解説 • 実装例を多めに紹介 What is it?
3 • この資料を作り始めるタイミングでぴっ たりの本を見つけた • 今回はほぼこの本の紹介です ◦ 書籍の実装例はCrystal ◦ 本発表ではJava
参考資料
4 よい設計とは?
• 要件を満たすシステムの構造 を計画・構築する ◦ アーキテクチャ(インフラ/ソフトウェア) ◦ クラス ◦ メソッド •
品質を担保する 5 ソフトウェア設計
6 • 機能の修正が簡単 • 機能の追加が簡単 • 修正範囲が明確 • 影響範囲が明確 •
テストがしやすい • リリーススピードが早い • バグの原因追求がしやすい よい設計の指標(一例) 「よい設計」は状況によって異なる 例えば保守しない案件では、 コードの読みやすさや バグの原因追求のしやすさは 優先度が低いかもしれない
7 • 機能の修正が簡単 • 機能の追加が簡単 • 修正範囲が明確 • 影響範囲が明確 •
テストがしやすい • リリーススピードが早い • バグの原因追求がしやすい 今回考える「よい設計」の指標
8 • 「機能の追加が簡単になるように実装するぞ!」 • 何から着手する? どうやって「よい設計」を実現するのか? ・・・
9 • 「機能の追加が簡単になるように実装するぞ!」 • 何から着手する? • ゴールに向かうために、 もう少し具体的な指針が必要 • そのひとつが「SOLID原則」
どうやって「よい設計」を実現するのか?
10 SOLID原則
11 • オブジェクト指向で用いられる五つの原則の頭字語 • ソフトウェア設計をより平易かつ柔軟にして保守しやすくするもの • Robert C. Martin が提唱した
SOLID原則とは? Robert C. Martin またの名をボブおじさん(Uncle Bob)
12 SOLID原則 略称 英語名称 日本語名称 SRP Single Responsibility Principle 単一責任の原則
OCP Open-Closed Principle 開放閉鎖の原則 LSP Liskov Substitution Principle リスコフの置換原則 ISP Interface Segregation Principle インターフェース分離の原則 DIP Dependency Inversion Principle 依存性逆転の原則
13 • クラスや関数は、ただひとつの機能について責任を持つ • どこまでをひとつの機能とするべきかは状況による ◦ 例)ファイルの読み書きを分けるか? 単一責任の原則 • Single
Responsibility Principle • Open-Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle 変更するための理由が、一つのクラスに対して一つ以上あってはならない いい言葉!
14 • 拡張(機能追加)するときに既存コードを修正するべきでない • すごくざっくり言うと、ポリモーフィズムを駆使して if文を減らそうって話 開放閉鎖の原則 • Single Responsibility
Principle • Open-Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle ソフトウェアの実体(クラス、モジュール、関数など)は、 拡張に対して開かれているべきであり、修正に対して閉じていなければならない
15 • サブクラスは親クラスの代わりとして使えるべき • 主に継承に適用される原則 • かなり理論的な原則で、もう少し細かい定義がある • 今日はあまり扱いません リスコフの置換原則
• Single Responsibility Principle • Open-Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle ある基底クラスへのポインタないし参照を扱っている関数群は、その派生クラスの オブジェクトの詳細を知らなくても扱えるようにしなければならない リスコフさん→
16 • インターフェースはこの機能を保証します、という「約束」 • 保証できない機能の実装を強制してはならない • 小さいインターフェースを組み合わせる インターフェース分離の原則 • Single
Responsibility Principle • Open-Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle 汎用なインターフェースが一つあるよりも、 各クライアントに特化したインターフェースがたくさんあった方がよい
17 • 依存とは「使うこと」 ◦ 使う側が使われる側に依存する • より安定したものに依存しましょう ◦ 安定=変更が少ない ◦
下位より上位が安定 ◦ 具象より抽象が安定 依存性逆転の原則 • Single Responsibility Principle • Open-Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle 上位モジュールはいかなるものも下位モジュールから持ち込んではならない。 双方とも具象ではなく、抽象(インターフェースなど)に依存するべきである
18 各原則の関係 原則 他の原則との関係 SRP 根底となる原則。適切な責務分割で修正範囲を限定する。 OCP クラス設計で目指す形。各クラスの関係性を工夫し 変更容易性を上げる。 LSP
OCPのためのテクニック。サブクラスを交換可能にする方法。 ISP OCPのためのテクニック。クラス間のインターフェース定義の方法。 DIP OCPのためのテクニック。クラス間の依存方向をコントロールする方法。 1. SRPでクラスの責務をひとつにし、修正が他のクラスに及ばないようにする 2. LSP/ISP/DIP を用いて、修正・追加が容易なOCPを目指す
19 単一責任、開放閉鎖は基礎的・ わかりやすいので人気 依存性逆転は難しくてかっこいいの で好まれる (私も投票した) 余談:好きな原則
20 実装例
21 • 設定を表現するクラス ◦ ApiConfig:APIの設定を 保持しているクラス ◦ DbConfig:DBの設定を 保持しているクラス ◦
RedisConfig:Redisの 設定を保持しているクラス 本発表で扱う問題 • 設定クラスを利用するクラス ◦ Api:外部APIを呼び出すクラス ◦ Db:DBからデータ取得するクラス ◦ Redis: Redisからデータ取得するクラス • データを取得して処理するクラス ◦ UserRepository: Api/Db/Redis などのクラスを 使ってデータを処理する ユーザーの情報を 外部APIやDBから取得するシステム
22 問題設定 • API、DBそれぞれにjson形式の設定ファイルがある • 設定ファイルの中身は、どちらもidとpasswordがある わるい設計 • 設定ファイルのidとpasswordを返すConfigManager •
idとpasswordを取得したいのは同じなので、ConfigManagerとして共通 化 1. 単一責任の原則( SRP) api_config.json db_config.json 変更するための理由が、一つのクラスに 対して一つ以上あってはならない
23 1. 単一責任の原則 typeに渡す文字列で 処理を分岐 idとpasswordの 取得は共通 DBクラスは省略 Main.java
24 1. 単一責任の原則 Main.java
25 1. 単一責任の原則 APIクラスからConfigManagerク ラスへの依存 Main.java
26 1. 修正が伝播する ◦ 片方の責務の修正によって、もう片方にも修正が必要になる 2. 可読性とテスト容易性が低い ◦ 可読性が悪く、テストが書きづらい わるい設計の問題点
変更するための理由が、一つのクラスに 対して一つ以上あってはならない
27 問題はConfigManager 変更理由としてあり得るもの • api_config.jsonの形式が変わる • db_config.jsonの形式が変わる これはSRPに違反している 修正が伝播する 変更するための理由が、一つのクラスに
対して一つ以上あってはならない
28 修正が伝播する api_config.jsonのkey名が 変更されたら、、、 ConfigManagerにも修正が 必要になって、、、 db_config.jsonに修正が 伝播する! 変更するための理由が、一つのクラスに 対して一つ以上あってはならない
29 可読性・ テスト容易性が低い 変更するための理由が、一つのクラスに 対して一つ以上あってはならない • typeの値によってConfigManagerの意味が変わる ◦ 処理を追わないと何が起こるかわからない ◦
可読性が低い状態 • 機能追加で、typeの種類が増えるかも ◦ あり得るパターンが増えると その動作を担保するテストも必要になる
30 1. プロパティが多い 2. クラスの責任を端的に言えない 3. プロパティで条件分岐する 4. テストが書きづらい 5.
修正が伝播する 単一責任の原則違反のにおい
31 1. プロパティが多い 2. クラスの責任を端的に言えない 3. プロパティで条件分岐する 4. テストが書きづらい 5.
修正が伝播する 単一責任の原則違反のにおい プロパティが多い場合、次の可能性が 高い • 2つ以上の責任を表現しようとして いる • プロパティを媒介に修正が伝播す る
32 1. プロパティが多い 2. クラスの責任を端的に言えない 3. プロパティで条件分岐する 4. テストが書きづらい 5.
修正が伝播する 単一責任の原則違反のにおい 幅広い意味を持つ名前のクラスは、様々な 責任を負わされる 「設定を管理する(ConfigManager)」はそ の典型例 もう一段階具体的に説明できないか 考えるべし
33 1. プロパティが多い 2. クラスの責任を端的に言えない 3. プロパティで条件分岐する 4. テストが書きづらい 5.
修正が伝播する 単一責任の原則違反のにおい flagやtypeなどのプロパティを持ち、 条件分岐しているもの プロパティによって「やりたいこと」が異な る →つまり責任が異なる 単一責任の原則にしたがってクラスを分 離する
34 1. プロパティが多い 2. クラスの責任を端的に言えない 3. プロパティで条件分岐する 4. テストが書きづらい 5.
修正が伝播する 単一責任の原則違反のにおい 複数の責任があると、テストケースが膨 大になる メソッドもシンプルになっているはずな ので、テスト自体も複雑にならない
35 1. プロパティが多い 2. クラスの責任を端的に言えない 3. プロパティで条件分岐する 4. テストが書きづらい 5.
修正が伝播する 単一責任の原則違反のにおい 複数の責任が一つのクラスに集まって いる 異なる責任の間でプロパティやメソッド を共有できてしまう ある責任に対する修正が、別の責任を 扱う箇所にも伝播する
36 責任にあわせてクラスを分割する よい設計への修正 設定ファイルを扱うクラスから条件分岐が消えた 設定を個別に変更でき、修正が伝播しない
37 責任にあわせてクラスを分割する よい設計への修正 依存関係も整 理された
38 問題設定 • UserRepositoryは、データを取得してユーザー一覧を返す • データの取得元にはAPIとDBがあり、切り替えられるようにする わるい設計 • UserRepositoryに、APIクラスとDBクラスのプロパティを持たせる •
どちらを取得元にするかは、コンストラクタで設定する 2. 開放閉鎖の原則( OCP) ソフトウェアの実体は、拡張に対して開か れているべきであり、修正に対して閉じて いなければならない
2. 開放閉鎖の原則( OCP) 39 2種類の コンストラクタ APIクラスは省略 処理を分岐 Main.java
2. 開放閉鎖の原則( OCP) 40 UserRepositoryから API、DBへの依存 Main.java
41 1. 修正の影響を受ける範囲が広い 2. 追加の影響を受ける範囲が広い わるい設計の問題点 ソフトウェアの実体は、拡張に対して開か れているべきであり、修正に対して閉じて いなければならない
42 試しに、Db.selectAllUser()をDb.getUserList()に変更してみる 修正の影響を受ける 範囲が広い ソフトウェアの実体は、拡張に対して開か れているべきであり、修正に対して閉じて いなければならない UserRepositoryにも修正が伝 播してしまった!
43 API、DBの他に、Redisからもデータを取得したくなり、クラスを追加 追加の影響を受ける 範囲が広い ソフトウェアの実体は、拡張に対して開か れているべきであり、修正に対して閉じて いなければならない UserRepositoryにも修正が必要 =修正が閉じていない
44 インターフェースを導入 よい設計への修正 UserRepositoryから 条件分岐が消えた
45 インターフェースを導入 よい設計への修正 各データ取得元は インターフェースを実装 UserRepositoryは インターフェースに依存
データ取得元の修正や追加の影響が、UserRepositoryに及ばない 46 よい設計のメリット DBクラスの 関数名を変更しても、 DbAdapterで吸収できる Redisクラスを 追加する場合、 DataStoreInterface を実装していればOK
47 OCPを実現するテクニックの一つ 書籍での紹介もOCPのおさらいなので、今回は割愛 3. リスコフの置換原則 (LSP) ある基底クラスへのポインタを持つ関数 群は、その派生クラスの詳細を知らなくて も扱えるべき
48 問題設定 • APIを実行するApiクラス、DBから データを取得するDbクラス • それぞれの接続情報を ApiConfig、DbConfigに持つ わるい設計 •
各Configクラスは ConfigInterfaceを実装 4. インターフェース 分離の原則( ISP) 一つの汎用なインターフェースより、 特化したインターフェースがたくさんあっ た方がよい
49 4. インターフェース 分離の原則( ISP) 一つの汎用なインターフェースより、 特化したインターフェースがたくさんあっ た方がよい どちらもConfigInterfaceを使用 ApiはgetId()を利用、DbはgetUser()を利用
getPassword()は共通 Main.java
50 4. インターフェース 分離の原則( ISP) 一つの汎用なインターフェースより、 特化したインターフェースがたくさんあっ た方がよい APIの設定ファイル DBの設定ファイル
設定を扱う共通の インターフェース 使わないメソッド
51 fatなインターフェースを媒体にして修正が伝播してしまう わるい設計の問題点 一つの汎用なインターフェースより、 特化したインターフェースがたくさんあっ た方がよい ①DbConfigのgetUser()を getUserName()に変更してみる ②DbのgetUser()がgetUserName()に変わる ②ConfigInterfaceのgetUser()が
getUserName()に変わる ③ApiConfigのgetUser()が getUserName()に変わる!
責任ごとにインターフェースを分離する DB側の修正がAPI側に伝播しない 無理な共通化は悲劇の始まりです 見極めが重要(難しいけど、、) よい設計への修正 一つの汎用なインターフェースより、 特化したインターフェースがたくさんあっ た方がよい 52 分離と共通化って真逆でしょ?
共通化したほうがいいって 聞いたけど
53 問題設定 • UserRepositoryは、APIでデータを取得してユーザー一覧を返す わるい設計 • UserRepositoryに、APIクラスのプロパティを持たせる 5. 依存性逆転の 原則(DIP)
上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき
5. 依存性逆転の 原則(DIP) 上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき 54 Main.java
• DIPは依存の方向をコントロールする手法 • 右のクラス図だけをみても、 設計の良し悪しを議論できない • 議論のために、次の前提を導入する UserRepositoryはApiに依存しているので、Apiの変更はUserRepositoryに 伝播する 逆に、UserRepositoryの変更はApiに伝播しない
わるい設計の問題点 上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき 55 • Apiの修正が頻繁に行われたり、他のクラスと頻繁に交換される
Apiが変更されたとしても、UserRepositoryは変更したくない 基本方針 • 利用する側(UserRepository)の都合でインターフェースを定義 • 利用される側(Api)はそのインターフェースを実装 よい設計への修正 上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき
56
上位、下位のモジュールを考えてみる より抽象度の高いUserRepositoryを上位 より詳細な処理を行うApiを下位 上位モジュールで下位モジュールが利用されている =下位モジュールを持ち込んでいる状態 より詳細な処理を扱う下位モジュール(具象)に依存している よい設計への修正 上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき
57
UserRepository(上位)の都合でインターフェースを定義し、 Api(下位)がそれを実装する Apiの変更が UserRepositoryに伝播しない よい設計への修正 上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき 58 上位→下位の依存だった設計が
下位→上位の依存になった =依存性が逆転した 抽象(上位・インターフェース)に依存 している
よい設計への修正 上位モジュールは下位モジュールから 何も持ち込んではならない 具象ではなく、抽象に依存するべき 59 UserRepositoryとApiは インターフェースに依存 下位が上位に依存
• よい設計の指標は様々で、今回はクラス設計について、機能追加や修正 の容易さについて議論した • SOLID原則は、よい設計の指標を実現するための考え方の一つ • SRPでクラスの責務をひとつにし、修正が他のクラスに及ばないようにす る • LSP/ISP/DIP
を用いて、修正・追加が容易なOCPを目指す 60 まとめ
61 • DRY原則 Don't Repeat Yourself ◦ すべての知識はシステム内において、単一、かつ明確な、そして信頼 できる表現になっていなければならない •
KISSの原則 Keep It Simple, Stupid ◦ 単純さを追求し、複雑さを避けること • YAGNI原則 You Ain't Gonna Need It ◦ 本当に必要になるまで作らない方がよい おまけ:その他の設計原則