GoFデザインパターン - "Bridge"
皆さんこんにちは
最近、プログラミングにおけるオブジェクト指向の理解を深めるため
GoFのデザインパターンを書籍やネットなどで勉強しているので
理解が難しかったものなどはメモ的に残しておこうと思います
※GoFのデザインパターンとは
「オブジェクト指向における再利用のためのデザインパターン」という書籍の共著者であるエリック・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースの4人がまとめた23種類のデザインパターンです。「GoF」とは上述4名を指す「The Gang Of Four」の略。
今回は、「Bridgeパターン」についてまとめてみます。
主な参考資料として「Java言語で学ぶデザインパターン入門」を使っています
Bridgeパターンとは?
2種類のクラス階層、「機能のクラス階層」と「実装のクラス階層」を分離しそれを橋渡しするデザインパターンです。
機能のクラス階層・実装のクラス階層とは?
・機能クラスの階層
機能を追加する目的で、あるクラスからサブクラスを作った場合の階層を「機能のクラス階層」という。つまり機能追加を目的とした継承です。
・実装のクラス階層
親クラスの抽象メソッドを具体的に実装する目的でサブクラスを作った場合の階層を「実装のクラス階層」という。つまり抽象クラスやインターフェースクラスを実装する場合です。
クラス階層を分離しないと?
・AbstractClassの抽象メソッドshowを実装する2つのクラスを作成し具体的な処理をそれぞれ実装。(実装のクラス階層)
・AbstractClassを拡張したAbstractCountClass作成した場合、そのクラス用にさらに実装クラスを作らなければいけません。
この場合、機能の拡張に応じて実装クラスの数がかなり増えていきます。
このような場合に「Bridgeパターン」で構成していきます。
Bridgeパターンのサンプル
サンプルプログラムクラス図
・Displayクラス(機能のクラス階層)
機能のクラス階層の最上位にあるクラス。implフィールドにはコンストラクタで渡された実装クラスのインスタンスを保持します。このフィールドが「bridge(橋渡し)」の部分を担います。
public class Display {
private DisplayImpl impl;
public Display(DisplayImpl impl) {
this.impl = impl;
}
public void open() {
impl.rawOpen();
}
public void print() {
impl.rawPrint();
}
public void close() {
impl.rawClose();
}
public final void display() {
open();
print();
close();
}
}
・CountDisplayクラス(機能のクラス階層)
DisuplayクラスにmultiDisplayというメソッドを追加したサブクラスです。
public class CountDisplay extends Display {
public CountDisplay(DisplayImpl impl) {
super(impl);
}
public void multiDisplay(int times) { // times回繰り返して表示する
open();
for (int i = 0; i < times; i++) {
print();
}
close();
}
}
・DisplayImplクラス(実装のクラス階層)
実装のクラス階層の最上位にあるクラス。
抽象クラスとなっていて、Displayクラスのメソッドに対応した3種類のメソッドを持っています。
public abstract class DisplayImpl {
public abstract void rawOpen();
public abstract void rawPrint();
public abstract void rawClose();
}
・StringDisplayImplクラス(実装のクラス階層)
DisplayImplを継承し具体的な処理を実装したクラス。
StringDisplayImplクラスは文字列を表示するクラスとなっている。
public class StringDisplayImpl extends DisplayImpl {
private String string; // 表示するべき文字列
private int width; // バイト単位で計算した文字列の「幅」
public StringDisplayImpl(String string) { //コンストラクタで渡された文字列stringを、
this.string = string; //フィールドに記憶しておく。
this.width = string.getBytes().length; //それからバイト単位の幅もフィールドに記憶しておいて、後で使う。
}
public void rawOpen() {
printLine();
}
public void rawPrint() {
System.out.println("|" + string + "|"); // 前後に"|"をつけて表示
}
public void rawClose() {
printLine();
}
private void printLine() {
System.out.print("+"); // 枠の角を表現する"+"マークを表示する。
for (int i = 0; i < width; i++) { // width個の"-"を表示して、
System.out.print("-"); // 枠線として用いる。
}
System.out.println("+"); // 枠の角を表現する"+"マークを表示する。
}
}
・Mainクラス(機能のクラス階層)
上記4つのクラスをMainクラスで呼び出すと下記のようになります。
public class Main {
public static void main(String[] args) {
Display d1 = new Display(new StringDisplayImpl("Hello, Japan."));
Display d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));
CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
d1.display();
d2.display();
d3.display();
d3.multiDisplay(5);
}
}
このようにクラス階層を分けることでそれぞれ独立した拡張が可能になり
機能のクラス階層の最上位に実装クラスのインスタンスを保持するフィールドを用意することにより、機能のクラス階層にクラスを追加した場合でも全ての実装のクラス階層のクラスにアクセスすることが可能となりました。
これにより拡張性と独立性が大きく向上します。
今回サンプルソースも含めて参考にさせていただいた書籍「Java言語で学ぶデザインパターン入門」はより細かく説明がされており
他のデザインパターンもわかりやすくまとめられております。
オブジェクト指向を深めるに基本的なデザインパターンを学習することはすごく良いのでおすすめです。
この記事が気に入ったらサポートをしてみませんか?