シングルトン オブジェクトの使用法についてはすでにレビューしましたが、この戦略がデザイン パターンであり、最もよく使用される戦略の 1 つであることをまだ認識していないかもしれません。
実際には、これらのパターンは数多くあり、特定の目的に応じて分類できます。
パターンの分類
パターンの種類 | 応用 |
---|---|
創造的 | オブジェクト作成の問題を解決するタイプ |
構造的 | アーキテクチャ内に正しく拡張可能なクラス階層を構築できるパターン |
行動的 | このパターンのクラスターにより、プログラム内のオブジェクト間の安全かつ便利な対話が促進されます。 |
通常、パターンはそれが解決する問題によって特徴付けられます。Java を使用するときによく遭遇するいくつかのパターンを見てみましょう。
パターン | 目的 |
---|---|
シングルトン | 私たちはこのパターンにすでに慣れています。複数のインスタンスを持つことができないクラスを作成してアクセスするためにこのパターンを使用します。 |
イテレーター | これについては私たちもよく知っています。このパターンにより、コレクション オブジェクトの内部表現を明らかにすることなく、コレクション オブジェクトを反復処理できることがわかりました。コレクションと一緒に使用されます。 |
アダプタ | このパターンは、互換性のないオブジェクトを接続して、連携できるようにします。アダプター パターンの名前を見れば、それが何をするのかを正確に想像できると思います。これは実際の簡単な例です。壁のコンセント用の USB アダプターです。 |
テンプレートメソッド | 統合問題を解決し、アルゴリズムの構造を変更せずにアルゴリズムのステップを変更できるようにする動作プログラミング パターン。 一連の組み立てステップの形式で自動車の組み立てアルゴリズムがあると想像してください。 シャーシ→ボディ→エンジン→キャビン内装 強化されたフレーム、より強力なエンジン、追加の照明を備えたインテリアを導入しても、アルゴリズムを変更する必要はなく、抽象的なシーケンスは変わりません。 |
デコレータ | このパターンは、オブジェクトに便利な機能を与えるラッパーを作成します。この記事の一部として検討します。 |
Java.io では、次のクラスがパターンを実装します。
パターン | java.io で使用される場所 |
---|---|
アダプタ |
|
テンプレートメソッド | |
デコレータ |
デコレータパターン
住宅設計のモデルを説明していると想像してみましょう。
一般に、アプローチは次のようになります。
最初に、いくつかのタイプの家を選択します。最小構成は屋根付きの 1 フロアです。次に、あらゆる種類のデコレータを使用して追加のパラメータを変更します。これは当然、家の価格に影響します。
抽象 House クラスを作成します。
public abstract class House {
String info;
public String getInfo() {
return info;
}
public abstract int getPrice();
}
ここには 2 つの方法があります。
- getInfo() は、家の名前と特徴に関する情報を返します。
- getPrice() は、現在の住宅構成の価格を返します。
標準的なハウス実装 (レンガと木製) もあります。
public class BrickHouse extends House {
public BrickHouse() {
info = "Brick House";
}
@Override
public int getPrice() {
return 20_000;
}
}
public class WoodenHouse extends House {
public WoodenHouse() {
info = "Wooden House";
}
@Override
public int getPrice() {
return 25_000;
}
}
どちらのクラスもHouseクラスを継承し、その Price メソッドをオーバーライドして、標準住宅のカスタム価格を設定します。コンストラクターで名前を設定します。
次に、デコレータ クラスを作成する必要があります。これらのクラスもHouseクラスを継承します。これを行うには、抽象デコレータ クラスを作成します。
ここに、オブジェクトを変更するための追加ロジックを配置します。最初は追加のロジックはなく、抽象クラスは空です。
abstract class HouseDecorator extends House {
}
次に、デコレータの実装を作成します。家に追加の機能を追加できるいくつかのクラスを作成します。
public class SecondFloor extends HouseDecorator {
House house;
public SecondFloor(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 20_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + second floor";
}
}
家に2階を増やせるデコレーター |
Decorator コンストラクターは、「装飾」する、つまり変更を追加する家を受け入れます。そして、 getPrice()メソッドとgetInfo()メソッドをオーバーライドして、古い家に基づいて新しく更新された家に関する情報を返します。
public class Garage extends HouseDecorator {
House house;
public Garage(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 5_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + garage";
}
}
家にガレージを追加するデコレーター |
これで、デコレーターを使って家を更新できるようになりました。これを行うには、家を作成する必要があります。
House brickHouse = new BrickHouse();
次に、設定します。家新しいデコレータに等しい変数を我が家に渡します。
brickHouse = new SecondFloor(brickHouse);
私たちの家変数は2階のある家になりました。
デコレーターを含むユースケースを見てみましょう。
コード例 | 出力 |
---|---|
|
ブリックハウス 20000 |
|
レンガハウス+2階 40000 |
|
レンガ造りの家+2階+ガレージ 45000 |
|
木造住宅+ガレージ+2階 50000 |
|
木造住宅 25000 木造住宅+ガレージ 30000 |
この例は、デコレータを使用してオブジェクトをアップグレードする利点を示しています。したがって、変更しませんでした木造家オブジェクト自体は削除されますが、代わりに古いオブジェクトに基づいて新しいオブジェクトが作成されます。ここで、利点には欠点があることがわかります。毎回メモリ内に新しいオブジェクトが作成され、メモリ消費量が増加します。
このプログラムの UML 図を見てください。

デコレーターの実装は非常にシンプルで、オブジェクトを動的に変更してアップグレードします。デコレータは、現在のクラスと同じ抽象型またはインターフェイスのオブジェクトをパラメータとして受け取るコンストラクタによって認識できます。Java では、このパターンは I/O クラスで広く使用されています。
たとえば、すでに述べたように、java.io.InputStream、OutputStream、Reader、およびWriterのすべてのサブクラスには、同じクラスのオブジェクトを受け入れるコンストラクターがあります。
GO TO FULL VERSION