元茹でガエルなエンジニアの記録

ちょっとやってみたことや考えたことなどを不定期に書き残していきます

【翻訳】15年後のデザインパターン

オブジェクト指向におけるデザインパターンを世に送り出したGoFの3人(1名はすでに他界)が出版から15年後に対談した内容の翻訳です。 https://www.informit.com/articles/article.aspx?p=1404056

【翻訳】15年後のデザインパターン

ラリー・オブライエン:この1年あまりの間に、8万5千ものiPhone用アプリが開発され、展開されています。グローバルにアクセスできる "Hello, World!こんにちは、世界! 時刻はXです "というウェブページをPHPで1行で書くことができる。「オブジェクト指向ソフトウェアの設計は難しい」というのが、デザインパターンの第一声である。この言葉は今でも正しいのだろうか?

リチャード・ヘルム:ソフトウェア設計は常に難しいものです。最近の開発環境の多くは、再利用可能なライブラリやツールキットによって複雑さを軽減していますが(EclipseAppleMicrosoft)、ビジネス上の問題に対するソフトウェアソリューションを設計することは、依然として難しいのです。

エリック・ガンマ:そうですね、iPhoneの話をされるのは面白いですね。iPhone SDKは、AppKitのようなNeXTStepオブジェクト指向フレームワークがベースになっています。15年前に私たちが「Design Patterns」(以下DP)を書いたときにはすでに存在しており、インスピレーションの源のひとつとなりました。実際に私たちのパターンのいくつかでこのフレームワークを参照している。Adapter、Bridge、Proxy、Chain of Responsibilityなどです。

リチャード: これは、優れたデザインの永続的な性質と、それがさまざまな技術的表現に耐えられることを示す素晴らしい例です。

ラルフ・ジョンソン:1行のプログラムを書くというのは、通常、デザインという意味ではありません。ソフトウェアはここ数年で大きく進歩し、以前は入念な設計が必要だったシステムの多くも、今では他のソフトウェアを再利用することで構築できるようになりました。しかし、そのように構築できないシステムもたくさんあり、10万行の新しいコードを書くことは、15年前と比べてそれほど簡単ではありません。より多くのことができるようになりますが、コストは同じです。

ソフトウェアを設計するのは大変です。だからこそ、楽しいのです。ソフトウェアを設計するのが得意な人は、難しい問題を解決すること、混沌に秩序をもたらすこと、困難に打ち勝つことを楽しんでいます。昔は難しかったことが今は簡単になっていますが、20年前には不可能だったような問題に取り組むようになったのです。オブジェクト指向プログラミングは、設計の難しさを解消するものではありません。

ラリー:この本の次のフレーズは、「...再利用可能なオブジェクト指向のソフトウェアを設計することは、さらに難しい」です。再利用は、90年代前半には、オブジェクト指向の主な利点として紹介されていました。しかし、この10年間で、多くの主流の開発者は、再利用をフレームワークの設計者に任せることにしたようだ。今必要なものを開発し、進化させる必要があれば最小限のリファクタリングを行い、質問されれば「You Ain't Gonna Need It」というカードを出す。再利用可能な設計を目指すことは、今日の開発者にとって重要な目標なのでしょうか?

リチャード:洗練されたレベルの進化があったのだと思います。再利用可能なソフトウェアは、ツールキットやフレームワークとして、基盤となるシステムや言語に移行しており、そのほとんどは専門家に任されるべきものです。

多くのソフトウェア開発者にとっての目標は、依然として変化を想定した設計であり、そこでの議論は、それを早い段階で行うのか(予見可能な状態で)、それとも遅い段階で行うのか(より多くのことが判明し、それが必要だと分かってから)、ということです。多くの場合、設計はリファクタリングされるべきであり、パターンはそのための目標を提供する。

