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
Code Smellsの Primitive Obsession に気を付けて設計する/Des...
Search
kirimaru
July 07, 2021
Technology
1
3.4k
Code Smellsの Primitive Obsession に気を付けて設計する/Designing-with-Code-Smells-Primitive-Obsession
リーダブルコード LT会 - vol.2 #readablelt
での登壇資料。
kirimaru
July 07, 2021
Tweet
Share
More Decks by kirimaru
See All by kirimaru
早くAPI作るならFastAPI がオススメ
hirotokirimaru
1
49
DDD(ドメイン駆動設計)を知らない人に知ったつもりさせる/Introduce_DDD_to_unfamiliar_individuals
hirotokirimaru
0
310
例示! Spring Bootで作られた REST APIのテストコード/ Testing-Example-for-a-REST-API-created-with-Spring-Boot
hirotokirimaru
2
1.9k
一緒に使うことが多い値は別クラスにしよう(Data Clumps)/data_clumps_is_useful
hirotokirimaru
0
730
Backlogが好きな話。/i_like_backlog
hirotokirimaru
0
140
私が好きなポートアンドアダプターを紹介する/I-like-hexagonal-architecture.pdf
hirotokirimaru
1
1.1k
名付けのためにクラス図を元に会話しよう/Let's-use-class-diagram-to-communicate-with-client
hirotokirimaru
0
640
FCCを推す/My favorite software architecture is FCC
hirotokirimaru
0
230
我々はなぜオブジェクト指向やDDD等のアーキテクチャを学ぶのか/Why_we_learn_ObjectOriented_and_DDD_Architecture
hirotokirimaru
1
1.1k
Other Decks in Technology
See All in Technology
Wasmで社内ツールを作って配布しよう
askua
0
180
興味の胞子を育て 業務と技術に広がる”きのこ力”
fumiyasac0921
0
540
SAE J1939シミュレーション環境構築
daikiokazaki
1
210
Strands Agents & Bedrock AgentCoreを1分でおさらい
minorun365
PRO
6
150
【CEDEC2025】ブランド力アップのためのコンテンツマーケティング~ゲーム会社における情報資産の活かし方~
cygames
PRO
0
220
相互運用可能な学修歴クレデンシャルに向けた標準技術と国際動向
fujie
0
170
【2025 Japan AWS Jr. Champions Ignition】点から線、線から面へ〜僕たちが起こすコラボレーション・ムーブメント〜
amixedcolor
1
110
データ基盤の管理者からGoogle Cloud全体の管理者になっていた話
zozotech
PRO
0
180
【CEDEC2025】『ウマ娘 プリティーダービー』における映像制作のさらなる高品質化へ!~ 豊富な素材出力と制作フローの改善を実現するツールについて~
cygames
PRO
0
180
【CEDEC2025】『Shadowverse: Worlds Beyond』二度目のDCG開発でゲームをリデザインする~遊びやすさと競技性の両立~
cygames
PRO
1
240
反脆弱性(アンチフラジャイル)とデータ基盤構築
cuebic9bic
2
140
Microsoft Learn MCP/Fabric データエージェント/Fabric MCP/Copilot Studio-簡単・便利なAIエージェント作ってみた -"Building Simple and Powerful AI Agents with Microsoft Learn MCP, Fabric Data Agent, Fabric MCP, and Copilot Studio"-
reireireijinjin6
1
210
Featured
See All Featured
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3k
Build your cross-platform service in a week with App Engine
jlugia
231
18k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.4k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
21k
Typedesign – Prime Four
hannesfritz
42
2.7k
The Pragmatic Product Professional
lauravandoore
36
6.8k
Practical Orchestrator
shlominoach
190
11k
We Have a Design System, Now What?
morganepeng
53
7.7k
The Power of CSS Pseudo Elements
geoffreycrofte
77
5.9k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
Documentation Writing (for coders)
carmenintech
73
4.9k
Why Our Code Smells
bkeepers
PRO
337
57k
Transcript
Code Smellsの Primitive Obsession に気を付けて設計する リーダブルコード LT会 - vol.2 #readablelt
20210707 きり丸(水上 皓登)@nainaistar
※ Javaのソースコードで例示します。 また、今回の話は型の話をするので、 Python等の動的型付言語では メリットが薄いかもしれません
名前:きり丸(水上 皓登) twitter:nainaistar GitHub:hirotoKirimaru ブログ:きり丸の技術日記 https://nainaistar.hatenablog.com/ 3 リファクタリングの 正解がわからない
良いコードとは?
良いコードとは悪くないコード
悪くないコードにするには、 Code Smellsに気を付けたらいい
Primitive Obsessionとは (基本データ型の執着) 言語に用意されているデータ型しか使わないことを指します。 用意されているデータ型だけしか使用していると、 どんなデータが入るか表現できません。 コードを読む際に、業務知識が必要になるコードもあります。 自分たちが作成した型を用意することで、 よりプログラムの表現力を高めることができます。
例えば、年度 現在は、西暦2021年度です。 下2桁の21年度だけ表現することもあります。 令和3年度とも言います。R3と表現するかもしれません。 今年度の期間という表現をした場合は、 学校であれば「2021/04/01-2022/03/31」ですし、 6月が期末の会社であれば「2021/07/01-2022/06/30」です。
例えば、年度 public class 年度{ private final String value; public 年度(String
value){ this.value = value; } } 単純に型をラップするだけでも 意図を表現できます。
例えば、年度 public class 年度{ // 2021等の4桁のみを許容する private final String value;
public 年度(String value){ if (value.length != 4){ throw new RuntimeException(“4桁以外”); } this.value = value; } } // もし、日付を文字列で管理していたら…? // ISO8601基本形式? 20210627T112445+0900 // ISO8601拡張形式? 2021-06-27T11:24:45+09:00 // 独自形式? 2021/06/27 11:24:45 // もっと独自形式? 2021/06/27|11:24:45 型としての制約をつけると より表現ができます。
例えば、年度 public class 年度{ private final String value; public 年度(String
value){ if (value.length != 4){ throw new RuntimeException(“4桁以外”); } this.value = value; } // 2021 -> 21 public String toShortYear() { return this.value.substring(2); } // 年度をLocalDateTimeへ変換する public LocalDateTime toLocalDateTime(){ return LocalDateTime.of( Integer.parseInt(value), 1, 1, 0, 0 ); } } 制約を満たしている状態でイン スタンスを生成しているので、 Nullチェック等は不要です。
例えば、ISBN ISBNは本を一意に特定するためのIDです。 仕様: • ISBNは10桁か13桁 • 10桁から13桁に変換できるロジックがある ◦ アプリ上は13桁で管理したいので、10桁は13桁に変換する •
ISBNは978, または979で始まる(現状の日本国内は978のみ) ◦ 192は書籍JANコードの2段目を意味する
例えば、ISBN ISBNは本を一意に特定するためのIDです。 仕様: • ISBNは10桁か13桁 • 10桁から13桁に変換できるロジックがある ◦ アプリ上は13桁で管理したいので、10桁は13桁に変換する •
ISBNは978, または979で始まる(現状の日本国内は978のみ) ◦ 192は書籍JANコードの2段目を意味する
例えば、ISBN 欠落しがちな仕様を 型で表現することが できます (ISBNは10桁か13桁) public class Isbn { private
final String isbn; public Isbn(String code) { // 10桁か13桁かチェック String len = code.length(); if (!(len == 10 || len == 13)) { throw new RuntimeException("エラー"); } String isbn1 = convert10to13(code); if (!”978”.equals(isbn1.substring(0, 3))) { throw new RuntimeException( “2段目バーコードを読み込んだ疑いあり” ); } this.isbn = isbn1; } }
例えば、ISBN ISBNは本を一意に特定するためのIDです。 仕様: • ISBNは10桁か13桁 • 10桁から13桁に変換できるロジックがある ◦ アプリ上は13桁で管理したいので、10桁は13桁に変換する •
ISBNは978, または979で始まる(現状の日本国内は978のみ) ◦ 192は書籍JANコードの2段目を意味する
例えば、ISBN 欠落しがちな仕様を 型で表現することが できます (10桁は13桁に変換する) public class Isbn { private
final String isbn; public Isbn(String code) { String len = code.length(); if (!(len == 10 || len == 13)) { throw new RuntimeException("エラー"); } // 10桁から13桁に変換する(省略) // 13桁ならそのまま String isbn1 = convert10to13(code); if (!”978”.equals(isbn1.substring(0, 3))) { throw new RuntimeException( “2段目バーコードを読み込んだ疑いあり” ); } this.isbn = isbn1; } }
例えば、ISBN ISBNは本を一意に特定するためのIDです。 仕様: • ISBNは10桁か13桁 • 10桁から13桁に変換できるロジックがある ◦ アプリ上は13桁で管理したいので、10桁は13桁に変換する •
ISBNは978, または979で始まる(現状の日本国内は978のみ) ◦ 192は書籍JANコードの2段目を意味する
例えば、ISBN 欠落しがちな仕様を 型で表現することが できます (ISBNは978はじまり) public class Isbn { private
final String isbn; public Isbn(String code) { String len = code.length(); if (!(len == 10 || len == 13)) { throw new RuntimeException("エラー"); } String isbn1 = convert10to13(code); // 本当にISBNか。 if (!”978”.equals(isbn1.substring(0, 3))) { throw new RuntimeException( “2段目バーコードを読み込んだ疑いあり” ); } this.isbn = isbn1; } }
例えば、山札・手札・場札 UNOを例にします。 今までの例から、札という型に変換することは思いつくでしょう。 しかし、山札・手札・場札等の配列の表現は 次のコードになっていないでしょうか。
例えば、 山札・手札・場札 public class UnoGame{ List<札> 山札; List<札> 手札; List<札>
場札; } public class 札{ String value; } 基本型のListを使っています。 これでは、表現を増やすのが 難しいです。
例えば、 山札・手札・場札 public class UnoGame{ 手札 手札; 山札 山札; 場札
場札; } public class 札 { String value; } public class 手札{ List<札> value; } public class 山札{ List<札> value; } public class 場札{ List<札> value; } 型がついたことにより、表現の 幅が広がりました。 また、操作するドメインが変わ り、メソッドがより適切な位置に 配備される可能性があります。
まとめ 業務知識でコードを読むのではなく、 コードに業務知識を表現させると可読性が上がります。 また、できる限り本物に近いデータを使用する必要があり、 異常データを防ぎやすくなるので、 異常検知が早くなる可能性があります。 データマッピングだけの型ではなく、 業務を表現した型を作ることで、 Code SmellsのPrimitive
Obsessionを回避してみませんか?
Appendix
ブログ 基本型以外を使って設計レベルアップ! (Primitive ObsessionとFCC)
話すこと / 話さないこと • Code Smells • Primitive Obsession •
Java • Primitive Obsession が有効ではない個所 (私もわからない) 詰めなおしは推奨されないので、 べき論が分からない 話すこと 話さないこと
対象者 / 非対象者 • リファクタリングをしたいけど、 ヒントが分からない人 • リファクタリングに興味が無い人 • 動的型付言語
対象者 非対象者
登壇を見た人への期待するアクション • Code Smellsに興味を持つ • 型を作ることをやってみる アクション