SlideShare a Scribd company logo
例外設計
       における大罪
              和田 卓人 (a.k.a id:t-wada or @t_wada)
                  Jun 27, 2012 @ java-ja
12年6月28日木曜日
自己紹介
 名前:          和田 卓人 (わだ たくと)

 ブログ: http://d.hatena.ne.jp/t-wada

 メール: takuto.wada@gmail.com

 Twitter: http://twitter.com/t_wada

 タワーズ・クエスト株式会社
 取締役社長
12年6月28日木曜日
よろしく
              おねがい
               します
12年6月28日木曜日
例外設計
       における大罪
12年6月28日木曜日
無視



12年6月28日木曜日
public List<Entity> findByKeyword (String keyword)
              throws Exception
{
       Connection conn = DriverManager.getConnection(...);
       PreparedStatement stmt = conn.prepareStatement(
           "SELECT * FROM Movie WHERE title LIKE ?");
       stmt.setString(0, keyword);
       ResultSet rs = stmt.executeQuery();
       List<Entity> entities = new ArrayList<Entity>();
       while (rs.next()) {
           // ...
       }
       return entities;
}



12年6月28日木曜日
きのこ93:
              エラーを無視するな

 • 例外��理を行わない言い訳
  • コードの流れが追いにくくなる
  • 納期が迫っている
  • エラーが戻ることが無い関数もある
  • 遊びで書いただけのプログラムだか
         ら…

12年6月28日木曜日
きのこ93:
              エラーを無視するな
   • エラーを無視しても何も良いことは無い
    • 不安定なコード
    • セキュリティ上問題のあるコード
    • 貧弱な構造とインターフェイス
   • どうする?
    • 戻り値を使う
    • 例外を使う
12年6月28日木曜日
隠蔽



12年6月28日木曜日
きのこ27: 死ぬはずのプログラムを
               無理に生かしておいてはいけない


   try-catchブロックをコードベースに
   大量に入れれば、「例外が発生しても
   絶対に止まらない」というアプリケー
   ションを作ることが可能なはずです。
   ただ、これは、もう死んでいる人の体
   を釘か何かで固定し、無理矢理立った
   状態にしているようなものです
   が・・・。
12年6月28日木曜日
}	
  finally	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  if	
  (rs	
  !=	
  null)	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  try	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  rs.close();
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  catch	
  (SQLException	
  e)	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  log.warn(e.getMessage(),	
  e);
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }
      	
  	
  	
  	
  	
  	
  	
  	
  }
      	
  	
  	
  	
  	
  	
  	
  	
  if	
  (stmt	
  !=	
  null)	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  try	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  stmt.close();
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  catch	
  (SQLException	
  e)	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  log.warn(e.getMessage(),	
  e);
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }
      	
  	
  	
  	
  	
  	
  	
  	
  }
      	
  	
  	
  	
  	
  	
  	
  	
  if	
  (con	
  !=	
  null)	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  try	
  {
                                                                              遠い記憶
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  con.close();
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  catch	
  (SQLException	
  e)	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  log.warn(e.getMessage(),	
  e);
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }
      	
  	
  	
  	
  	
  	
  	
  	
  }
      }                                                                                           最近の Java に関しては try-with-resources で検索すべし
12年6月28日木曜日
乱用



12年6月28日木曜日
public int sum(List<Integer> toSum) {
      Iterator<Integer> iter = toSum.iterator();
      int sum = 0;
      try {
        while(true) {
          sum += iter.next();
        }
      } catch (NoSuchElementException e) {
      }
      return sum;
    }




12年6月28日木曜日
項目57: 例外的状態にだけ
                 例外を使用する
• 例外は、その名が示す通り、例外的条
     件に対してのみ使用するべきです。通
     常の制御フローに対しては、決して使
     用すべきではありません。

• 上手く設計された API は、通常の制御
     フローに例外を使用することを、クラ
     イアントに強制してはなりません。