エリック:実は今、「...最も難しいのは、再利用可能なオブジェクト指向ソフトウェアを進化させることだ」という言葉を付け加えようと思っています。これについては、「Design Pattern」の中でも少し触れています。例えば、Factory、Adapter、Facadeは、再利用可能なライブラリーを変更したり、進化させたりする際に役立ちます。この問題に対しては、さらにいくつかのパターンがあり、それを捕捉することは価値があるだろう。

耐久性のあるAPIを設計するには、さらに洗練されたレベルが必要であることに同意します。今日下す決断は、明日できることに影響を与えます。しかし、専門家でさえ、最初からうまくいくのは難しいのです。例えば、EclipseAPIに潜入すると、プリファレンスを保存するためのAPIに3つの異なるバージョンがあることがわかります。

ラルフ:ほとんどのプログラマーは、再利用可能なソフトウェアを書くために雇われているわけではありません。ただし、そのソフトウェアが数年後に要件が変わり、世界が変わったときにも使えるようにする必要があるという意味では、再利用可能なソフトウェアを書くために雇われているのです。しかし、再利用可能なソフトウェアがどのように機能するかを知る必要はありますし、私たちのパターンは再利用可能なソフトウェアでは一般的なものなので、平均的なプログラマーが知っていても役に立つでしょう。この本は、どのパターンを使おうかと考えている人に向けるのではなく、他の人が選んだパターンを使っている人に向けた方が、今はいいのかもしれませんね。

エリック:私はこのアイデアが好きです。先ほどのiPhone SDKを学んだとき、使用されているパターンを知っていたので、すぐにライブラリに親しみを感じることに気づきました。

ラリー:一時期、あらゆるものが "パターン" になっていましたね。建築、組織行動、分析などのパターンがありました。しかし、あまり明らかではなかったのは、デザインパターンカタログの23のパターンが、他のデザインパターンによって拡張されたり、例えば、アーキテクチャのパターンに関連したりするような進化を遂げていたことです。世の中にはたくさんのパターンがあります。新しい「図1.1」はないのだろうか。デザインパターンの関係」?

ラルフ:「お渡しするフィギュアはありますか」という意味であれば、答えは「ありません」です。もし「誰かが新しいフィギュアを作るべきか」という意味なら、答えは「Yes」です。

エリック:リンダ・ライジングの『パターン・アルマナック2000』というのがありますが、これは多くのパターンを捉えようとしたものです。これはかなりの労力を要しましたが、コミュニティではそれほど大きな支持を得られませんでした。私は、ソーシャルフィルタリングによって、最も人気のあるパターンを見つけ出し、「このパターンが役に立った人は、このパターンも好きだった」というようなヒントを得ることができるのではないかと思い始めました。つまり、すべてのパターンが同じ関連性と重みを持っているわけではないのです。DPでパターンの良いサンプルを選んだのは、かなり幸運でした。

ラリー:DPで使われている記述テンプレートは、「名前」「意図」「構造」以外にも多くの特徴がありました。パターンの流行に乗った人たちが皆、それらの要素をすべて提供しているわけではありません。もし、今、そのテンプレートをリファクタリングするとしたら、どんなことをしますか?

ラルフ:このテンプレートは、私たちの本に載っているような低レベルのオブジェクト指向パターンには適していました。それ以外の目的には向いていませんでした。人々はすぐにそのことに気づき、別のテンプレートを選びました。すべてのパターンのコレクションには標準的なテンプレートが必要ですが、どのテンプレートもすべてのパターンに適しているとは思えません。だから、私はテンプレートについての議論はつまらないと思います。

エリック:私は、特定のテンプレートに執着しているわけではありません。でも、最終的に1つのテンプレートに合意し、それにこだわったことはよかったと思います。例えば、用途がわかっているか、その結果を記述しているか、などです。

ラリー:パターンブームの後、"アンチパターン" という流れがありました。アンチパターンはパターンと同じように価値があるのでしょうか?

リチャード:おそらくある。失敗を共有し、そこから学ぶ方法を提供します。

