Javaにおけるロールオブジェクトパターン:オブジェクトの役割と振る舞いの柔軟性を高める
ロールオブジェクトデザインパターンの目的
Javaオブジェクトに動的に役割を効率的に割り当て、実行時の柔軟性を最適化しながら、オンザフライで振る舞い、責任を適応させる。
ロールオブジェクトパターンの詳細な説明と現実世界の例
現実世界の例
スタッフが状況に応じて様々な役割を担うレストランを想像してみてください。例えば、従業員は状況に応じて、サーバー、キャッシャー、キッチンヘルパーなどの役割を担うことができます。レストランが忙しいときは、サーバーがキャッシャーの役割も担って迅速に支払処理を行う場合があります。その後、同じ従業員が混雑時にキッチンで手伝うこともあります。この柔軟性により、レストランはリアルタイムの需要を満たすために責任を動的に割り当てることができ、効率性と顧客満足度を高めることができます。ソフトウェアにおけるロールオブジェクトパターンは、オブジェクトが実行時に異なる役割と振る舞いを受け入れることを可能にし、同様の柔軟性と適応性を提供することで、これを模倣しています。
簡単に言うと
Javaのロールオブジェクトパターンは、コンテキスト固有のオブジェクトビューを個別の動的に管理されるロールオブジェクトでモデル化し、モジュール設計と実行時の適応性を高めます。
wiki.c2.com の説明
透過的に接続されたロールオブジェクトを通じて、オブジェクトを異なるクライアントのニーズに適応させます。各ロールオブジェクトは、そのクライアントのコンテキストでオブジェクトが果たす役割を表します。オブジェクトは、その役割セットを動的に管理します。役割を個々のオブジェクトとして表現することで、異なるコンテキストを分離し、システム構成を簡素化します。
Javaにおけるロールオブジェクトパターンのプログラミング例
ロールオブジェクトデザインパターンは、オブジェクトのコンテキスト固有のビューを個別のロールオブジェクトとしてモデル化することを提案するパターンです。これらのロールオブジェクトは、コアオブジェクトに動的にアタッチおよび削除されます。コアオブジェクトとそのロールオブジェクトからなる結果の複合オブジェクト構造は、サブジェクトと呼ばれます。サブジェクトは多くの場合複数の役割を果たし、同じ役割が異なるサブジェクトによって果たされる可能性があります。
提供されたコードでは、`Customer`オブジェクトが`Borrower`と`Investor`などの異なる役割を果たすことができます。これらの役割はそれぞれ`BorrowerRole`クラスと`InvestorRole`クラスで表現され、`CustomerRole`クラスを拡張しています。
こちらが`Customer`クラスです
public abstract class Customer {
public abstract boolean addRole(Role role);
public abstract boolean hasRole(Role role);
public abstract boolean remRole(Role role);
public abstract <T extends Customer> Optional<T> getRole(Role role, Class<T> expectedRole);
public static Customer newCustomer() {
return new CustomerCore();
}
public static Customer newCustomer(Role... role) {
var customer = newCustomer();
Arrays.stream(role).forEach(customer::addRole);
return customer;
}
}
こちらが`BorrowerRole`クラスです
@Getter
@Setter
public class BorrowerRole extends CustomerRole {
private String name;
public String borrow() {
return String.format("Borrower %s wants to get some money.", name);
}
}
このクラスでは、`borrow`メソッドが`Borrower`役割に固有の操作を表しています。
同様に、`InvestorRole`クラスは`Investor`役割を表します
@Getter
@Setter
public class InvestorRole extends CustomerRole {
private String name;
private long amountToInvest;
public String invest() {
return String.format("Investor %s has invested %d dollars", name, amountToInvest);
}
}
`InvestorRole`クラスでは、`invest`メソッドが`Investor`役割に固有の操作を表しています。
`Customer`オブジェクトは、これらの役割のいずれか、または両方を実行できます。これは`main`関数で示されています。
public static void main(String[] args) {
var customer = Customer.newCustomer(BORROWER, INVESTOR);
LOGGER.info("New customer created : {}", customer);
var hasBorrowerRole = customer.hasRole(BORROWER);
LOGGER.info("Customer has a borrower role - {}", hasBorrowerRole);
var hasInvestorRole = customer.hasRole(INVESTOR);
LOGGER.info("Customer has an investor role - {}", hasInvestorRole);
customer.getRole(INVESTOR, InvestorRole.class)
.ifPresent(inv -> {
inv.setAmountToInvest(1000);
inv.setName("Billy");
});
customer.getRole(BORROWER, BorrowerRole.class)
.ifPresent(inv -> inv.setName("Johny"));
customer.getRole(INVESTOR, InvestorRole.class)
.map(InvestorRole::invest)
.ifPresent(LOGGER::info);
customer.getRole(BORROWER, BorrowerRole.class)
.map(BorrowerRole::borrow)
.ifPresent(LOGGER::info);
}
このクラスでは、`Borrower`と`Investor`の両方の役割を持つ`Customer`オブジェクトが作成されます。`hasRole`メソッドを使用して、`Customer`オブジェクトが特定の役割を持っているかどうかを確認します。`getRole`メソッドを使用してロールオブジェクトへの参照を取得し、その後、役割固有の操作を実行します。
実行例の出力がこちらです
10:22:02.561 [main] INFO com.iluwatar.roleobject.ApplicationRoleObject -- New customer created : Customer{roles=[INVESTOR, BORROWER]}
10:22:02.564 [main] INFO com.iluwatar.roleobject.ApplicationRoleObject -- Customer has a borrower role - true
10:22:02.564 [main] INFO com.iluwatar.roleobject.ApplicationRoleObject -- Customer has an investor role - true
10:22:02.574 [main] INFO com.iluwatar.roleobject.ApplicationRoleObject -- Investor Billy has invested 1000 dollars
10:22:02.575 [main] INFO com.iluwatar.roleobject.ApplicationRoleObject -- Borrower Johny wants to get some money.
Javaでロールオブジェクトパターンを使用する場面
- オブジェクトがその役割に基づいて動的に振る舞いを変更する必要がある場合。
- 複数のオブジェクトが共通の振る舞いを持つが、その役割に基づいて異なる振る舞いをする必要がある場合。
- 実行時に役割を追加、削除、または変更できるシナリオ。
Javaにおけるロールオブジェクトパターンの現実世界の応用
- ユーザーが異なる権限と責任を持つことができるアプリケーションにおけるユーザーロール管理。
- キャラクターが動的に異なる役割(例:ヒーラー、戦士、魔法使い)を担うことができるゲームキャラクターの役割。
- コンテキストに応じてタスクに異なる役割を割り当てることができるワークフローシステム。
ロールオブジェクトパターンのメリットとデメリット
メリット
- Javaオブジェクトが動的に役割を切り替えることを可能にし、進化するアプリケーションのニーズに対応することで、ソフトウェアの柔軟性を高めます。
- 役割固有の振る舞いをコアオブジェクトのロジックから切り離すことで、コードの保守性を高めます。
- 既存のコードを変更せずに新しい役割を追加することができます。
デメリット
- 複数のロールオブジェクトを管理する必要があるため、複雑さが増します。
- 役割の割り当てと振る舞いの切り替えの動的な性質による潜在的なパフォーマンスオーバーヘッド。
関連するJavaデザインパターン
- 戦略パターン:オブジェクトの振る舞いを動的に変更するという点で類似していますが、ロールオブジェクトパターンは組み合わせることができる役割に焦点を当てています。
- デコレータパターン:どちらもオブジェクトに振る舞いを追加できますが、ロールオブジェクトパターンは静的な拡張ではなく、動的な役割の切り替えを可能にします。
- 状態パターン:役割の変更と同様に状態遷移を管理しますが、ロールオブジェクトパターンは状態よりも振る舞いの役割に重点を置いています。