12年6月28日木曜日
ヒント34: 例外は例外的な
               問題のみに使用すること

• 「すべての例外ハンドラーを除去して
     も、このプログラムは動作することが
     できるだろうか?」

• 答えが「ノー」であれば、例外では無
     い状況下で例外が使われている


12年6月28日木曜日
過剰
  防御




12年6月28日木曜日
防御的プログラミング
      の是非


12年6月28日木曜日
package javaja;

public class Defensive {
  private UserRepository userRepository;
  public Defensive(UserRepository userRepository) {
     this.userRepository = userRepository;
  }

    public User createUser(String name, int age) {
      if (name == null) {
         throw new NullPointerException("name is null");
      }
      if (age < 0) {
         throw new IllegalArgumentException("age is negative");
      }
      if (name.isEmpty()) {
         throw new IllegalArgumentException("name is empty");
      }
      User user = this.userRepository.create(name, age);
      return user;
    }
}
12年6月28日木曜日
package javaja;

public class UserRepository {
  public User create(String name, int age) {
     if (name == null) {
       throw new NullPointerException("name is null");
     }
     if (age < 0) {
       throw new IllegalArgumentException("age is negative");
     }
     if (name.isEmpty()) {
       throw new IllegalArgumentException("name is empty");
     }
     return new User(name, age);
  }
}



12年6月28日木曜日
答えは
          あるのか
12年6月28日木曜日
契約による
                設計
              Design by Contract (DbC)


12年6月28日木曜日
Bertrand
       Meyer




12年6月28日木曜日
契約による設計
              Design by Contract (DbC)



• あるルーチン            にお
                   (注: メソッドと読み替えても良い)

     けるすべての事前条件が呼び出し側に
     よって満足された場合、そのルーチン
     は作業完了時にすべての事後条件とす
     べての不変表明を保証する



12年6月28日木曜日
契約による設計
              Design by Contract (DbC)


• 冗長性のある検証は実際損傷を与える
• システム全体の視点で見るとき、シン
     プルさ(simplicity)が重要になる

 • 複雑さは品質の敵である
• 過剰に防御するのではなく、誰の責任
     なのかをはっきりさせる
12年6月28日木曜日
契約による設計
              Design by Contract (DbC)



• 例外処理とは、予想外の実行時状態に対
    処するメカニズム

• 失敗とは、ルーチンの実行で、契約を満
    足させられなくなること



12年6月28日木曜日
契約による設計
              Design by Contract (DbC)

• ルーチンはリトライ(Retry)か組織的パ
   ニック(Organized Panic)のどちらかで
   例外を処理する

  • リトライとはルーチン本体を再び実行
       すること

  • 組織的なパニックはルーチンを失敗に
       して、そのルーチンを呼び出したもの
       に例外を送る
12年6月28日木曜日
12年6月28日木曜日
おまけ
12年6月28日木曜日
きのこ21: 技術的例外とビジネ
       ス例外を明確に区別する

• 技術的例外とビジネス例外がある。こ
    れらを同じ例外階層構造に入れてはな
    らない

• 技術的例外は貫通させてフレームワー
    クに任せる。ビジネス例外は準正常系
    なので呼び出し側で対処する
12年6月28日木曜日
きのこJ6:
     見知らぬ人ともうまくやるには


     「出来てはならぬことを禁じる」の
     ではなく、はじめから「出来ていいこ
     とだけを出来るようにする」と考える
     のです。



12年6月28日木曜日
Effective Java 2nd

 • 回復可能な状態にはチェックされる例
     外を、プログラミングエラーには実行
     時例外を使用する

 • チェックされる例外を不必要に使用す
     るのを避ける

 • 標準例外を使用する
 • 抽象概念に適した例外をスローする
12年6月28日木曜日
Effective Java 2nd

 • 各メソッドがスローするすべての例外
     を文書化する

 • 詳細メッセージにエラー記録情報を含
     める

 • エラーアトミック性に努める
 • 例外を無視しない