ラルフ:私は「コード・スモーク」、「デザイン・スモーク」、「アーキテクチャー・スモーク」などの概念を好むんです。それらは常に間違いとは限りません。時には、一緒に生きていかなければならない問題であることもあるのです。例えば、「ストーブパイプ」システムは、企業がシステムを相互接続する良い方法を持っていなかったこと、技術が急速に変化していたこと、企業のさまざまな部分が協力しなかったことなどが原因で、発展しました。成熟し、ゆっくりと成長している企業では、ストーブパイプシステムは、おそらくSOAと強力なアーキテクチャチームによって排除されるべきアーキテクチャの臭いです。しかし、他社を買収して急成長している企業では、ストーブパイプは新会社を統合するのに時間がかかることが原因です。いくらアーキテクトがリーダーシップを発揮しても、この問題は解消されない。

私の生徒の中には、"Big Ball of Mud"(泥だんご) のパターンを書いた者もいる。パターンを読むほとんどの人にとって、これは何としても避けなければならないアンチパターンだ。しかし、多くのIT組織、特に私が見てきた政府のIT組織では、より良いものを作るための十分な専門知識がないのです。彼らが作ったものは、数年のうちにその構造を失い、他のすべてのものとつながってしまうのです。

エリック:デザインパターンを使う動機付けとして、コードスメルやアンチパターンを使うのは貴重なことです。デザインパターンを使う前と後のコードを見なければ、デザインパターンの価値を理解することは難しい。

ラリー:GoFは、OOPが主流のパラダイムになった比較的早い時期に登場し、良くも悪くも、「パターン」はOOPアプローチと結びついているようです。関数型やダイナミック型の支持者が、自分たちの言語にはパターンが「必要ない」と自慢しているのを耳にすることさえあります。これに対して、あなたはどう答えるだろうか?

エリック:余談ですが、私たちがデザインパターンを書いたのは、JavaC#が登場する前だということも忘れがちです。

ラルフ:そのような言語では、問題を解決する別の方法が用意されているので、デザインパターンのいくつかのパターンを必要としないものもあるんだ。私たちのパターンは、C++からSmalltalkまでの言語、つまり「オブジェクト指向」と呼ばれるあらゆるものを対象としていますが、すべてのプログラミング言語のためのものではないのは確かです。他の言語のプログラマーはパターンを必要としない、とは誰も言っていないと思います。

エリック:デザインパターンはどんな言語でもいずれは出てくるものです。デザインのデジャヴは言語に依存しません。こうした経験は必ずしもパターンとして捕捉されるわけではありませんが、実際に存在するのです。Erlangのデザイン原則が思い浮かびます。

ラリー:動的言語関数型言語のパターンについて学ぶには、どこに行けばよいのでしょうか?誰が良い貢献をしているのでしょうか。

ラルフ:「動的」というのがSmalltalkRubyPythonのような動的オブジェクト指向言語のことであれば、私たちのパターンが適用可能です。関数型言語には別のパターンが必要ですが、誰がそれに取り組んでいるのかは知りません。

ラリー:OOPは静的な構造と動的な構造の組み合わせを提供し、設計意図をいくつかの異なる軸で(少なくとも異なる図で)伝えることを可能にします。現在は、関数型やメタプログラミングのアプローチが重視されているため、静的な構造はそれほど多くありません。その代わりに、"ドメイン固有の言語 "や "流動的なインターフェース "の文脈でAPIを作成することによって、設計意図を伝えることが多く語られるようになっている。これらのアプローチは、DPのパターンに取って代わるものなのでしょうか、それとも補完するものなのでしょうか?

リチャード:補足します。優れたデザインで構成されたリッチなクラスライブラリは、ドメイン特化型言語の機能・特性を引き継ぎます。よく設計されたユーザーインターフェースツールキットのコードを読むことは、GUIのためのドメインスペシフィック言語を読むようなものです。ライブラリのコンストラクトは、ドメイン固有言語の動詞と名詞になるのです。例えば、Interviews/Unidraw(John Vlissidesとの共著)のコードを遡って見てみると、言語はC++ですが、GUIの作り方を知っている人なら容易に理解できるコードになっています。

