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

ふくおか Scrum vol.4 不確実性に立ち向かう!

ふくおか Scrum vol.4 不確実性に立ち向かう!

Yoshikazu YAMADA

November 08, 2019
Tweet

More Decks by Yoshikazu YAMADA

Other Decks in Programming

Transcript

  1. 8 環境不確実性 ⽬的不確実性 ⽅法不確実性 通信不確実性 マーケッ トの変化 How の 不確実性

    What の 不確実性 コミュニケーション の不確実性 不確実性の種類と関係
  2. 11 知識 仮説 知識 知識 仮説 知識 知識 知識 仮説

    検証 検証 知識 知識 不確実性 不確実性 不確実性 仮説検証により不確実性から知識を創造 検証駆動
  3. 15 特性 副特性 概要 信頼性 システムが必要時に正しく継続動作できる度合い 成熟性 通常時に故障を回避できる度合い 可⽤性 必要時に継続して稼働できる度合い

    障害許容性 障害時に運⽤できる度合い 回復性 障害時にシステムの復旧 (データを含む回復) できる度合い 保守性 効果的・効率的 に維持や変更ができる度合い モジュール性 変更の影響範囲が局所化されるよう、コンポーネント間が独⽴し た構成となっている度合い 再利⽤性 コンポーネントが新たなシステム構築において利⽤できる度合い 解析性 変更部分や障害原因の特定のための診断や修正箇所の識別・影響 の評価の 効果性・効率性 の度合い 変更性 ⽋陥や品質の低下なく変更が 効果的・効率的 に⾏える度合い 試験性 テストの 設計・実施 のし易さの度合い
  4. 17 結合度 凝集度 凝集性 • 関連する “振る舞い” が集まっている度合 ⇨ 要求

    (振る舞い) の変更の影響範囲を局所化 結合度 • モジュール間の関連性・依存性の度合 ⇨ 結合度の低いモジュール間の変更の影響範囲を隔離 凝集性と結合度の関係 • 凝集性の⾼いモジュール間の結合度は低 原則
  5. 18 SOLID 原則 The Single Responsibility Principle (SRP) – 単⼀責任の原則

    • クラスを変更する理由は 1 つ以上存在してはならない。 The Open-Closed Principle (OCP) – オープン・クローズドの原則 • ソフトウェアの構成要素は拡張に対して開いて、修正に対して閉じていなければならない。 The Liskov Substitution Principle (LSP) – リスコフの置換原則 • 派⽣型はその基本型と置換可能でなければならない。 The Dependency Inversion Principle (DIP) – 依存関係逆転の原則 • 上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも「抽象」に 依存すべきである。 • 「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。 The Interface Segregation Principle (ISP) – インターフェース分離の原則 • クライアントに、クライアントが利⽤しないメソッドへの依存を強制してはならない。
  6. 19 凝集性 (cohesion) • モジュールに含まれる要素間の機能的な結び付き • 機能的に期待され関連する振る舞いのまとまり : 役割 (責任)

    public interface Modem { public void dial(String number); public void hangup(); public void send(char c); public char receive(); }
  7. 23 SRP に違反する構造 • 2つの異なる役割を担うクラス (Rectangle) Ø 無関係な変更理由による影響 描画処理の変更 ⇒

    ⾻格計算処理 図形の描画 (Graphical Application) アプリケーション 図形の⾻格 (Geometry Application) 計算アプリケーション
  8. 24 SRP に従った構造 • 2つの異なる役割を担うクラス (Rectangle) の分離 図形の描画 (Graphical Application)

    アプリケーション 図形の⾻格 (Geometry Application) 計算アプリケーション
  9. 26 役割 (変更理由) はどう決まる︖ • 役割 : アクターが期待する振る舞い • 変更理由

    : ユースケースの変更 (アクターが期待する振る舞いの変更) • ユースケースの変更 : ビジネスの変更 予測困難︕
  10. 29 OCP – オープン・クローズドの原則 既存コードに対する修正ではなく新たなコードの追加による拡張 (要求の 変化に対応) Open : 拡張に対して開かれている

    モジュールの振る舞いに対する変更要求に対して、モジュールに新たな振る舞いを追加 (新たなコードの 追加) により対応可能 Closed : 修正に対して閉じている モジュールの振る舞いを拡張しても、既存のコードは影響を受けない (既存のコードに修正を加える必要 がない)
  11. 32 ソフトウェアの構造における 契約 と 抽象化 ü 契約 オブジェクトを使う側に対するオブジェクトの公約 ü 抽象化

    • オブジェクトを使う側に対するオブジェクトの内部構造の隠蔽 • 詳細の隠蔽 • 公開したいオブジェクトの⼀側⾯
  12. 33 OCP に従った構造#1 • Interface による Client に対する契約と Server 実装内部構造の分離と隠蔽

    • Client は実装という 具象・詳細 ではなく契約という抽象に依存 ⇒ Server の変更に対して、既存コードの修正ではなく実装クラスの追加で対応可能
  13. 34 OCP に従った構造#2 新たな図形の追加 (要求の変化) に対して、コードの追加 (Shape の派⽣クラスの追加) により対応可能 な

    OCP に準じた構造 public abstract class Shape { abstract void draw(); } public class Circle extends Shape { double radius; Point center; void draw() {} } public class Square extends Shape { double side; Point top; void draw() {} } public class ShapeDrawer { void drawShapes(List<Shape> shapes) { for (Shape shape : shapes) { shape.draw(); } } }
  14. 35 OCP に従った構造に対する仕様変更 【仕様変更】 リスト中の全ての Circle を描画後に Square を描画しなければならない public

    abstract class Shape { abstract void draw(); } public class Circle extends Shape { double radius; Point center; void draw() {} } public class Square extends Shape { double side; Point top; void draw() {} } public class ShapeDrawer { void drawShapes(List<Shape> shapes) { for (Shape shape : shapes) { shape.draw(); } } }
  15. 36 OCP に従った構造に対する仕様変更 【仕様変更】 リスト中の全ての Circle を描画後に Square を描画しなければならない public

    abstract class Shape { abstract void draw(); } public class Circle extends Shape { double radius; Point center; void draw() {} } public class Square extends Shape { double side; Point top; void draw() {} } public class ShapeDrawer { void drawShapes(List<Shape> shapes) { for (Shape shape : shapes) { shape.draw(); } } } 既存コードの修正が必須︕
  16. 37 OCP に従った構造に対する仕様変更の対応例#1 public abstract class Shape { abstract void

    draw(); abstract boolean compare(Shape shape); } public class Circle extends Shape { double radius; Point center; void draw() {} boolean compare(Shape shape) {} } public class Square extends Shape { double side; Point top; void draw() {} boolean compare(Shape shape) {} } public abstract class ShapeComparator implements Comparator<Shape> { public int compare(Shape shape0, Shape shape1) { return shape0.compare(shape1) ? -1 : 1; } } public class ShapeDrawer { void drawShapes(List<Shape> shapes) { Collections.sort(shapes, new ShapeComparator()); for (Shape shape : shapes) { shape.draw(); } } } • ⾚枠部分のコードは Shape 派⽣型の追加に対して Closed でない。 • Shape 派⽣型が追加される度に⾚枠部分に修正が必須 • ⾚枠部分については単⼀責任の原則に従い分割する等の対応が必要
  17. 39 OCP の適⽤ • どういった種類の変更に対して設計 (構造) を閉じたいのかを選択 • どういった種類の変更が頻繁に起こるのかを推測 •

    そういった種類の変更から設計 (構造) を守る「抽象」を構築 • 変更の推測は困難 • 「抽象」は設計 (構造) を複雑にする可能性を内包 • 実際に望ましい「抽象」が必要になった時点で構築 • ⼀度 Open な構造に対する変更が発⽣した時点で「抽象」を構築
  18. 40 変化、「抽象」の構築に備える • TDD を実践しテスト可能な構造を維持する。 • 短いサイクル (数時間単位) で開発する。 •

    頻繁にデリバリーし、顧客やユーザーのフィードバックを得る。 • 重要な機能から優先的に開発する。 • テストによりプロダクトを守り、テストにより構造を抽象化する。 • “⼩さく” 構造を拡張する。 • 早い段階で変更の予測に有⽤な知識を得る。
  19. 44 LSP に違反 ⇒ OCP にも違反 public class CarDriver {

    void drive(Car car) { if (car instanceof FastCar) { … } else if (…) { … } } } • ⾚枠部分のコードは RunTime Type Identification (ランタイム情報) を使⽤して派⽣型毎に異なる 処理を実装 : LSP に違反 • 派⽣型が追加される度に⾚枠部分に修正が必須 : OCP に違反
  20. 45 LSP に違反する構造 LSP に違反するケースは︖ public class Rectangle { double

    width; double height; public void setWidth(double width) { this.width = width; } public void setHeight(double height) { this.height = height; } public double area() { return this.height * width; } } public class Square extends Rectangle { public void setWidth(double width) { this.width = width; this.height = width; } public void setHeight(double height) { this.height = height; this.width = height; } }
  21. 46 LSP に違反する構造 public static void space(Rectangle rectangle) { rectangle.setHeight(4);

    rectangle.setWidth(5); assert rectangle.area() == 4 * 5; } space メソッドの作者 (Rectagle のクライアント) は Height と Width が独⽴していると信じている︕ (⻑⽅形の「縦」と「横」の⻑さが独⽴していると考えるのは合理的) • space に渡される全ての Rectangle がこの仮定を満たすとは限らない。 • space は Rectangle 派⽣オブジェクトに対して正常に動作しない。 space は Rectangle の代わりに Rectangle 派⽣オブジェクトを使えない。
  22. 48 Rectangle のクライアント Square の作者 Rectangle (⻑⽅形) を引数に 受取っている。 ⻑⽅形が「縦」「横」を独⽴し

    て扱えるのは普遍的な性質だ︕ 私にはその普遍性を利⽤する権 利がある︕ Square (正⽅形) は「縦」 「横」が等しいという普遍的な 性質に従っている︕
  23. 50 モデルの正当性 ≠ モデル本来の普遍性 • モデルの正当性は⽴場によって異なる。 • ⽴場を考慮せずに正当性は判断できない。 ※ クライアント視点での合理的な仮定は

    TDD (Unit Test) により明確化 (コード化) 設計 (モデル) を利⽤するクライアントの視点での合理的な仮定によって のみモデルの正当性は明確化可能
  24. 51 Column – is-a の関係 分類法 (物事を分類によって理解するアプローチ) • A は

    (is) B の⼀種 (a) • 象は哺乳類の⼀種 • 哺乳類は動物の⼀種 • 動物は⽣物の⼀種 継承関係にあるオブジェクト間で成⽴すべき関係 • SubClass は (is) SuperClass の⼀種 (a)
  25. 52 “LSP” と “is-a 関係” • is-a 関係の視点 : Rectangle

    と Square は is-a 関係が成⽴ • Rectangle のクライアントの視点 : “振る舞い” の⾯で is-a 関係が不成⽴ 「is-a 関係」に付随する「振る舞い」 • 「is-a 関係」+「“振る舞い“の同等性」 • クライアントにとって合理的に理解可能かつ依存可能な「振る舞い」を「is-a 関係」で実現
  26. 55 契約 と 事前条件・事後条件 契約 : コンポーネントがクライアントに提供する モノ・コト に関する合意 (取り決め)

    事前条件 : 契約実⾏前に満たすべき状態や引数 事後条件 : 契約実⾏後に満たすべき状態や戻り値
  27. 56 契約による設計 (DBC : Design By Contract) メソッドの “事前条件と事後条件” を

    “前もって明⽂化” • 事前条件 : メソッド実⾏前に成⽴させなければならない条件 • 事後条件 : メソッド実⾏後に成⽴させなければならない条件 Square#setWidth(double width) の事後条件 assert ((this.width == width) && (this.height == old.height)) Square#setHeight(double height) の事後条件 assert ((this.height == height) && (this.width == old.width))
  28. 58 事前条件・事後条件 の例 契約 : Java トレーニングサービス 事前条件 : Java

    の基本⽂法を理解していること 事後条件 : ⼀般的な web システムにおける Server-Side プログラミングの知識の習得 より強い事前条件 Java の基本⽂法に加え 2 年以上のプロ グラミング業務経験があること より弱い事前条件 特になし より強い事後条件 Server-Side プログラミングに加え、テ スト駆動開発の知識の習得 より弱い事後条件 Java プログラミングの知識の習得 条件に関する強弱の妥当性はクライアントとの合意が必要 (妥当性は観点による)
  29. 61 LSP に準じているかどうかを判定する経験則 1. 派⽣クラスにおいて退化している機能 2. 派⽣クラスからの例外のスロー ※ 基本クラスではスローされない例外を派⽣クラスでスロー public

    class Derived extends Base { public void f() { // do nothing.. } } public class Base { public void f() { doSomething(); } } public class Derived extends Base { public void f() { throw new RuntimeException() } } public class Base { public void f() { doSomething(); } }
  30. 62 依存関係逆転の原則 (DIP : The Dependency Inversion Principle) a. 上位のモジュールは下位のモジュールに依存してはなら

    ない。どちらのモジュールも「抽象」に依存すべきであ る。 b. 「抽象」は実装の詳細に依存してはならない。実装の詳 細が「抽象」に依存すべきである。
  31. 63 ソフトウェアの⽅針 • Biz Model • Biz Rule • Workflow

    実装の詳細 • Technical Matter • Device Upper Layer - 上位層 Lower Layer – 下位層
  32. 64 ソフトウェアの⽅針 • Biz Model • Biz Rule • Workflow

    実装の詳細 • Technical Matter • Device 変更 再利⽤
  33. 65 ソフトウェアの⽅針 • Biz Model • Biz Rule • Workflow

    実装の詳細 • Technical Matter • Device 望ましい依存の⽅向
  34. 66 ソフトウェアの⽅針 • Biz Model • Biz Rule • Workflow

    実装の詳細 • Technical Matter • Device 望ましくない依存の⽅向の弊害 • 技術要因による変更が上位層に 影響を与える。 • 実装の詳細に依存しているため (※) 上位層の再利⽤性が低下す る。 ※ ⽅針が詳細に依存しているため⽅針の再利⽤に実装の詳細が含まれる。
  35. 69 Policy Layer ソフトウェアの⽅針 Mechanism Layer 実装の詳細 – 制御・機構 望ましい階層化アーキテクチャ

    Utility Layer 実装の詳細 – 個別技術 <<interface>> Policy Service Mechanism Policy <<interface>> Mechanism Service Utility
  36. 70 Policy Layer ソフトウェアの⽅針 Mechanism Layer 実装の詳細 – 制御・機構 所有権の「逆転」

    Utility Layer 実装の詳細 – 個別技術 <<interface>> Policy Service Mechanism Policy <<interface>> Mechanism Service Utility クライアントがイン ターフェースを所有 クライアントがイン ターフェースを所有 • 上位レイヤ (クライアント) がインターフェースを宣⾔ し、下位レイヤが実装を請 け負う • 上位レイヤ (クライアント) がインターフェースを所有
  37. 73

  38. 74 「⽅針」と「詳細」(抽象と実装の詳細) が分離されていない。 • Button は Lamp に直接依存 ü Button

    は Lamp の変更の影響を受けてし まう。 ü Button の再利⽤性の低下 (Button がコン トロール可能なのは Lamp のみ) Button クラスの実装例 - 1 public class Button { private Lamp lamp; Button(Lamp lamp) { this.lamp = lamp; } public void poll() { if (/* 制御条件 */) { this.lamp.turnOn(); } } }
  39. 75 Button クラスの実装例 - 2 抽象への依存と依存関係の逆転 • Button は ButtonServer

    という「抽象」に依存 ü Button は Lamp の変更の影響を受けない。 ü Button の再利⽤性の向上 (ButtonServer を実装するデバイスを制御可能) • Lamp は ButtonServer という「抽象」に依存
  40. 78 “肥⼤化した” インターフェース component I/F#0 I/F#1 I/F#3 I/F#2 I/F#6 I/F#4

    I/F#7 I/F#5 I/F#8 client#a client#b client#c ※ I/F = API ≒ 関数、メソッド
  41. 79 “肥⼤化した” インターフェース component I/F#0 I/F#1 I/F#3 I/F#2 I/F#6 I/F#4

    I/F#7 I/F#5 I/F#8 client#a client#b client#c ※ I/F = API ≒ 関数、メソッド 依 存 関 係
  42. 81 インターフェース汚染の例 public class Door { public void lock(); public

    void unlock(); public boolean isDoorOpen(); } Door • ロック (lock) • ロックの解除 (unlock) • ロックの状態 (isDoorOpen) クライアントは特定の Door インターフェース の実装に依存しない (特定の実装に影響を受けな い)
  43. 82 インターフェース汚染の例 public class Timer { public void register(int timeout,

    TimerClient client) { … } } ⻑時間ドアが開放されていた場合に警報を発す る TimedDoor コンポーネント (Door インター フェースを実装) を作成 Timer • タイムアウト時間に達した際に TimerClient#timeout を実⾏する Timer コンポーネント TimerClient • タイムアウトした際のコールバックのた めのコンポーネント (#timeout にタイ ムアウト時の処理を実装) public interface TimerClient { public void timeout() { … } }
  44. 83 インターフェース汚染の例 TimerClient#timeout ⇒ TimedDoor へのタイムアウ ト通知を⾏うために継承 (TimerClient から Door

    を派 ⽣) を使ったクラスの構造例 • TimedDoor は Timer に⾃分⾃⾝を登録し、 Timer からタイムアウト通知を受ける
  45. 84 Door ⇢ TimerClient への依存 • Door 派⽣クラスの⼤半は Timer を必要としな

    い ⇒ timeout を退化させることになり LSP に 違反する可能性 クライアントへの TimerClient 依存の強制 • Timer を使わないクライアントも TimerClient に依存 TimerClient#timeout により Door は “汚染” され、 Door 肥⼤化の要因 インターフェース汚染の例 – 問題点
  46. 86 逆向きの⼒ : クライアントがインターフェースに与える強制⼒ public class Timer { public void

    register(int timeout, int timeoutId, TimerClient client) { … } } public interface TimerClient { public void timeout(int itmeoutId) { … } } Timer のクライアントが複数のタイムアウトを 登録する場合 ⇒ 右図のような設計変更が発⽣ TimerClient のクライアント • 全てのクライアントが影響を受けるもの の、クライアントが望む変更の影響であ るため致命的な問題にはならない。 Door のクライアント • クラスの構造上、全く関係のない Door のクライアントに影響が及ぶ︕
  47. 87 インターフェース分離の原則 (ISP : The Interface Segregation Principle) クライアントに、クライアントが利⽤しないメソッドへの依存を強制し てはならない。

    • 利⽤しないメソッドへの依存 ⇒ 利⽤しない (無関係な) メソッドの変更に不⽤意に影響を受ける。 • 利⽤しないメソッドへの依存 ⇒ 同メソッドに依存する (メソッドを実際に使っている) 他のクライア ントが強いる変更に不⽤意に影響を受ける。
  48. 88 ※ 厳密には分離可能 インターフェース汚染の例 – 分離による汚染の回避 ISP への準拠 : Timer

    と Door のクライアントに対するイン ターフェース (≠ java interface) を分離 2 つのインターフェースは Timer データを共有するため、同 ⼀のオブジェクトに実装する必要 (※) がある︕
  49. 89 インターフェースの分離例 – 委譲による分離 public class TimedDoor { public TimerClient

    getAdapter() { return new DoorTimerAdapter(this); } } public class DoorTimerAdapter implements TimerClient { TimedDoor door; DoorTimerAdapter(TimedDoor door) { this.door = door; } public void timeout() { door.doorTimeout(); } }
  50. 96 YAGNI You Ainʼt Gonna Need It あとで必要になることはない︕ • 後で使うだろうという予測の元に作ったものは、実際には10%程度しか使われな

    い。したがって、それに費やした時間の90%は無駄になる。 • 余計な機能があると、仕事が遅くなり、リソースを浪費する。 • 予期しない変更に対しては、設計を単純にすることが備えとなる。そして、必要 以上の機能を追加すると、設計が複雑になってしまう。 • ⼈⽣の時間は、貴重である。したがって、⼈間の能⼒は、ただコードを書くため ではなく、現実の問題に集中するために使うべきである。 • 結局は、その機能は必要ないかもしれない。もしそうなったら、あなたがその機 能を実装するのに費やした時間も、他のみんながそれを読むのに費やした時間も、 その機能が占めていたスペースも、すべて無駄になってしまうだろう。 • コードをすばやく実装するために最も良い⽅法は、あまりコードを書かないこと である。そして、バグを減らすために最も良い⽅法も、あまりコードを書かない ことである。
  51. 101 Code Smell – 設計原則に反する実装の臭い︕ 1.Duplicate Code – コードの重複 2.Long

    Method – ⻑いメソッド 3.Lazy Class – 暇なクラス 4.Dead Code – 死んだコード 5.Feature Envy – 隣の芝⽣が⻘くみえるフィーチャー 6.Primitive Obsession – 原始的な型への執着 7.Magic Number – マジックナンバー 8.Inappropriate Intimacy – 不適切な親交 9.Data Clumps – データの塊
  52. 105 Principle, Pattern, Code Smell Code Smell Pattern Principle クリーンな構造

    (設計) の原則 原則を実現するための構造のアイデア 原則に違反する構造箇所の⽰唆
  53. 106 原則の適⽤ 適度な (必要最低限の) 原則の適⽤ = シンプルで変化に柔軟な構造 過度な (必要以上の) 原則の適⽤

    = 不必要な複雑性を取り込んだ構造  % $ $ " % $  ()!#   % $  &'   !#
  54. 108 0. クラスメンバーの順序 (クラスの構造) 合理性の例 1. クラス外から参照されるメンバーは可読性の観点から前⽅に配置 2. 変数はその他のメンバーから参照されるため可読性の観点からメソッドより前⽅に配置 3.

    private なメソッドは参照される public なメソッドの直後に配置 ※ コードの流れが下に向かって詳細化していくことにより可読性が向上 “何らかの合理的な”※ 順序で配置せよ︕ public class Sample { public static final int constant#1; private static final int variable#1; private int variable#2; … public void method#1() { method#2; } private void method#2() { } } ※ [Google Java Style Guide] #3.4.2 Ordering of class contents (https://google.github.io/styleguide/javaguide.html#s3.4-class-declaration)
  55. 110 #a #c #d #e #f #g #h #b ⼤きなクラス

    vs. ⼩さなクラス Client Class
  56. 111 #a #c #d #e #f #g #h #b ⼤きなクラス

    vs. ⼩さなクラス Client Class 変更 #2 変更 #3 変更 #4 変更 #1 変更 #2 変更 #3 変更 #4 変更 #1 責務 #3 責務 #1 責務 #4 責務 #2 責務 #1 責務 #2 責務 #3 責務 #4
  57. 114 Memo 不適切な名前は不適切な責務の集約のはじまり 不適切なクラス名の兆候 ü 不明確な意図 ü 偽情報の混⼊ ü ノイズワードの混⼊

    ü 不適切な品詞 ü ⼀貫性の無い命名 ü 適切な領域の⽤語の使⽤ クラスの概要解説⽂は、「もし」、「そして」、 「あるいは」、「しかし」等の単語 (接続詞) 以外の単語を使⽤して 100 語以内で作成でき ることが望ましい。
  58. 115 単⼀責務の原則 (SRP : The Single Responsibility Principle) クラスを変更する理由は 1

    つ以上存在してはならな い。(クラスは複数の責務をもってはいけない)
  59. ⼤きなクラス vs. ⼩さなクラス ⼤きなクラス • 理解しなければならない範囲 : ⼤ • 変更の影響範囲

    : ⼤ • 複数の責務 • 複数の変更理由 ⼩さなクラス • 理解しなければならない範囲 : ⼩ • 変更の影響範囲 : ⼤ • 単⼀の責務 • 単⼀の変更理由
  60. 118 凝集度の程度の分類 偶発的凝集 (Coincidental Cohesion) 「最悪」 適当 (無作為) に集められたものがモジュールとなっている。モジュール内の各部分には特に関連性はない (例えば、よく

    使われる関数を集めたモジュールなど)。 論理的凝集 (Logical Cohesion) 論理的に似たようなことをするものを集めたモジュール (例えば、全ての⼊出⼒ルーチンを集めたモジュールなど)。 時間的凝集 (Temporal Cohesion) 動作させたときにモジュール内の各部分が時間的に近く動作する (例えば、ある例外を受けたときに動作するルーチンとし て、ファイルをクローズするルーチン、エラーログを作成するルーチン、ユーザに通知するルーチンなどを集めたモジュー ル)。 ⼿続き的凝集 (Procedural Cohesion) ある種の処理を⾏うときに動作する部分を集めたモジュール (例えば、ファイルのパーミッションをチェックするルーチン とファイルをオープンするルーチンなど)。 通信的凝集 (Communicational Cohesion) 同じデータを扱う部分を集めたモジュール (例えば、同種のレコードの情報を操作するルーチンを集めたモジュールなど)。 逐次的凝集 (Sequential Cohesion) ある部分の出⼒が別の部分の⼊⼒となるような部分を集めたモジュール (例えば、全体としてあるファイルを読み込んで処 理をするモジュール)。 機能的凝集 (Functional Cohesion) 「最善」 単⼀のうまく定義されたタスクを実現するモジュール (例えば、⾓度のサインを計算するモジュール)。 凝集度 (Dec. 26, 2018, 14:52 UTC). In Wikipedia: The Free Encyclopedia. Retrieved from https://ja.wikipedia.org/wiki/凝集度
  61. 121 クラスの凝集度の測定 !"#$∗ = 1 ( ∑* + ,(./) −

    2 1 − 2 3456∗ (Lack of Cohesion in Methods) G : クラスに含まれるメソッドの数 H : クラスに含まれるインスタンス変数の数 IJ : / 番⽬のインスタンス変数 K(IJ) : / 番⽬のインスタンス変数にアクセスするメソッドの数 3LM NOPQ R S 3456∗ Cohesion
  62. class 124 method block#α block#β block#γ メソッドの分割とクラスの凝集性 α 〜 β

    のブロックで分割可能な⼤きなメソッドをもつクラス
  63. 125 method val#1 .. val#l val#m .. val#n メソッドの分割とクラスの凝集性 block#α

    block#β block#γ method#α(val#1, .., val#5) method#β(val#2, .., val#l) method#α(val#m, .., val#n) block#α block#β block#γ 3 つのブロックを 3 つのメソッド (α〜γ) に分割
  64. class val#a val#b val#c class 126 メソッドの分割とクラスの凝集性 method#α(val#1, ..,val#5) method#β(val#2,

    .., val#l) method#γ(val#m, .., val#n) method#α() method#β() method#γ() メソッド間で共有される引数をインスタンス変数に抽出
  65. class#2 val#c class val#a val#b val#c 127 メソッドの分割とクラスの凝集性 method#α() method#β()

    method#γ() class#1 val#a val#b method#α() method#β() method#γ() インスタンス変数とメソッドの関連性に従いクラスを分割
  66. 129 3. 変更のために最適化 以下の原則の遵守 1. 単⼀責務の原則 (SRP : The Single

    Responsibility Principle) 2. オープン・クローズドの原則 (OCP : The Open-Closed Principle) 3. 依存関係逆転の原則 (DIP : The Dependency Inversion Principle)
  67. 130 単⼀責務の原則 (SRP : The Single Responsibility Principle) クラスを変更する理由は 1

    つ以上存在してはならな い。(クラスは複数の責務をもってはいけない)
  68. 132 依存関係逆転の原則 (DIP : The Dependency Inversion Principle) a. 上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも「抽象」に依

    存すべきである。 b. 「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。 ソフトウェアの⽅針 • Biz Model • Biz Rule • Workflow 実装の詳細 • Technical Matter • Device 抽象
  69. 133 まとめ - Refactoring : Class 0. クラスの構造 – クラスメンバーの順序

    • “何らかの合理的な” 順序で配置 1. ⼩さいクラス︕ • 単⼀責務の原則 • 凝集度 • メソッドのリファクタリングによるクラスの最適化 2. 変更のために最適化 • 単⼀責務の原則 (SRP : The Single Responsibility Principle) • オープン・クローズドの原則 (OCP : The Open-Closed Principle) • 依存関係逆転の原則 (DIP : The Dependency Inversion Principle)
  70. 135 良い関数 (保守性の⾼い関数) 読み⼿の理解が容易な明快で明確なシステムのストーリー ※ List<Invoice> retrieveInvoicesByOrderDate(String orderDate) { 引数

    (発注⽇付⽂字列) を⽇付に変換する。 Date searchDate = Date.toDate(orderDate); 明細検索条件を作成する。 SearchCondition condition = SearchCondition.toCondition(searchDate); 条件に従い明細を検索する。 List<Invoice> invoices = datastore.selectBySearchCondition(condition); 検索結果の明細を戻す。 return invoices; } ※ 理想的には DSL の理解無しに理解可能
  71. 139 1. ⼩さいこと︕ 1. ⼩さくせよ︕ 2. 更に⼩さくせよ︕ public String method(…

    { } public String method(… { } public String method(… { } 5 〜 15 lines
  72. 140 ⼩さなコードブロック ü if ⽂、else ⽂、while ⽂等のコードブロックは 1 ⾏が理想 ü

    ブロック内ではメソッドの呼び出しのみを⾏うのが理想 ü {変数名.メソッド名} に {処理の対象.処理の内容} を表現する命名を⾏うことによりストーリ ーの明快性と明確性を現出 1. public Price computeDiscountedProductPriceOverUserStatus(Product product, User user) { 2. .. 3. if (isDiscountTargetUser(user)) 4. return computeProductPriceWithDiscount(product); 5. .. 6. return computeProductPriceWithoutDiscount(product); 7. }
  73. 141 q ワーキングメモリ ワーキングメモリ(Working Memory)とは認知⼼理学において、情報を⼀時的に保ちながら操作する ための構造や過程を指す構成概念である。作業記憶、作動記憶とも呼ばれる。 ワーキングメモリ (Oct. 24, 2018,

    22:39 UTC). In Wikipedia: The Free Encyclopedia. Retrieved from https://ja.wikipedia.org/wiki/ワーキ ングメモリ q マジカルナンバー 「The Magical number seven, plus or minus two: some limits on our capacity for processing information」という論⽂の中で、⼀度聞いただけで直後に再⽣するような場合、⽇常的なことを対象に する限り記憶容量は7個前後になるということを⽰した。この7個というのは情報量ではなく意味を持った 「かたまり(チャンク)」の数のことで、数字のような情報量的に⼩さなものも、⼈の名前のように情報 量的に⼤きな物も同じ程度、7個(個⼈差により±2の変動がある)しか覚えられないということを発表し た。 マジカルナンバー (Nov. 17, 2018, 22:39 UTC). In Wikipedia: The Free Encyclopedia. Retrieved from https://ja.wikipedia.org/wiki/ジョ ージ・ミラー_(⼼理学者) Memo
  74. 144 ユーザのステータスからディスカウント済み商品価格を算出する関数 public Price computeDiscountedProductPriceOverUserStatus(Product product, User user) { ..

    1. 対象ユーザがディスカウント対象か判断する。 if (isDiscountTargetUser(user)) 2. ディスカウント対象であれば、ディスカウント有り⾦額を算出し返す。 return computeProductPriceWithDiscount(product); .. 3. ディスカウント対象外であれば、ディスカウント無し⾦額を算出し返す。 return computeProductPriceWithoutDiscount(product); } 以下のコードは 3 つのことを⾏なっている︖
  75. 145 ユーザのステータスからディスカウント済み商品価格を算出 public Price computeDiscountedProductPriceOverUserStatus(Product product, User user) { ..

    1. 対象ユーザがディスカウント対象か判断する。 if (isDiscountTargetUser(user)) 2. ディスカウント対象であれば、ディスカウント有り⾦額を算出し返す。 return computeProductPriceWithDiscount(product); .. 3. ディスカウント対象外であれば、ディスカウント無し⾦額を算出し返す。 return computeProductPriceWithoutDiscount(product); } 関数の名前 : 抽象レベルの⽰唆
  76. 解決領域 問題領域 147 ソフトウェアの⽅針 • Biz Model • Biz Rule

    • Workflow 実装の詳細 • Technical Matter • Device 抽象レベル
  77. 148 ユーザのステータスからディスカウント済み商品価格を算出 public Price computeDiscountedProductPriceOverUserStatus(Product product, User user) { 1.

    対象ユーザがディスカウント対象か判断する。 if (isDiscountTargetUser(user)) 2. ディスカウント対象であれば、ディスカウント有り⾦額を算出し返す。 return computeProductPriceWithDiscount(product); 3. ディスカウント対象外であれば、ディスカウント無し⾦額を算出し返す。 return computeProductPriceWithoutDiscount(product); } 関数と抽象レベル ü 関数の名前はその関数の抽象レベルを表す。 ü 関数の内部が関数の抽象レベルより 1 段階低い抽象レベルのステップで構成される場合、その関数は “1 つのこと” をしている。 関数の⽬的 : 1 つの抽象レベルのことを 1 段階低い抽象レ ベルのステップに分解すること。
  78. 150 関数の抽象レベルの⼀貫性 (TO) ユーザのステータスからディスカウント済み商品価格を算出 public Price computeDiscountedProductPriceOverUserStatus(Product product, User user)

    { .. (TO ⾦額算出) 対象ユーザがディスカウント対象か判断する。 if (isDiscountTargetUser(user)) ディスカウント対象であれば、ディスカウント有り⾦額を算出し返す。 return computeProductPriceWithDiscount(product); .. ディスカウント対象外であれば、ディスカウント無し⾦額を算出し返す。 return computeProductPriceWithoutDiscount(product); } ü TO 節 (〜のために) の並びとしてプログラムの解釈が可能 ü それぞれの TO 節は次の TO 節への参照を含む解釈が可能 TO 節の流れる並びとすることで抽象レベルの⼀貫性を実現
  79. 152 public Money calculatePay(Employee e) throws InvalidEmployeeType { switch(e.type) {

    case COMMISSIONED : return calculateCommissionedPay(e); case HOURLY: return calculateHourlyPay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeeType(e.type); } } 事前探求事項② 以下のコードの問題点とその理由を探求 して下さい。
  80. 153 public Money calculatePay(Employee e) throws InvalidEmployeeType { switch(e.type) {

    case COMMISSIONED : return calculateCommissionedPay(e); case HOURLY: return calculateHourlyPay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeeType(e.type); } } 1) switch ⽂の使⽤ ü 新たな従業員タイプが追加されるたびに⼤型化 (※) 2) 複数のことを実施 ü 従業員のタイプ (区分) の判定、従業員のタイプ毎の給与計算 3) 単⼀責務の原則 (SRP : The Single Responsibility Principle) 違反 ü 将来変更が必要となる理由が 2 つ以上存在 (従業員のタイプの追加、給与計算ロジックの変更) 4) オープン・クローズドの原則 (OCP : The Open-Closed Principle) 違反 ü 従業員のタイプの追加のたびに発⽣するコードの変更
  81. 154 public Money calculatePay(Employee e) throws InvalidEmployeeType { switch(e.type) {

    case COMMISSIONED : return calculateCommissionedPay(e); case HOURLY: return calculateHourlyPay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeeType(e.type); } } 事前探求事項③ 以下のコードをリファクタリングして下 さい。
  82. 155 public interface Employee { public Money calculatePay(); } public

    interface EmployeeFactory { public Employee makeEmployee(EmployeeRecord record) throws InvalidEmployeeType; } public class EmployeeFactoryBean implements EmployeeFactory { public Employee makeEmployee(EmployeeRecord record) throws InvalidEmployeeType { switch (recorde.type) { case COMMISSIONED: return new CommissionedEmployee(record); case HOURLY: return new HourlyEmployee(record); case SALARIED: return new SalariedEmployee(record); default: throw new InvalidEmployeeType(record.type); } } } 多態を好む︕
  83. 156 5. 名前 ü 内容を 明快・明確 に表現する名前を付ける︕ ü 命名規約 +

    語彙 の⼀貫性 ü 動詞 または 動詞句 (動詞 + 名詞) ü 戻り値、引数、処理の順序 の明快に表現 ü ⻑い名前の付与を厭わない︕ ü 命名に時間をつかうことを厭わない︕
  84. 158 なぜ少ないほど良い︖︖︖ ワーキングメモリとマジカルナンバー ü メソッドのコードを把握するには引数を念頭に置きながらの Code Reading が必要 ü “念頭に置く”

    ⇒ Working Memory & Magical Number の制約 メソッドとは異なる抽象レベル ü 通常、メソッドと引数の抽象レベルは異なり、メソッドの Code Reading において、引数の “実装の詳細” を把握する必要性
  85. 159 フラグ・セレクタ 引数︖︖︖ ü boolean 型 または 列強型等 の引数によりメソッドの挙動を制御 フラグ・セレクタ

    引数をもつメソッドは ”複数のこと” を⾏っている︕ ü javax.servlet.http.HttpServletRequest#getSession(boolean create) の場合 • create == true Ø Session が存在すれば 返す Ø Session が存在しなければ 新規作成 して 返す • create == false Ø Session が存在すれば 返す Ø Session が存在しなければ null を 返す フラグ・セレクタ 引数の使⽤を避ける︕ フラグ・セレクタ 引数
  86. 160 1 つの概念を構成する要素 ü 概念の構成が明確な例 Ø moveTo(x, y) ü 概念の構成が不明確な例

    Ø writeField(out, name) : 第 1 引数が不明確 1 つの概念を構成する要素の “⾃然な” 順序 ü 順序が明確な例 Ø assertExpectedEqualsActual(Expected, actual) ü 順序が不明確な例 Ø assertEquals(expected, actual) ※ 引数の順序は規約でありメソッドの使⽤ が学習することが必須 複数の引数は 1 つの概念を構成する順序付けられた要素 2 つ以上の引数
  87. 163 7. 副作⽤を避ける︕ 出⼒引数を使⽤しない︕ 副作⽤︖︖︖ ü メソッドが⾏う “1 つのこと” 以外の隠蔽的な処理

    出⼒引数 ü 戻り値が void の関数における引数のオブジェクトに対する出⼒ ü 出⼒引数の例 Ø s が Footer なのか︖, ⼊⼒なのか︖ Ø Footer を s に追加するのか︖ , 出⼒なのか︖ appendFooter(s) public void appendFooter(StringBuilder report)
  88. 164 8. 分離原則 メソッドは⼤別すると以下に分類可能 1. 何らかの処理を実施 2. 何らかの値を返却 1, 2

    を同時に⾏わない︕ boolean set(String key, String value) void set(String key, String value) boolean isExist(String key)
  89. 165 9. 例外 戻りコードではなく例外を使⽤ 分離原則に違反する可能性 ü 何らかの処理を実⾏する本来は void 型の関数で戻りコードを返却 呼び出し元に対するエラー処理の強制

    ü 呼び出し元に対してメソッドの呼び出し直後のエラー処理を強制 Ø 呼び出し元のメソッドが 2 つのことを⾏う可能性 ü 例外を使⽤すれば メソッドの呼び出し と エラー処理 を分離可能 Ø 例外機構の使⽤、AOP 等
  90. 166 まとめ - Refactoring : Method 0.良い関数とは︖︖︖ 1.良い関数の原則 • ⼩さい︕

    • 1 つのことを⾏う︕ • 1 つの抽象レベル︕ • switch ⽂ • 名前 • 引数 • 副作⽤を避ける︕ • 分離原則 • 例外
  91. 174 • 変数 • 定数 • メソッド • 引数 •

    クラス • パッケージ • アーカイブ • ファイル • ディレクトリ • etc. コードにおける名前 (命名) の対象
  92. 175 名前 (命名) の原則 1. 意図の明確化 2. 偽情報混⼊の回避 3. 汎⽤的な名前の⾃粛

    4. ノイズワードの回避 5. 発⾳可能 6. 検索可能 7. 独⾃ルールによるコード化の回避 8. 品詞 9. 命名の⼀貫性 10.スコープと変数名 11.適切な領域の⽤語の使⽤ 12.気取らず 明確・正確 な表現 13.明確・詳細 な単語 14.⽂脈の付与 15.問題指向の名前 16.正確な反意語の使⽤
  93. 176 1. 意図の明確化 • なぜ対象が存在するのか︖ • 対象は何をするのか︖ • 対象はどのように使⽤するのか︖ 可読性

    (明瞭性、簡潔性、明快性) の向上 特に 定数、変数、メソッド、 クラス の名前
  94. 177 意図の明確化の例 int d; // 日単位の経過時間 int h; // 時間単位の経過時間

    int m; // 分単位の経過時間 int s; // 秒単位の経過時間 int elapsedTimeInDays; int daysSinceCreation; int daysSinceModification; int fileAgeInDays; … 対象、計測対象、計測の単位の明確化
  95. 178 public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>();

    for (int[] x : theList) if (x[] == 4) list1.add(x); return list1; } 何が良くない︖︖なぜ良くない︖
  96. 180 public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>();

    for (int[] x : theList) if (x[] == 4) list1.add(x); return list1; } 何が良くない︖︖なぜ良くない︖ 何をするのか︖ 何が⼊っている︖ どんな意味がある︖ どう使うのか︖
  97. 181 public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>();

    for (int[] cell : gameBoard) if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell); return flaggedCells; } 意図の明確化により暗黙さを排除 (暗黙 : ⽂脈の明快さ)
  98. 182 public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>();

    for (int[] cell : gameBoard) if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell); return flaggedCells; } まだ良くない︖︖
  99. 183 public List<Cell> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>();

    for (Cell cell : gameBoard) if (cell.isFlagged()) flaggedCells.add(cell); return flaggedCells; } “構造” のリファクタリング
  100. 184 2. 偽情報混⼊の回避 • 意味を曖昧にする (意図の不明確化) ような 間違った情報 や 誤解を

    与える情報 の排除 • 意味が⽂脈によって変化する、意図した意味以外に複数の意味が存在 するような単語の排除
  101. 185 偽情報の例 ü 意図した以外の意味を連想するような略語 ü プログラミング⾔語において重要な意味を持つ単語 (API、型、スコープ、等) ü “ごく⼀部” が異なる名前

    ü 形態的に紛らわしい⽂字 “l” (⼩⽂字 L) や “O” (⼤⽂字 O) の使⽤ Account[] accountList; Account[] accounts; Account[] accountGroup; int a = 1; if ( O == 1 ) a == O1; else l = O1;
  102. 188 ノイズワードの例 ü 無情報な名前 (a0, a1, …aN 等) ü “テキトウな⽂字”

    の付与 (Product, ProductInfo, ProductData, ProductDesc 等) ü 冗⻑的な単語の付与 (Data, Table, Info, Variable 等) public static void copyChars(char[] a1[], char a2[]) { for (int i = 0; i < a1.length; i++) { a2[i] = a1[i] } } public static void copyChars(char[] soucrce, char destination) { for (int i = 0; i < a1.length; i++) { a2[i] = a1[i] } }
  103. 190 1. 語 : ⾔語学では語は独⽴して発声できる最⼩の単位である。 語 (Nov. 25, 2018, 01:23

    UTC). In Wikipedia: The Free Encyclopedia. Retrieved from https://ja.wikipedia.org/wiki/語 2. 会話 : 2⼈もしくはそれ以上の主体が、主として⾔語の発声・⼿話・ジェスチャーなどによる意思表 ⽰によって共通の話題をやりとりするコミュニケーションや、あるいは話をする⾏為全般(内容・様 式など)のこと。 会話 (Jan. 10, 2019, 08:12 UTC). In Wikipedia: The Free Encyclopedia. Retrieved from https://ja.wikipedia.org/wiki/会話 3. ⼤脳が従事する機能 Basic Vision: An Introduction to Visual Perception Revised Edition by Robert Snowden, Peter Thompson, Tom Troscianko (2012) 視覚 50% 聴覚 10% 触覚・味覚 等 10% 運動 20% その他 10% Memo
  104. 193 Column – インターフェース名、実装名 実装名 • AccountFactoryImpl • AccountFactory •

    AccountFactoryBean • CAccountFactory インターフェース名 • AccountFactory • IAccountFactory どれが良い︖なぜ良い︖︖
  105. 195 品詞 クラス名 : 名詞 または 名詞句 ü Camera ü

    LaptopComputer ü Lens メソッド名 : 動詞 または 動詞句 ü takePhoto ü pressShutter ü setCameraOnTripod
  106. 199 ソフトウェアの⽅針 • Biz Model • Biz Rule • Workflow

    実装の詳細 • Technical Matter • Device Upper Layer - 上位層 Lower Layer – 下位層 ソフトウェアの “⽅針” と “詳細”
  107. 解決領域 問題領域 200 ソフトウェアの⽅針 • Biz Model • Biz Rule

    • Workflow 実装の詳細 • Technical Matter • Device ソフトウェアの “問題領域” と “解決領域”
  108. 201 Domain (Biz) Integration Web (UI) Framework Middleware OS Network,

    Hardware, etc. “問題領域” と “解決領域” はどこ︖
  109. 202 Domain (Biz) Integration Web (UI) Framework Middleware OS Network,

    Hardware, etc. 解決領域 解決領域 問題領域
  110. 206 13. 明確・詳細 な単語 ⽂脈に合わせより 明確・詳細 な 代替可能な単語 を検討する。 代替可能な単語

    send deliver, dispatch, announce, distribute, route, post, ship find search, extract, locate, recover, seek, hunt, look for, dig out start launch, create, begin, open, depart make create, set up, build, generate, compose, add, new get compute, estimate, calculate, etc. ※ process, handle, perform, output, get 等の基本動詞を多⽤しない。
  111. 208 15. 問題指向の名前 ⽅法 (How) でなく モノ・コト (What) を表現する。 解決ではなく問題

    を表現する。 inputRecord input : ⼊⼒ ⇒ ⽅法 employeeRecord employee : 社員 ⇒ モノ・コト
  112. 209 16. 正確な反意語の使⽤ 正確な反意語により⼀貫性を保つ。 add/remove Increment/decremen t open/close begin/end insert/delete

    show/hide first/last lock/unlock source/target get/put old/new up/down get/set min/max start/stop next/previous create/destroy source/destination
  113. 211 ダメなコードをコメントで取り繕ってはいけない。書き直すのだ。 Brian Kernighan, P. J. Plauger - The Elements

    of Programming Style 構造が雑然とし表現が不明確なコードをコメントで改善はできない。
  114. 213 Column – コメントの悪弊 1)リファクタリングの不⾜ ü 複雑な構造のコードを取り繕うコメント ü 不適切な表現のコードを取り繕うコメント 2)コードの変更

    ü コードの変更に対するコメントの⾮追随・⾮適合 3)不正確な情報・誤解を与えるコメント 4)コメントは置き去り ⇒ 信頼できる正確な情報はソースコード
  115. 216 “良い” コメント 1.著作権・ライセンス “コードでは表現不可能な情報” の記載 2.有益な情報の付与 (※) 3.意図・背景 の説明

    4.明確化 (※) 5.警告 (※) 6.TODO 7.公開 API ※ 可能な限りコードの構造と表現の改善によりコメントの使⽤を回避することが望ましい。
  116. 217 1. 著作権・ライセンス /* Copyright 2017 The Kubernetes Authors. Licensed

    under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
  117. 218 2. 有益な情報の付与 (※) // kk:mm:ss EEE, MMM dd, yyyy

    にマッチする正規表現 Pattern timeMatcher = Pattern.compile(“\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*”); 情報の付与にコメントが “⼿っ取り早い” ケース 上記の例において、コメント付与の理由が以下の場合 ü 正規表現⾃体の解説 ü 当該メソッドの主たる関⼼事が正規表現の⽣成以外 当該コードブロックをメソッドに抽出し、メソッド名に適切な名前を付与することでコメントの使⽤を回 避可能 ü 正規表現⾃体の解説は不要 (API ドキュメントを参照)
  118. 219 3. 意図・背景 の説明 構造や表現では説明できない コードの背景 や 決定の意図 • あるコードの理由が当該箇所から抽象度的に遠い場所にある。

    • あるコードの理由がビジネス上の説明を要する。 // 競合状態を発⽣させるために⼤量のスレッドを⽣成 … // 〜の理由から〜の場合の基準値を〜に設定 … // 特殊ケースとして〜の場合は〜を判定に使⽤ …
  119. 220 4. 明確化 (※) 曖昧な引数や戻り値の意味を解読可能なように翻訳 標準ライブラリや 3rd Party ライブラリ等 “構造や表現を変更できないコード”

    に対する翻訳コメント assertTrue(ab.compareTo(ab) == 0); // ab == ab assertTrue(aa.compareTo(ab) == -1); // aa < ab assertTrue(ab.compareTo(aa) == 1); // ab > aa • コメントが間違っている可能性 != 0 のため、動作確認の 作業は必須 • コメントは正確に記述しアップデートを確実に実施
  120. 222 6. TODO 何らかの理由で現在はできないと判断したやらなければならない仕事 • 推奨されない API 使⽤箇所の修正 • 依頼事項

    • 将来予定されている事象に合わせた変更 可能な限り TODO に対応した状態を維持する︕ TODO = 「あとでやる」という覚え書き
  121. 227 // このコンポーネントのプロセッサ遅延 private int backgroundProcessorDelay = -1; // このコンポーネントでライフサイクルイベントをサポートするためのもの

    private LifecycleSupport lifecycle = new LifecycleSupport(this); // このコンテナのコンテナイベントリスナ private ArrayList listeners = new ArrayList(): // コンテナを関連付けるための Loader の実装 private Loader loader = null; // このコンテナと関連付けられた Logger 実装 private Log logger = null; // 関連付けられた logger の名前 private String logName = null; 冗⻑だわ︕︕
  122. // this.closed が false の場合、指定時間 (ミリ秒) 待機 // 指定の待機時間を超過したら例外をスロー 230

    public synchronized void waitForClose(long timeoutMillis) throws Exception { if (!this.closed) { wait(timeoutMillis); if (!this.closed) { throw new Exception(); } } } // this.closed が true まで待機 誤解よ︕︕
  123. 231 3. 冗⻑な規約 コメントの記述を強制する規約 /** * @param firstName 名前 (名)

    * @param lastName 苗字 (姓) * @phoneNumber 電話番号 … */ public void addContact(String firstName, String lastName, String phoneNumber, ...)
  124. 233 5. 閉じカッココメント 関数のネストを減らせ︕ try { while (true) { if

    (..) { try { } // try catch (Exception e) { .. } // catch } // if } // while } // try
  125. 235 7. コードのコメントアウト コードは消しても無くならない (※) 消せ︕ // 2019/04/15 irof 修正開始

    // hoge = fuga(1); hoge = fuga(2); // 2019/04/15 irof 修正終了 ※ ソースコード上から消しても ソースコード管理システム から消えることはない。
  126. 236 8. ⾮局所的コメント コメントは “対象となるコードのそば” に記載︕ システム全体におよぶ情報を局所的なコメントの⽂脈に記載しない。 • Data Store

    の接続情報を特定の Data Store Access Component に記載 • Web Server の接続情報を特定の REST Component に記載 var server = http.createServer(function(req, res) { if (req.method == 'GET') { ... res.write(writeResponse(req, res)); res.end(); ... }).listen(assignPort(8080)); .. let assignPort = (port) => port; // デフォルトポートは 8080 • assignPort 関数にはデフォルトポート番号を 決定する権限がない。 • デフォルトポートに関するコメントは assignPort 関数から “離れたところにあるコー ド” について語っている。
  127. 241 • インフラ関連の ビルド & テスト • ⾮機能関連のテスト • リリース

    • 構成ドリフトの排除 Dev Test Production Everything in CI/CD
  128. CONFIDENTIAL linkedin.com/company/red-hat youtube.com/user/RedHatVideos facebook.com/redhatinc twitter.com/RedHat 243 Red Hat is the

    worldʼs leading provider of enterprise open source software solutions. Award- winning support, training, and consulting services make Red Hat a trusted adviser to the Fortune 500. Thank you