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

複雑な条件と戦う

philomagi
September 04, 2019

 複雑な条件と戦う

複雑な条件の組み合わせで
- テストが難しく
- 実装が肥大化し
- 変更が辛い
状態になったコードを改善する。

Specification Pattern/仕様パターン について、「実装的に嬉しいこと」にフォーカスして整理。

philomagi

September 04, 2019
Tweet

More Decks by philomagi

Other Decks in Technology

Transcript

  1. 5 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga }
  2. 6 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga }
  3. 7 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } 組み合わせが複雑になる
  4. 8 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } 組み合わせが複雑になる → 入力の組み合わせも増える
  5. 9 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } 組み合わせが複雑になる → 入力の組み合わせも増える → 考えるべき事項が増える
  6. 10 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } 組み合わせが複雑になる → 入力の組み合わせも増える → 考えるべき事項が増える → テストが難しくなる
  7. 11 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz }
  8. 12 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz }
  9. 13 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } パターンが増える
  10. 14 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } パターンが増える → 実装が増える
  11. 15 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } パターンが増える → 実装が増える → またパターンが増える
  12. 16 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } パターンが増える → 実装が増える → またパターンが増える → 実装が肥大化していく
  13. 17 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } パターンが増える → 実装が増える → またパターンが増える → 実装が肥大化していく → 内容の把握が大変になる
  14. 18 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } パターンが増える → 実装が増える → またパターンが増える → 実装が肥大化していく → 内容の把握が大変になる → 変更が辛くなる
  15. 26 1. 「条件オブジェクト」のIFを定義 interface Specification { isSatisfiedBy(param): boolean and(other: Specification):

    Specification or(other: Specification): Specification } 条件を満たしているかどうか判 定するメソッド
  16. 27 1. 「条件オブジェクト」のIFを定義 interface Specification { isSatisfiedBy(param): boolean and(other: Specification):

    Specification or(other: Specification): Specification } 条件オブジェクト同士を 組合せるためのメソッド
  17. 28 2. 具体的な「条件オブジェクト」を実装 class HogeSpecification implements Specification { isSatisfiedBy(param): boolean

    { return param.A && !param.B && param.C > 0 } // and/or の実装例は↓で // https://gist.github.com/tooppoo/2769cb3e842589d4b6c9392e3778408f }
  18. 29 3. 「複雑な条件」を組み立てる function isXxx( A: boolean, B: boolean, C:

    number, D: number ): boolean { const spec = new HogeSpecification().and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) }
  19. 30 3. 「複雑な条件」を組み立てる function isXxx( A: boolean, B: boolean, C:

    number, D: number ): boolean { const spec = new HogeSpecification().and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) }
  20. 31 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After
  21. 32 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After
  22. 33 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After 条件オブジェクトは互いに独立している
  23. 34 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After 条件オブジェクトは互いに独立している 条件オブジェクト毎に個別にテストできる const spec = new HogeSpecification() expect(spec.isSatisfiedBy(param)).toBe(true)
  24. 35 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D return hoge && fuga } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After 条件オブジェクトは互いに独立している 条件オブジェクト毎に個別にテストできる 一つのテストで考えることが減るので、 テストが簡単になる
  25. 36 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) .or(new PiyoSpecification()) .or(new FizzSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After
  26. 37 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) .or(new PiyoSpecification()) .or(new FizzSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After
  27. 38 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) .or(new PiyoSpecification()) .or(new FizzSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After 実装の追加はオブジェクトの組み立てのみ 具体的な条件はオブジェクト内部に隠蔽
  28. 39 function isXxx( A: boolean, B: boolean, C: number, D:

    number ): boolean { const hoge = A && !B && C > 0 const fuga = (A || B) || C < D const piyo = B && C > 0 && D < 0 const fizz = (A && C > 0) || (!B && D >= C) return hoge && fuga || piyo || fizz } function isXxx( A: boolean, B: boolean, C: number, D: number ): boolean { const spec = new HogeSpecification() .and(new FugaSpecification()) .or(new PiyoSpecification()) .or(new FizzSpecification()) return spec.isSatisfiedBy({ A, B, C, D }) } // Before // After 実装の追加はオブジェクトの組み立てのみ 具体的な条件はオブジェクト内部に隠蔽 • 追加が最低限なので、肥大化しにくい • 簡単に変更できる