エリック:DPのパターンを補完する一方で、メタプログラミングが設計に使われるデザインパターンに取って代わることも確かにあり得ます。JUnit 3からJUnit 4への進化が思い浮かびます。JUnit 3はComposite、Template Method、Commandといったいくつかのパターンを使用した小さなフレームワークでした。JUnit 4 は、J2SE 5.0 で導入されたメタプログラミング機能である Annotations を活用しています。パターンの使用はなくなり、フレームワークは、小さなアノテーションのセットと、アノテーションされたJavaコードを実行するテストランナーインフラストラクチャに進化した。

ラリー:ジェネレーティブパターンです。A Pattern Languageには253のパターンがあり、DPには23のパターンがあります。DPのカタログは、Alexanderの「(パターン・ランゲージは)配置のルールを教えてくれるだけでなく、そのルールを満たす配置を好きなだけ構築する方法を示してくれる」という命令を満たしていると思いますか?

リチャード:いいえ、杓子定規なガイドでも生成的なガイドでもありません。エンジニアやデザイナーが本棚に手を伸ばし、クラッチや2ストロークエンジンの一般的な機械設計を見つけるような工学ハンドブックから、よりインスピレーションを受けました。

ラリー:「Design Pattern」をリファクタリングするとしたら、どのようになりますか?

エリック:この演習は2005年に行いました。以下は私たちのセッションのメモです。私たちは、オブジェクト指向の設計原則とパターンのほとんどが、当時と変わっていないことを発見しました。私たちは、分類を変更し、新しいメンバーを追加し、またいくつかのパターンを削除したいと思いました。議論の大半は、分類の変更と、特にどのパターンを削除するかについてでした。

どのパターンを捨てるか議論したところ、やはりどのパターンも好きだということがわかりました。(本当は、Singletonをやめることに賛成なのですが。その使用は、ほとんど常にデザイン上のくさいニオイがします)。

そこで、その変更点をご紹介します。

  • InterpreterFlyweightは、他のパターンとは別物なので、「Other/Compound」という別のカテゴリに移動させるべきでしょう。Factory MethodはFactoryに一般化する。

  • カテゴリは次のようにする。Core、Creational、Peripheral、Otherです。ここでは、重要なパターンを強調し、使用頻度の低いパターンから分離することを意図している。

  • 新しいメンバーは以下の通り。Null Object, Type Object, Dependency Injection, Extension Object/Interface (プログラム設計のパターン・ランゲージ3, Addison- Wesley, 1997の「Extension Object」参照)である。

  • カテゴリー分けはこちら

    • Core: Composite, Strategy, State, Command, Iterator, Proxy, Template Method, Facade
    • Creational: Factory, Prototype, Builder, Dependency Injection
    • Peripheral: Abstract Factory, Visitor, Decorator, Mediator, Type Object, Null Object, Extension Object
    • Other: Flyweight, Interpreter

上に書いたように、これはあくまで草稿の状態でのメモです。テストケースを作らずにリファクタリングを行うのは、常に危険です...。

この記事を読んで

15年(なんならもっと前)とはソフトウェア開発自体も進化/変化しているので、これらのパターンの中には、自分で実装することが減ったものもあると思います。ですが、関心ごとの切り出し方や分離の界面をどうしようか、というヒントとしては今でも役に立っています。個人的にはSingletonをやめることに賛成、と言っていることが印象的でした。自分は使っているので。。

アジャイル開発をやっていると、イテレーティブに機能を追加していきます。そのとき、変更や拡張がしやすい柔軟な 設計であるかどうかが重要になってきます。変更の必要が生じたときに、その場限りのツギハギのように増築していくのではなく、だったらここでXXXパターンを適用して少しリファクタリングしてから機能追加しよう、とできると良いように思います。