Javaにおける拡張オブジェクトパターン:オブジェクト機能の柔軟な拡張
別名
- インターフェース拡張
拡張オブジェクトデザインパターンの目的
拡張オブジェクトパターンは、構造を変更することなく、動的に新しい機能を追加できる追加オブジェクトをアタッチすることにより、オブジェクトの動作を柔軟に拡張できるようにします。
現実世界の例を用いた拡張オブジェクトパターンの詳細な説明
現実世界の例
拡張オブジェクトデザインパターンの現実世界の例として、モジュール式キッチン家電が挙げられます。フードプロセッサー、ジューサー、グラインダーなど、さまざまなアタッチメントを追加できるベースブレンダーユニットを考えてみましょう。各アタッチメントは、ベースユニット自体を変更することなく、ブレンダーに新しい機能を追加します。ユーザーは、現在のニーズに基づいてさまざまな機能を動的に切り替えることができ、ブレンダーは非常に汎用性が高く、さまざまなタスクに適応できます。これは、ソフトウェアにおける拡張オブジェクトパターンを反映しており、新しい機能がオブジェクトに動的かつコンテキストに応じて追加され、柔軟性と再利用性が向上します。
分かりやすく言うと
拡張オブジェクトパターンは、コアクラスを変更することなく、オブジェクトに動的に機能を追加するために使用されます。これは、プログラム内の既存のクラスとオブジェクトに新しい機能を追加するために使用される動作デザインパターンです。このパターンにより、プログラマーは既存のソースコードをリファクタリングすることなく、クラスの機能を拡張/変更できます。
Wikipediaによると
オブジェクト指向コンピュータプログラミングでは、拡張オブジェクトパターンは、元のオブジェクトがコンパイルされた後にオブジェクトに追加されるデザインパターンです。変更されたオブジェクトは、多くの場合、クラス、プロトタイプ、または型です。拡張オブジェクトパターンは、一部のオブジェクト指向プログラミング言語の機能です。拡張メソッドの呼び出しと型定義で宣言されたメソッドの呼び出しとの間に構文上の違いはありません。
Javaにおける拡張オブジェクトパターンのプログラム例
拡張オブジェクトパターンは、構造を変更することなく、動的に新しい機能を追加できる追加オブジェクトをアタッチすることにより、オブジェクトの動作を柔軟に拡張できるようにします。
このJava実装では、 `SoldierUnit`、 `SergeantUnit`、 `CommanderUnit` の3種類のユニットがあります。各ユニットには、追加機能を提供する拡張機能を含めることができます。拡張機能は、 `SoldierExtension`、 `SergeantExtension`、 `CommanderExtension` です。
`Unit` クラスは、すべてのユニットの基本クラスです。拡張名に基づいて拡張オブジェクトを返す `getUnitExtension` メソッドがあります。
public abstract class Unit {
private String name;
protected Unit(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract UnitExtension getUnitExtension(String extensionName);
}
`UnitExtension` インターフェースは、すべての拡張の基本インターフェースです。具体的な拡張はそれぞれこのインターフェースを実装します。
public interface UnitExtension {
String getName();
}
`SoldierUnit` クラスは、特定の種類のユニットです。 `SoldierExtension` オブジェクトを返すように `getUnitExtension` メソッドをオーバーライドします。
public class SoldierUnit extends Unit {
public SoldierUnit(String name) {
super(name);
}
@Override
public UnitExtension getUnitExtension(String extensionName) {
if ("SoldierExtension".equals(extensionName)) {
return new SoldierExtension(this);
}
return null;
}
}
`SoldierExtension` クラスは、特定の種類の拡張です。 `UnitExtension` インターフェースを実装し、 `SoldierUnit` に追加機能を提供します。
public class SoldierExtension implements UnitExtension {
private SoldierUnit unit;
public SoldierExtension(SoldierUnit unit) {
this.unit = unit;
}
@Override
public String getName() {
return "SoldierExtension";
}
public void soldierReady() {
// additional functionality for SoldierUnit
}
}
メインアプリケーションでは、さまざまな種類のユニットを作成し、各ユニットに拡張機能があるかどうかを確認します。拡張機能が存在する場合、拡張オブジェクトで特定のメソッドを呼び出します。
public class App {
public static void main(String[] args) {
var soldierUnit = new SoldierUnit("SoldierUnit1");
var sergeantUnit = new SergeantUnit("SergeantUnit1");
var commanderUnit = new CommanderUnit("CommanderUnit1");
checkExtensionsForUnit(soldierUnit);
checkExtensionsForUnit(sergeantUnit);
checkExtensionsForUnit(commanderUnit);
}
private static void checkExtensionsForUnit(Unit unit) {
var extension = "SoldierExtension";
Optional.ofNullable(unit.getUnitExtension(extension))
.map(e -> (SoldierExtension) e)
.ifPresentOrElse(SoldierExtension::soldierReady, () -> System.out.println(unit.getName() + " without " + extension));
}
}
これにより、次のコンソール出力が生成されます。
22:58:03.779 [main] INFO concreteextensions.Soldier -- [Soldier] SoldierUnit1 is ready!
22:58:03.781 [main] INFO App -- SoldierUnit1 without SergeantExtension
22:58:03.782 [main] INFO App -- SoldierUnit1 without CommanderExtension
22:58:03.782 [main] INFO App -- SergeantUnit1 without SoldierExtension
22:58:03.783 [main] INFO concreteextensions.Sergeant -- [Sergeant] SergeantUnit1 is ready!
22:58:03.783 [main] INFO App -- SergeantUnit1 without CommanderExtension
22:58:03.783 [main] INFO App -- CommanderUnit1 without SoldierExtension
22:58:03.783 [main] INFO App -- CommanderUnit1 without SergeantExtension
22:58:03.783 [main] INFO concreteextensions.Commander -- [Commander] CommanderUnit1 is ready!
この例は、拡張オブジェクトパターンが、構造を変更することなく、オブジェクトの動作を柔軟に拡張できる方法を示しています。
現実世界の例を用いた拡張オブジェクトパターンの詳細な説明

Javaで拡張オブジェクトパターンを使用する場合
このパターンは、サブクラス化の複雑さを回避するために、オブジェクトの機能を実行時に拡張する必要があるシナリオに適用できます。オブジェクトの機能をデプロイメント後に拡張する必要があるシステム、またはインスタンス間で機能が大幅に異なる可能性のあるシステムで特に役立ちます。
Javaにおける拡張オブジェクトパターンの実際のアプリケーション
- 既存のコードを変更せずに、アプリケーションサーバーのサービスを拡張する。
- IntelliJ IDEA や Eclipse などの IDE のプラグインを使用して、ベースアプリケーションに機能を追加する。
- ライセンスレベルに基づいて、エンタープライズソフトウェアの追加機能を有効にする。
- OpenDoc
- オブジェクトのリンクと埋め込み
拡張オブジェクトパターンの利点とトレードオフ
利点
- オブジェクトの機能を動的に拡張できるため、柔軟性が向上します。
- 基本オブジェクトとその拡張の間の疎結合を促進します。
- オブジェクトを拡張のために開いたままにし、変更のために閉じたままにすることで、開閉原則をサポートします。
トレードオフ
- 拡張オブジェクトの管理により、複雑さが増す可能性があります。
- オブジェクトと拡張の間の相互作用が効率的に設計されていない場合、パフォーマンスのオーバーヘッドが発生する可能性があります。
関連するJavaデザインパターン
- デコレータ: 責任を動的に追加するという点では意図が似ていますが、異なる構造を使用します。
- コンポジット: オブジェクトのグループも管理します。これは拡張の一種と見なすことができます。
- ストラテジー: オブジェクトの動作を動的に変更する別の方法を提供します。