ある寒い日、私のカヤックは、テネシー州東部にあるState Line Fallsという16フィートの高さの滝の上に、危なっかしそうに止まっていました。この急流には、ボートも人も粉々にするという嫌なうわさがあります。上から見る限りでは、先に危険が潜んでいる様子はありません。でも、私にはわかっていました。ここ数日間、目覚めている間はずっとガイドブックの説明が頭から離れませんでした。トラックほどの大きさの5つの岩が4つのコースを作っています。そこを抜ける直前に滝の全貌が見えるはずです。4つのコースのうち3つは、怖いもの知らずの人でさえ恐れるほどの危険なコースと言われています。流れは、4つめの「やさしい」コースを進みながら加速し、ぎざぎざの岩にぶつかります。私の職業はプログラマーで、2人の子供を持つ父親です。カヤック選手としての腕前は、中級といったところです。私には、ガイドブックで「限界」と記されている最難関の滝に挑戦するような資格はありません。それなのに、なぜ私はこんな場所でこんなことをしているのでしょうか。
私の中にいるプログラマーは何度も同じ質問をしました。私は、スケジュールによって高まるプレッシャーと、失敗という恐ろしい結果への不安を抱えながら、慣れない問題に取り組もうとするたびに、数え切れないほど何度も同じような崖っぷちに立たされてきました。この記事で私は、プログラマーの混乱をうまく解消できるツールとしてアンチパターンを紹介しようと思います。また、アンチパターンがデザイン・パターンの研究を補完できる方法についても説明します。developerWorksを頻繁にご覧になっているのであれば、多くのデザイン・パターンが紹介され、その内在する価値についての議論をご覧になっていることでしょう。バグ・パターンに関するEric Allen氏の記事(参考文献を参照)では、その有用性について説明されていますが、皆さんはおそらく、アンチパターンに関しては、バグ・パターンに対するほど理解されているわけではないでしょう。しかしそれは、皆さんだけではありません。開発者は何年もの間デザイン・パターンに関して洞察に満ちた書籍や記事を発表してきましたが、アンチパターンの紹介に関しては極めて消極的です。この記事の目的は、アンチパターンがデザイン・パターンにとって必要なものであり、デザイン・パターンを補完するものであることを示すことです。
State Line Fallsのコースを考えながら、私は知識のおさらいをします。他人の話から、3つめのコースの右側を下ればうまくいくこと、そして滝の下の浅瀬の底にある岩にぶつからないように、急流を素早く漕ぎ渡らなければならないことを知っています。私はこの知識で自信を深めると、脇で小さく渦巻く安全な場所を離れて本流に入ることにします。
私はこのとき知らず知らずのうちに、デザイン・パターンを使用していたのです。私は、自分の前にすでにうまく漕ぎ終えた同じようなカヤック選手の漕ぎ方を見て方策を立てました。デザイン・パターンは、私の技術レベル以上の急流を漕ぎ渡る自信を与えてくれたのです。私は、これと同じ原理をプログラミングやアーキテクチャーに頻繁に利用しています。成功した方策の結果を繰り返し見ることで、特定の問題の解決法を学ぶことができます。デザイン・パターンがあれば、結果はプラスになります。独自の方法を編み出したり、良き先輩を観察したり、あるいは専門家の方法について読み取ることができます。
プログラミング技術での非常に重要な進歩の多くは、デザイン・パターンから生まれています。Model-View-Controllerパターンによって、ユーザー・インターフェースとモデルとの間の明確な境界に沿ってコードを有効に分割できることがわかりました。Publish-Subscribeデザイン・パターンからは、ブロードキャストを行わずにイベントを管理する方法を学びました。その他のデザイン・パターンは、リモート通信用のプロキシEJBインターフェースやコレクション・クラス、Swingフレームワーク、その他の様々なJavaフレームワークに大きな影響を与えています。
私は、反復可能なプロセスを構築するのが大好きです。反復可能なプロセスに関して、デザイン・パターンは多くのものを提供します。問題をさらに細かい問題に分け、そのいくつかについて反復可能なソリューションを利用できないか、という考え方がデザイン・パターンによって促されます。また、他人に利用してもらうために、どのようにデザイン知識を公式に表現し伝えたらよいか、と考えるようにも促されます。
しかし、デザイン・パターンだけでは十分ではありません。プログラミング問題を、越えていかなければならない未開の地と考えると、デザイン・パターンはせいぜい部分的な地図にすぎないのです。結局のところ、必要性を満たす完全なソリューションが既に存在するなら、自分で構築するよりも、それを購入するでしょう。さらに、サポーティング・ソフトウェアの発達に伴い、地図上の既存の道筋、つまりインフラストラクチャーは急速に変化します。部分的な地図では、危険なナビゲートをする恐れがあります (部分的な地図がすべてそうだというわけではありませんが)。目的地にたどり着くには、思い切って地図を離れることも必要でしょう。では、迷ったときはどうすればよいのでしょうか。
カヤックの向きを進行方向に合わせようとしていると、急流で左の方に押し流されてしまいました。カヤック選手たる者、上級者向けの急流に挑む際は、危険な箇所をあらかじめ熟知しておかねばなりません。そんなわけで、私も事前に予習を行いました。私の前の何人かのカヤック選手が左の方へ押し流されて衝突したのを知っていたので、この問題の解決法を考え、頭の中で繰り返し練習しました。準備が整うと、私は果敢にドロー・ストロークを使って、元の位置に戻りました。この難局を無事に切り抜けるかすかな望みがでてきたのです。
これをアンチパターンとしてみると次のようになります。まず、私は難しい問題に取り組むことになりました。私は、成功した他のソリューションに基づいてプランを立てました。これが私のデザイン・パターンです。私のプランはうまくいきませんでしたが、急流の他の難所を分析することによって、それに対応することができました。つまり、私はアンチパターンを使用したのです。私は心構えができていたので、問題を認識し、やり方を調整して、軌道修正しました。つまり、リファクタリングを行ったのです。上級のカヤッキングやプログラミングでは、自分自身の誤りから学習することは、大切ですが難しいことです。他人の誤りから学ぶ方がずっとたやすいでしょう。私はこの方法によって、通常自分の能力では及びもつかないような困難に取り組むことができました。
アンチパターン - ソフトウェア危篤患者の救出 (Antipatterns: Refactoring Software, Architectures, and Projects in Crisis) (参考文献を参照) の著者は、次のようにアンチパターンを定義しています。
アンチパターンとは、問題解決のために一般的に採用され、必ず否定的な結果に導くソリューションを記述する、文学的表現形式である。
主要な言葉について以下に説明します。
- 文学的表現形式:アンチパターンは、コードではなく問題の記述です。これは重要なことです。なぜなら、私たちは素早く効果的にメッセージを伝えることができ、クライアントも素早く理解することができるからです。
- 一般的に採用され:パターン化されていなければ、アンチパターンもありえません。欠陥をアンチパターンのレベルにまで引き上げるには、不適切な対応の幾つかの異なった例を明らかにする必要があります。できるならば、異なった状況で集めることが望ましいのですが。
- 否定的な結果:計画したことが、目に見える否定的な影響を与えなければなりません。
最も有名なアンチパターンである2000年問題は、この興味深い新たな分野の危険と展望を示しています。何十万人もの開発者が、4桁ではなく2桁で日付をコード化したため、これらの数字が正確に認識されず、何百万ものバグを引き起こす可能性がありました。多くの著名な研究者が、広範囲にわたる被害を予測しましたが、この問題に対する集中的な研究によって、識別とリファクタリングの新たな技術がコードを非常に効果的に修復し、予測された規模の問題はほとんど起こりませんでした。デザイン・パターンと同様に、アンチパターンも繰り返し発生するソリューションです。これらの違いは、アンチパターンがマイナスの結果を持つという点です。アンチパターンを文書化する場合は、少なくとも次の要素を取り込む必要があるでしょう。
- 名前:時としてアンチパターンには、開発者が付けた1つまたは複数の非公式名がすでにあります。そうした名前がない場合は、名前を付ける必要があるでしょう。記述的でシンプルなものにしてください。
- 問題:問題は、アンチパターンという欠陥を有するソリューションと、開発者の目を欠陥のあるソリューションに向ける誘因を記述します。この記述によって他の人は、どのようにして問題を発見するかがわかります。
- リファクタリングされたソリューション:アンチパターンは、落とし穴から抜け出したり、落とし穴を完全に回避するのに役立ちます。リファクタリングされたソリューションは、他の人に問題の解決方法を伝えるガイドです。
他の多くの業界、特に製造業では、通常はデザイン・パターンと組み合わせるなどして、何らかの形でアンチパターンを使用しています。現在の製造会社は、他社で成功したプロセスの改良をやみくもに模倣しています。「ジャスト・イン・タイム」デザイン・パターンを例にとると、「ジャスト・イン・タイム」製造は、在庫を減らし、品質の問題を素早く解決し、品質を向上させることができるプロセスです。プロセスにおける連続したそれぞれのステップは、前のステップからジャスト・イン・タイムで提供される組立部品を使用します。現在、主要な自動車メーカーはすべて、この技術を使用しています。また、製造会社は、組立作業、テスト、およびデータ収集に他のデザイン・パターンを採用しています。優れた製造会社は、これにとどまりません。組織的なプロセスの欠陥を見つける必要性も認識しています。Zero DefectsやQuality Circlesなどのプログラムによって、工場労働者は、組織的なプロセスの問題と、それを防ぐ最良の方法について話し合う時間を定期的に持つことができます。平均的なプログラムでも、雇用者にとっては、維持コストの何倍にも相当する節約が可能です。アンチパターンは、以下のように他の業界でも利用されています。
- 健康業界。研究者が、不適切な食習慣を発見して公表し、優れた医師はその情報を使用して、症状だけでなく、不健康の根本的な原因を変えるよう患者を指導します。
- 警察。警察官は、地域共同体と協力して、問題の多い地域における犯罪の根本的な原因を特定し、それを防止します。このようなプログラムによって、薬物使用や凶悪犯罪を大幅に減らすことができます。
- 出版業界。10年もの間、何もしていませんでしたが、その後、業績上位の出版社は、再び著者と緊密な関係を持つようになっています。出版社とのそのような関係によって著者は、好ましくない習慣を見つけ出して、それを改め、より優れた書籍を著すことができます。最も成功している技術出版社はこうした方法を使用しており、他の出版社もこれに倣い始めています。
アンチパターンが他の業界で成功しているとしたら、プログラマーもそこから得るものがあるでしょうか。私は、プログラマーはそれ以上のものを得ると考えています。プログラマーとして成功するには、一般的な落とし穴を素早く見分けなければなりません。ソフトウェア開発は、信じられないほど様々なツール、言語、メソッド、および方策を使用します。地図の話に戻りましょう。問題は、それぞれの状況において少しずつ異なります。知識を一般化し、それをどんな状況へでも伝えることができなければ成功することはできないでしょう。デザイン・パターンは、非常に限られた範囲の問題を対象にしますが、アンチパターンはより一般的です。実際に、犯しやすい誤りの多くは、以前に発生したものです。アンチパターンによって、開発サイクルのより早い段階でより多くの誤りを認識することができます。ほとんどのプログラマーは、少しの時間をはらって現状の落とし穴を認識することによって、多くの時間と労力を節約することができます。Java言語の発展の過程において、他のプログラミングにも見られた多くの誤りの例がみてとれます。
- EJBエンティティーBeanの初期の実装は、通信コストが高すぎることが一因となって、非常に速度の遅いものでした。しばらくして、人々は、1つの重要な問題が往復(round-tripping)であることに気付き始めました。結局私たちは、ファサードといった改善法をつかって、それらのソリューションをリファクタリングすることを学びました 。他の開発者が、EJBコンポーネントが存在するはるか前にこの地雷を踏んでいました。パソコンの初期のデータベースでは、管理者がストアード・プロシージャーを使用して多くのデータベース通信を1つにまとめるようになるまでは、パフォーマンスとの闘いでした。初期の分散システムの開発者は、同様の問題を発見し、Facadeソリューションを公開しました。
- 一般的なJavaアンチパターンの別の例は、Magic Servletです。この極めて基本的なアンチパターンについては、 Magic Servletアンチパターン のサイドバーで詳しく説明していますが、私の比較的上級レベルの多くのカスタマーに落とし穴をしかけました。その中では、単一のサーブレットが、ビュー、モデル、およびコントローラーのロジックを処理します。そのようなデザインのメインテナンスは、不可能も同然です。というのも、ビューが変更されるたびに、モデルのロジックを変更し、またその反対も行わなければならないからです。Magic ServletもまたJava言語以前にさかのぼります。この問題の最も一般的な例は、実はVisual Basicで発生します。Visual Basicでは、プログラマーは通常、押しボタンなどの単一のコントロールに10Kのスクリプトをアタッチします。
これらの問題は、ほんの一部にすぎません。それぞれの新たな開発によって、Java開発者は、何らかの規則性を伴って過去の誤りを再発見します。それを回避しようと考えて落とし穴を研究すれば、確実に問題を減らすことができます。
アンチパターンが注目に値するものであることがおわかりいただければ幸いです。では、アンチパターンを日常作業にどのように取り入れればよいのでしょうか。図1は、アンチパターンを特定し解決するためのプロセスの大まかなステップを示しています。これらについては、私の著書であるBitter Java (参考文献を参照) でさらに詳しく説明しています。
図1. 基本的なアンチパターン・プロセスのステップ
- 問題の特定。 Javaプログラミングでは、問題は、バグ、パフォーマンス問題、保守の困難なクラス、またはメモリー・フットプリントの増大の可能性があります。
- パターンの確立。 ソフトウェア・エンジニアリングの学生なら、開発サイクルが進むにつれてバグの修正コストが急激に増大することを知っているでしょう。問題のパターンを確立する場合、開発サイクルのより早い段階でより多くの問題を特定し修正できれば、利益は増します。このようなバリュー・チェーンをさかのぼるには、パターンを確立し、できるだけ広くアンチパターンに従うことが重要です。
- コードのリファクタリング。 このステップでは、問題の原因となるコードをリファクタリングします。このステップは、これまでに特定した問題のすべてに適用される単純なバグ修正です。クイック・パッチではなく完全な修正を行ってください。同じ箇所にバグが発生しやすい場合は文書を追加します。ソリューションを他の人に配布できるように、ステップを文書化する必要もあるでしょう。
- ソリューションの普及。 ここではバグを抱えている人が修正方法を知っているか、また落とし穴に出くわした人がその回避方法を知っているかを確認してください。アンチパターンを公開することによって、利益をより広い範囲に分配することができます。
- プロセスの修正。 ここではアンチパターンに対して防護壁を構築します。修正には様々な形があります。テスト・ケースを改善すると、回帰を素早く特定することができます。日常のプロセスまたはコミュニケーション・チャネルを修正すれば、開始前に問題を防ぐことができます。コーディング標準を強化することで、中括弧やコメントの配置に関連するバグなどのコーディング・エラーを取り除くことができます。
これらのステップは、特定のものから一般的なものに移行するというアプローチを使用しています。バグを見つけ、パターンを確立し、バグを修正し、プロセスを修正します。これらのステップを日常業務に取り入れることによって、より優れたプログラマーになれるでしょう。しかし、そこでやめてはいけません。アンチパターンに関する知識を使用して、プロセスを修正し、企業のプログラマーのレベル・アップを図り、バリュー・チェーンを登りつめてください。アンチパターンを公開し、他のプログラマーのレベル・アップも図ってください。デザイン・パターンの好きなプログラマーの方なら、アンチパターンには得るものが多いことをご理解いただけるでしょう。
私の残りのレースは何事もなく終わりました。私はカヤックを漕ぎ、滝を急速に下り、泡の上にふわりと着水しました。私は笑顔でカヤックを漕ぎつづけました。車を停めてある下流の安全な場所にたどり着くまで笑顔を抑えることができませんでした。また、あえて抑えようとも思いませんでした。
-
Bitter Java (Webサイト) は、アンチパターンの研究を促進することを目的にしています。メッセージ・ボードやリンク、ニュースレターがあります。
-
Bitter Java (書籍)は、この記事の公開とほぼ同時期に出版されます。アンチパターンについてより多くのバックグラウンド情報を得られるだけでなく、メモリー管理やキャッシュ、EJBコンポーネント、コーディング規則、パフォーマンス分析などに関する情報も得ることができます。
- 「Rules and Patterns for Session Facades」 (WebSphere、2001年6月) は、往復を扱うためのソリューションであるセッション・ファサードについて、さらに詳しく説明しています。
- IBMのレッドブックDesign and Implement Servlets, EJBs and JSPs for WebSphere は少し古いですが、Magic Servletサイドバーで使用されているModel-View-Controllerアーキテクチャーの修正に関して有用な情報を得ることができます。
- Malcolm Davis氏の「Struts、オープン・ソースMVC実装」 (developerWorks、2001年2月) は、この人気のあるフレームワークを紹介し、それがどのようにWebプロジェクトの変更を管理し、特殊化を進めるかについて説明しています。
- W.J. Brown氏、Malveau氏、W.H. Brown氏、McCormick氏、およびMowbray氏のアンチパターン - ソフトウェア危篤患者の救出 (Antipatterns: Refactoring Software, Architectures, and Projects in Crisis)(John Wiley & Sons、1998年) は、アンチパターンという突破口を開いた書籍です。この本には、アンチパターンを取り込む際に使用できるテンプレートに関して有用な情報があります。
- Eric Allen氏によるdeveloperWorks の人気コラム「Javaコードの診断」 は、アンチパターンの本質について、バグ・パターンの形式で、かつさらに基本的なレベルで説明しています。
- バグ・パターン: Javaプログラムで頻発しがちなバグの分析と修正
- 「宙ぶらりん複合型」バグ・パターン: ヌル・ポインター例外の最もよくある原因を鎮圧する
- 「ヌル・フラグ」バグ・パターン: 例外状況を表すフラグとしてヌル・ポインターを使うことを避ける
- 「二段たどり」バグ・パターン: 再帰的なクラス・キャストという概念上のエラーを最初から克服する
- うそつきビューのバグ・パターン: GUIの最良の友になってうそつきビューを暴き出しましょう
- 破壊工作データのバグ・パターン: 隠れたデータ爆弾が奇妙なクラッシュの原因かもしれません
- 破綻したディスパッチのバグ・パターン: 引き数のアップ・キャストによって、不正確なメソッドの呼び出しを修正する
- Javaコードのパフォーマンスを向上させる: 末尾再帰変換はアプリケーションの速度を向上させる可能性はあるが、すべてのJVMで可能な操作ではない
- 正しいメソッド呼び出しのためのRecorderによるテスト: メソッドの呼び出しを順序正しく行うために、ユニット・テストのためのRecorderを記述する
- 型詐欺師のバグ・パターン: タグを使用したオブジェクトの型の区別は、ラベルの貼り違えにつながる可能性がある
- クリーンアップ・コード散在バグ・パターン: リソースの獲得および解放を同時に実行する
- 虚偽の実装というバグ・パターン: 第1回: 前提とした不変条件がインターフェースの破壊を招くこともある
- 虚偽の実装というバグ・パターン: 第2回: 表明とユニット・テスト - バグを除去するための実行可能なドキュメンテーション -
- 「みなし子スレッド」バグ・パターン: マスター・スレッドが自滅し、その他のスレッドが生き残っていると、どうなるか?
- 「テスト可能な」アプリケーションの設計: 以下に示す7つの原則は、テストを念頭においてコード設計を行う際のもとになるものです
- 拡張可能アプリケーションの設計 第1回: ブラック・ボックス、オープン・ボックス、またはガラス・ボックス: どんな場合にどれがふさわしいか?
- 拡張可能アプリケーションの設計 第2回: ガラス・ボックスはどんな時、どこで、どのように最もよく機能するかを調べる
- 拡張可能アプリケーションの設計 第3回: ブラック・ボックスはどんなとき、どこで、どのように最もよく機能するかを調べる
- 拡張可能アプリケーションの設計 第4回: S式がどのように軽量のブラック・ボックス拡張性を実現するか
- 深さ優先visitorと、破綻したディスパッチ: このVisitorパターンの変形を使えば、コードをより簡潔にできます
- 仕様という綱渡り: 明確に定義された仕様が、ソフトウェア・システムにとってなぜ重要か
-
e-businessサイトのIBM Patterns で実行中のデザイン・パターンをご覧ください。
-
developerWorks JavaゾーンでJavaに関連する他の様々な記事をご覧ください。

Bruce Tate氏は、独立コンサルタント兼執筆者です。彼は12年間IBMに勤め、Javaのproof-of-conceptチームなどの様々な仕事に携わりました。Tate氏は、IBMで、Austinのあるスタートアップのためにソリューション開発組織を運営していましたが、その後IBMを退職し、J2Life, LLC という独自の事業を立ち上げました。彼は、Bitter Javaを含め、Javaアンチパターンに関する3冊の書籍を執筆しました。彼の連絡先は、bruce.tate@j2life.comです。