言語を選択
durumis AIが要約した文章
- ソフトウェア開発では、実践は理論よりも重要であり、簡単なプログラムを通してオブジェクト指向設計の重要性を理解することができます。
- チケット販売アプリケーションを例に、オブジェクト間の依存関係と結合度を下げて変更に柔軟なコードを書く方法を説明し、手続き型とオブジェクト指向 プログラミングの違いを比較します。
- オブジェクト指向設計は、変更可能なコードを作成し、オブジェクトはデータを自ら責任を持つ自律的な存在として、協力するオブジェクト間の依存関係を適切に 管理する必要があります。
導入部
ロバート・L・グラスは、理論よりも実践が先だと主張しました。特にソフトウェア開発では、実践が理論よりも先行している代表的な分野であり、 開発者は具体的なコードに触れて手を汚すときに最も多くのことを学びます。そのため、理論や概念はしばらく置いておいて、簡単な プログラムを一つ見ていきたいと思います。
チケット販売アプリケーションの実装
- 小劇場の宣伝のために、小さなイベントを企画しようと考えています。
- イベント内容:抽選で選ばれた観客に、公演を無料で観覧できる招待状を送付
- イベントに当選した観客と当選しなかった観客を区別する必要があります。
- イベント当選者:招待状をチケットと交換
- イベント落選者:チケットを現金で購入
何が問題なのか
ロバート・マーティンは、ソフトウェアモジュール(サイズにかかわらず、クラス、パッケージ、ライブラリなどのプログラムを構成する任意の要素)が持たなければならない 3つの機能について説明しています。
- 実行中に正常に動作する。
- 変更のために存在する。
- 簡単な作業だけで変更が可能である必要がある。
- コードを読む人とコミュニケーションをとる。
- 開発者が簡単に読み、理解できる必要がある。
先のチケット販売アプリケーションは、正常に実行するという最初の制約は満たしていますが、変更の容易さやコミュニケーションという目的は満たしていません。
予想外のコード
コミュニケーションという目的を満たしていない理由を見てみましょう。
- Theaterクラスのenter()メソッドが実行すること
- 小劇場は、観客のバッグを開けて、中に招待状が入っているかどうかを確認します。
- バッグの中に招待状が入っていれば、販売員に窓口に保管されているチケットを観客のバッグに移すように指示します。
- バッグの中に招待状が入っていなければ、観客のバッグからチケットの金額分の現金を取り出してチケットを購入し、 チケットをバッグに入れます。
- 観客の立場では、小劇場という第三者が勝手にバッグを漁ったり、お金を取ったり、チケットを入れたりしているのを目の当たりにしなければなりません。
- 販売員の立場では、小劇場という第三者が許可なく窓口のチケットや現金の値を操作しているのを目の当たりにしなければなりません。
- 理解可能なコードとは、動作が私たちの予想を大きく外れないコードを意味しますが、上の小劇場は私たちの予想を裏切ります。
- 観客は自らバッグからお金を取り出して販売員に支払い、チケットを受け取る必要があります。
- 販売員は自ら窓口にあるチケットを直接取り出して観客に渡し、観客からお金を受け取って窓口に 保管する必要があります。
- また、enter()メソッドを理解するためには、さまざまな詳細をすべて記憶しておく必要があります。
- AudienceがBagを持っています。
- Bagの中には現金とチケットが入っています。
- TiketSelletがTicketOfficeからチケットを販売しており、TicketOfficeの中にはお金とチケットが 保管されています。
変更に脆弱なコード
enter()メソッドは、2つの条件を前提としています。
- 観客は、現金と招待状を保管するために、常にバッグを持っています。
- 販売員は、窓口でのみチケットを販売します。
では、下記のような状況はどうでしょうか?
- 観客はバッグを持っていない場合があります。
- 観客は現金ではなくクレジットカードを使用する場合があります。
- 販売員が窓口以外でチケットを販売する場合があります。
例えば、最初の要求を満たすためには、AudienceのBagオブジェクトを削除し、それに関連するTheaterクラスの enter()メソッドを変更する必要があります。これは、Theaterクラスが観客はバッグを持っており、販売員は窓口でのみチケットを 販売するという、あまりにも詳細な事実に依存しているためです。このような詳細な事実に一つでも変更があれば、当該クラスとその依存する クラス(例:Theater)をすべて修正する必要があります。
これは、オブジェクト間の依存関係に関連する問題であり、依存関係は変更への影響を暗示しています。しかし、オブジェクト指向設計は、相互に依存し合いながら協力するオブジェクトのコミュニティを構築することを目的としているため、無闇に依存関係をなくすのではなく、アプリケーションの機能を実現するために必要な最小限の依存関係のみを維持し、不要な依存関係は削除する必要があります。
オブジェクト間の依存関係が過剰な場合を結合度が高いと言いますが、2つのオブジェクト間の結合度が高いほど、一緒に変更される可能性も高くなります。そのため、設計の目標は、オブジェクト間の 結合度を下げて、変更しやすい設計にすることです。
設計を改善する
観客がバッグを持っているという事実や、販売員が窓口でチケットを販売しているという事実を、Theaterが知る必要はありません。ただ Theaterは、観客が小劇場に入場することを望んでいます。そのため、観客が自身でバッグの中の現金と招待状を処理し、販売員が 自身で窓口のチケットと販売料金を扱うように、自律的な存在にする必要があります。
自律性を高めよう
密接に関連する作業のみを行い、関連性のない作業は他のオブジェクトに委譲するオブジェクトを、凝集度が高いといいます。自分の データを自身で処理する自律的なオブジェクトを作成すれば、結合度を下げて凝集度を高めることができます。
手続き型とオブジェクト指向
- 手続き型
- Theaterのenter()メソッドはプロセスであり、Audience、TicketSeller、Bag、 TicketOfficeはデータです。
- プロセスとデータを別々のモジュールに配置する方式を、手続き型プログラミングと呼びます。
- 私たちの直観に反するコードが多いです。(例:観客は自身でお金と招待状を管理します。)
- データの変更による影響を狭めることが難しいです。
- 責任を集中管理します。(Theaterがすべて管理)
- オブジェクト指向
- データとプロセスが同じモジュール内に配置される方式を、オブジェクト指向プログラミングと呼びます。
- 私たちの直観に合ったコードを書くことができます。
- データの変更による影響を、カプセル化によって効果的に狭めることができます。
- 各オブジェクトが自身を自ら責任を持ちます。
さらに改善できる
- AudienceクラスのBagクラスは、依然としてAudienceクラスに引っ張られる受動的な存在なので、Bag クラスを自律的なオブジェクトにする必要があります。
- TicketSellerクラスのTicketOfficeも、TicketSellerに勝手に管理されています。 TicketOfficeを自律的なオブジェクトにする必要があります。
- しかし、変更後、TicketOfficeはAudienceと追加の結合度が発生しました。
- このように、設計ではトレードオフを良く検討する必要があります。この場合、Audienceとの結合度を下げるために TicketOfficeをある程度受動的なオブジェクトにすることに合意することもできます。
そうだ、嘘だ!
- 現実では受動的な存在であっても、オブジェクト指向の世界に入れば、すべてが能動的で自律的な存在に変身します。
- 擬人化を用いて、受動的なオブジェクトをまるで笑い、話し、怒る対象として考えるのが良いでしょう。
オブジェクト指向設計
なぜ設計が必要なのか
- 設計とは、コードを配置することです。
- 良い設計とは、今日の要求する機能を完全に実行しながら、明日の変更をスムーズに受け入れられることです。
オブジェクト指向設計
- 変更可能なコードとは、理解しやすいコードです。
- オブジェクト指向パラダイムは、私たちが世界を見るように、コードを書くことができるように助けます。
- オブジェクトは、自身のデータを自身で責任を持つ自律的な存在です。
- 優れたオブジェクト指向設計とは、協力するオブジェクト間の依存関係を適切に管理する設計です。
出典
- オブジェクト