どうもこんにちは。orioです。
引き続き読書会を開催しています。
題材としては「デザインパターンとともに学ぶオブジェクト指向のこころ」です。
事前準備(一応途中参加も受け付けております。外部からの参加も全然OKです)
- appear.inにて参加(参加希望の方はtamao0083@growth-and.comまでご連絡ください)
- 音声通話の準備(マイクとヘッドホンの準備。ヘッドセットがあれば楽です)
- 課題図書の購入 (オブジェクト指向のこころ)
- 範囲の理解
大まかな流れ
- 全員が事前に範囲を読んでおく(この時、わからない点や思ったことをメモっておくと良い)
- 説明役を一人決め、その説明役が当日説明をする
- 次の日時、範囲、説明役(及び説明代理)を決める
- ブログに参加者、範囲、レビュー等の情報を掲載
今回の読書会範囲
- 第10章 Bridgeパターン
読書会参加者
- 古家(グロースアンドコミュニケーションズ株式会社)
- orio(グロースアンドコミュニケーションズ株式会社)
- 井上(ライジングサン企画株式会社)※グループ会社のエンジニアの方です
今回は説明者は井上さんがやりました。
第10章 Bridgeパターン
Bridgeパターンとは?
Bridgeパターンとは、2つのクラス階層、「機能のクラス階層」と「実装のクラス階層」を分離した上で、橋を渡すように結びつけるパターンです。
ある流動的要素「本書では、形状と描画プログラム」を見つけ出して、抽象クラスにカプセル化し、クラス継承よりもオブジェクトの集約をつかうことで、継承関係に依存しすぎた、結合度が高く、凝集度が低い、冗長な設計から解放され、変更がしやすく、保守性に優れた開発をすることができます。
私自身、継承関係に依存しすぎたソースコードや設計を今まで現場で見てきたことがあります。基底クラスの機能追加でも、そのサブクラスにも修正が及ぶ為、その分修正コストが高くなります。
そこで、Bridgeパターンを使うことにより、どのようなメリットがあるのでしょうか?本章では、Brigeパターンを使う理由や、メリットについて取り上げています。
従来のオブジェクト思考の考え方
この章ではBridgeパターンというものがどういうものなのか?というのを従来のオブジェクト指向のクラス継承の考え方と比較して考察します。従来の考え方というのは、オブジェクト指向プログラミングを学習し始めた人たちがBridgeパターンを知らないで実装した場合の継承を多用した設計方法のことです。
本章の例だと、線を描画するプログラミングの設計で、基底クラスとしてShape(形状)クラスをつくり、派生クラスにRectangle(四角形)、Circle(円)を作成し、Shapeクラスには+Draw()メソッドがあり、Rectangle(四角形)、Circle(円)がShapeクラスの+Draw()メソッドを継承しています。RectangleにはDP1(描画プログラム1)用のV1RectangleとDP2(描画プログラム2)用のV2Rectangleを派生させ、Circleには、DP1用のV1CircleとDP2用のV2Circleを派生させます。
※クラス作成からのメソッドの作成は説明から除外致します。
ここまで、クラスの継承関係を書きましたが、このように継承関係の設計のアプローチを採用してしまうと、DP3(描画プログラム3)を追加した場合、即ち流動的要素を追加した場合、2種類の形状(RectangleとCircle)×3種類の描画プログラム(DP1、DP2、DP3)の6種類の形状を作成しなければならなく、クラスの爆発問題がおきると筆者は述べてます。
クラスの爆発問題が発生するのは、抽象的側面(Shapeの種類と実装(描画プログラム)が密結合になっている(結合度が高い)為です。
この問題を解決するには、流動的要素から抽象的側面における流動的要素を切り出すことにより、要求の増加とクラス数の増加を線形の関係にする必要があります。
これが、「実装から抽象的側面を切り出して、それらを独立して変更、追加できるようにする」というBridgeパターンの目的なのです。
では、Bridgeパターンをつかって流動的要素「Shape(形状)」と「描画(Drawing)」を切り出して考えてみまししょう。
Bridgeパターンを使用したアプローチ
本章の例だと、線を描画するプログラミングの設計で、流動的要素「Shape(形状)」の抽象クラスと「描画(Drawing)」の抽象クラスをつくります。
次にShapeの派生クラスにCricleとRectangleをつくり、Drawingの派生クラスにV1DrawingとV2Drawingをつくります。
2つのクラス群ができあがったら、集約の関連性を考えて、ShapeがDrawingをつかう考え方と、DrawingがShapeをつかう考え方をみていきましょう。
まず、後者のケースを考えてみます。
DrawingがShapeを使用する場合、すなわちDrawingは形状に関する知識、即ちそれがどういったもので、どのように表示されなければならないという情報を知っていなければなりません。
これは、「オブジェクトは常に自らの責務のみ注力するべき」という基本原則に則っていないことになります。また、カプセル化の原則にも違反することとなります。DrawingはShapeを描画するときにどのShape型かという情報を保持していなくてはいけなく、Shapeを抽象クラスにした意味がなくなってしまいます。
では、前者を見ていきましょう。
Shape(形状)を自ら描画する為にDrawingを使用するという考え方はどうでしょうか。
ShapeにDrawingへの参照を保持しておけば、Shapeは自ら使用しているDrawingの型を知る必要がなくなり、描画を制御するという責務もShape内に保持できます。
この設計に従うと、Shapeは自らのふるまいを実現する為にDrawingを使用することになります。
DP1(描画プログラム1)を呼び出すのは、V1Drawingに、DP2(描画プログラム2)を呼び出すのは、V2Drawingに任せることになります。
この時、Drawingの+drawLine()、+drawCircle()を呼び出すことができるよう、Shapeクラス内に#drawLine()、#drawCircle()のprotectedメソッドを常に追加することで、ShapeがDrawingを参照できるようになります。
まとめ
・Bridgeパターンとは、2つのクラス階層、「機能のクラス階層」と「実装のクラス階層」を分離した上で、橋を渡すように結びつけるパターン
・クラスの爆発問題が発生するのは、抽象的側面(Shapeの種類と実装(描画プログラム)が密結合になっている(結合度が高い)為
・Bridgeパターンの目的は「実装から抽象的側面を切り出して、それらを独立して変更、追加できるようにする」
次回について
次回は7月17日になります。対象範囲は11章のAbstract Factoryパターンをやります。
説明者はorioでやる予定です。