12年6月28日木曜日
チェック例外の
    代償は、開放/
    閉鎖原則に違反
    する点です

    => 検索すれば良質の議論が読め
    ます

12年6月28日木曜日
まとめ
12年6月28日木曜日
まとめ
         • 例外処理/設計における大罪
            • 無視
            • 隠蔽
            • 乱用
            • 過剰防御
         • 「契約による設計」がひとつの答え
12年6月28日木曜日
名著を読もう!




12年6月28日木曜日
ご清聴ありがとうございました




12年6月28日木曜日

More Related Content

例外設計における大罪

  • 1. 例外設計 における大罪 和田 卓人 (a.k.a id:t-wada or @t_wada) Jun 27, 2012 @ java-ja 12年6月28日木曜日
  • 2. 自己紹介 名前: 和田 卓人 (わだ たくと) ブログ: http://d.hatena.ne.jp/t-wada メール: takuto.wada@gmail.com Twitter: http://twitter.com/t_wada タワーズ・クエスト株式会社 取締役社長 12年6月28日木曜日
  • 3. よろしく おねがい します 12年6月28日木曜日
  • 4. 例外設計 における大罪 12年6月28日木曜日
  • 6. public List<Entity> findByKeyword (String keyword) throws Exception { Connection conn = DriverManager.getConnection(...); PreparedStatement stmt = conn.prepareStatement( "SELECT * FROM Movie WHERE title LIKE ?"); stmt.setString(0, keyword); ResultSet rs = stmt.executeQuery(); List<Entity> entities = new ArrayList<Entity>(); while (rs.next()) { // ... } return entities; } 12年6月28日木曜日
  • 7. きのこ93: エラーを無視するな • 例外処理を行わない言い訳 • コードの流れが追いにくくなる • 納期が迫っている • エラーが戻ることが無い関数もある • 遊びで書いただけのプログラムだか ら… 12年6月28日木曜日
  • 8. きのこ93: エラーを無視するな • エラーを無視しても何も良いことは無い • 不安定なコード • セキュリティ上問題のあるコード • 貧弱な構造とインターフェイス • どうする? • 戻り値を使う • 例外を使う 12年6月28日木曜日
  • 10. きのこ27: 死ぬはずのプログラムを 無理に生かしておいてはいけない try-catchブロックをコードベースに 大量に入れれば、「例外が発生しても 絶対に止まらない」というアプリケー ションを作ることが可能なはずです。 ただ、これは、もう死んでいる人の体 を釘か何かで固定し、無理矢理立った 状態にしているようなものです が・・・。 12年6月28日木曜日
  • 11. }  finally  {                if  (rs  !=  null)  {                                try  {                                                rs.close();                                }  catch  (SQLException  e)  {                                                log.warn(e.getMessage(),  e);                                }                }                if  (stmt  !=  null)  {                                try  {                                                stmt.close();                                }  catch  (SQLException  e)  {                                                log.warn(e.getMessage(),  e);                                }                }                if  (con  !=  null)  {                                try  { 遠い記憶                                                con.close();                                }  catch  (SQLException  e)  {                                                log.warn(e.getMessage(),  e);                                }                } } 最近の Java に関しては try-with-resources で検索すべし 12年6月28日木曜日
  • 13. public int sum(List<Integer> toSum) { Iterator<Integer> iter = toSum.iterator(); int sum = 0; try { while(true) { sum += iter.next(); } } catch (NoSuchElementException e) { } return sum; } 12年6月28日木曜日
  • 14. 項目57: 例外的状態にだけ 例外を使用する • 例外は、その名が示す通り、例外的条 件に対してのみ使用するべきです。通 常の制御フローに対しては、決して使 用すべきではありません。 • 上手く設計された API は、通常の制御 フローに例外を使用することを、クラ イアントに強制してはなりません。 12年6月28日木曜日
  • 15. ヒント34: 例外は例外的な 問題のみに使用すること • 「すべての例外ハンドラーを除去して も、このプログラムは動作することが できるだろうか?」 • 答えが「ノー」であれば、例外では無 い状況下で例外が使われている 12年6月28日木曜日
  • 17. 防御的プログラミング の是非 12年6月28日木曜日
  • 18. package javaja; public class Defensive { private UserRepository userRepository; public Defensive(UserRepository userRepository) { this.userRepository = userRepository; } public User createUser(String name, int age) { if (name == null) { throw new NullPointerException("name is null"); } if (age < 0) { throw new IllegalArgumentException("age is negative"); } if (name.isEmpty()) { throw new IllegalArgumentException("name is empty"); } User user = this.userRepository.create(name, age); return user; } } 12年6月28日木曜日
  • 19. package javaja; public class UserRepository { public User create(String name, int age) { if (name == null) { throw new NullPointerException("name is null"); } if (age < 0) { throw new IllegalArgumentException("age is negative"); } if (name.isEmpty()) { throw new IllegalArgumentException("name is empty"); } return new User(name, age); } } 12年6月28日木曜日
  • 20. 答えは あるのか 12年6月28日木曜日
  • 21. 契約による 設計 Design by Contract (DbC) 12年6月28日木曜日
  • 22. Bertrand Meyer 12年6月28日木曜日
  • 23. 契約による設計 Design by Contract (DbC) • あるルーチン にお (注: メソッドと読み替えても良い) けるすべての事前条件が呼び出し側に よって満足された場合、そのルーチン は作業完了時にすべての事後条件とす べての不変表明を保証する 12年6月28日木曜日
  • 24. 契約による設計 Design by Contract (DbC) • 冗長性のある検証は実際損傷を与える • システム全体の視点で見るとき、シン プルさ(simplicity)が重要になる • 複雑さは品質の敵である • 過剰に防御するのではなく、誰の責任 なのかをはっきりさせる 12年6月28日木曜日
  • 25. 契約による設計 Design by Contract (DbC) • 例外処理とは、予想外の実行時状態に対 処するメカニズム • 失敗とは、ルーチンの実行で、契約を満 足させられなくなること 12年6月28日木曜日
  • 26. 契約による設計 Design by Contract (DbC) • ルーチンはリトライ(Retry)か組織的パ ニック(Organized Panic)のどちらかで 例外を処理する • リトライとはルーチン本体を再び実行 すること • 組織的なパニックはルーチンを失敗に して、そのルーチンを呼び出したもの に例外を送る 12年6月28日木曜日
  • 29. きのこ21: 技術的例外とビジネ ス例外を明確に区別する • 技術的例外とビジネス例外がある。こ れらを同じ例外階層構造に入れてはな らない • 技術的例外は貫通させてフレームワー クに任せる。ビジネス例外は準正常系 なので呼び出し側で対処する 12年6月28日木曜日
  • 30. きのこJ6: 見知らぬ人ともうまくやるには 「出来てはならぬことを禁じる」の ではなく、はじめから「出来ていいこ とだけを出来るようにする」と考える のです。 12年6月28日木曜日
  • 31. Effective Java 2nd • 回復可能な状態にはチェックされる例 外を、プログラミングエラーには実行 時例外を使用する • チェックされる例外を不必要に使用す るのを避ける • 標準例外を使用する • 抽象概念に適した例外をスローする 12年6月28日木曜日
  • 32. Effective Java 2nd • 各メソッドがスローするすべての例外 を文書化する • 詳細メッセージにエラー記録情報を含 める • エラーアトミック性に努める • 例外を無視しない 12年6月28日木曜日
  • 33. チェック例外の 代償は、開放/ 閉鎖原則に違反 する点です => 検索すれば良質の議論が読め ます 12年6月28日木曜日
  • 35. まとめ • 例外処理/設計における大罪 • 無視 • 隠蔽 • 乱用 • 過剰防御 • 「契約による設計」がひとつの答え 12年6月28日木曜日