行動型パターンは、アルゴリズムとオブジェクト間の責任の分配に関わり、行動パターンはオブジェクトとクラスのパターン、およびそれらの間の通信パターンを記述します。行動パターンは、プログラムの実行時に追跡が難しい複雑な制御フローを描写し、行動クラスパターンと行動オブジェクトパターンに分けられます。
- 行動クラスパターンは、継承メカニズムを使用してクラス間で行動を分配します。
- 行動オブジェクトパターンは、オブジェクトの集約を使用して行動を分配します。一部の行動オブジェクトパターンは、対等なオブジェクトのグループがどのように相互に協力して、どのオブジェクトも単独では完了できないタスクを達成するかを記述します。
責任チェーンパターン(Chain of Responsibility)#
リクエストの送信者と複数のリクエスト処理者を結合しないように、すべてのリクエストの処理者を前のオブジェクトが次のオブジェクトの参照を記憶することで連結します。リクエストが発生したときは、このチェーンに沿ってリクエストを渡し、処理するオブジェクトが見つかるまで続けます。
構造#
- 抽象処理者(Handler):リクエストを処理するインターフェース(例:
handleRequest()
)を定義し、次の処理者の参照を保持します。 - 具体的処理者(Concrete Handler):処理ロジックを実装し、リクエストを処理するか、渡すかを決定します。
- クライアント(Client):処理チェーンを作成し、リクエストをトリガーします。
実装
// 抽象処理者
public abstract class Approver {
protected Approver nextApprover;
public void setNext(Approver next) { this.nextApprover = next; }
public abstract void handleRequest(LeaveRequest request);
}
// 具体的処理者:グループリーダー
public class GroupLeader extends Approver {
@Override
public void handleRequest(LeaveRequest request) {
if (request.getDays() <= 2) {
System.out.println("グループリーダーが休暇を承認しました");
} else if (nextApprover != null) {
nextApprover.handleRequest(request); // リクエストを渡す
}
}
}
// クライアントがチェーンを構築
Approver groupLeader = new GroupLeader();
Approver manager = new Manager();
groupLeader.setNext(manager);
groupLeader.handleRequest(new LeaveRequest("張三", 3)); // マネージャーに処理を転送
イテレータパターン#
イテレータパターンは、行動型デザインパターンの一種で、コレクションオブジェクト内の要素にアクセスするための統一された方法を提供し、コレクション内部の表現を公開しないことを目的としています。簡単に言えば、コレクションを遍歴する責任を別のオブジェクトにカプセル化し、特定の方法でコレクション内の要素にアクセスできるようにします。(理解できない、スミダ)
クラスを配列イテレートとイテレータの違い:イテレータパターンは、抽象的な遍歴ロジックとデータ構造のデカップリングを通じて、より高い柔軟性(異なる遍歴戦略:ページング、並列)と拡張性を提供し、特に内部実装を隠し、多様な遍歴戦略をサポートするシナリオに適しています。ジェネリクス(例:Iterator<T>
)を使用して型安全性を保証し、異なるコレクション(List
、Set
)に統一インターフェースを提供します。たとえば、Java のIterable
インターフェースは、for-each
ループがすべてのコレクションと互換性を持つことを可能にします。一方、配列の直接イテレートは、シンプルで固定されたデータ構造のシナリオでより高いパフォーマンスの利点を持ちます。
CS61B 11. Inheritance IV: Iterators, Object Methods
応用
音楽プレイリストの遍歴:スマートフォンやコンピュータで音楽を再生する際、通常はプレイリストを作成します。プレイリストはコレクションと見なすことができ、各曲はコレクション内の要素として扱われます。イテレータパターンを使用すると、イテレータオブジェクトを介してプレイリスト内の曲に逐次アクセスし、再生、一時停止、曲の変更などの操作を行うことができます。
イテレータパターンの構成#
- 抽象イテレータ(Iterator):コレクションオブジェクトを遍歴するために必要なメソッドを定義し、hashNext () や next () メソッドなどを含みます。
- 具体的イテレータ(Concrete Iterator):イテレータインターフェースの具体的な実装クラスで、具体的な遍歴ロジックを担当します。現在の遍歴位置情報を保持し、必要に応じてコレクション要素を前後に遍歴できます。
- 抽象コレクション(Aggregate):一般的にはインターフェースで、iterator () メソッドを提供します。たとえば、Java の Collection インターフェース、List インターフェース、Set インターフェースなどです。
- 具体的コレクション(ConcreteAggregate):抽象コレクションの具体的な実装クラスで、たとえば List インターフェースの順序付きリスト実装である ArrayList、List インターフェースのリンクリスト実装である LinkList、Set インターフェースのハッシュリスト実装である HashSet などです。
実装#
デザインパターン第 16 講 —— イテレータパターン(Iterator)-CSDN ブログ
ステートパターン#
オブジェクトが内部状態を変更するときに、その振る舞いを変更することを許可します。異なる状態を隔離します;各状態は個別のクラスです。
コアロール#
- コンテキスト(Context):現在の状態の参照を維持します。
- 抽象状態(State):状態の振る舞いのインターフェースを宣言します。
- 具体的状態(Concrete State):特定の状態での振る舞いを実装し、状態遷移を引き起こす可能性があります。
実装#
// 抽象状態インターフェース:注文状態の振る舞い契約 (参考ウェブページ2[2](@ref)とウェブページ8[8](@ref))
public interface OrderState {
void pay(OrderContext context); // 支払い操作
void cancel(OrderContext context); // 注文のキャンセル
void ship(OrderContext context); // 発送操作
void refund(OrderContext context); // 返金申請
void confirmReceipt(OrderContext context); // 受取確認
}
// 支払い待ち
public class PendingPaymentState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("✅ 支払い成功、注文は支払い済み状態に入ります");
context.setState(new PaidState());
}
@Override
public void cancel(OrderContext context) {
System.out.println("❌ 注文はキャンセルされました");
context.setState(new CanceledState());
}
// 他の操作は禁止(参考ウェブページ8の状態制約[8](@ref))
@Override
public void ship(OrderContext context) {
throw new IllegalStateException("支払い待ちの注文は発送できません");
}
// ... 他のメソッドの禁止ロジックを類似に実装
}
// 支払い済み
public class PaidState implements OrderState {
@Override
public void ship(OrderContext context) {
System.out.println("🚚 商品が発送されました");
context.setState(new ShippedState());
}
@Override
public void refund(OrderContext context) {
System.out.println("🔄 返金申請が受理されました");
context.setState(new RefundingState());
}
// 重複支払いは禁止(参考ウェブページ3の状態遷移[3](@ref))
@Override
public void pay(OrderContext context) {
throw new IllegalStateException("支払い済みの注文は再度支払いできません");
}
}
// 発送済み
public class ShippedState implements OrderState {
@Override
public void confirmReceipt(OrderContext context) {
System.out.println("🎁 受取確認、取引完了");
context.setState(new CompletedState());
}
@Override
public void refund(OrderContext context) {
System.out.println("📦 返品プロセスを開始します");
context.setState(new RefundingState());
}
}
// コンテキストクラス(注文主体)
public class OrderContext {
private OrderState currentState;
private String orderId;
public OrderContext() {
this.currentState = new PendingPaymentState(); // 初期状態 支払い待ち
this.orderId = UUID.randomUUID().toString();
}
// 状態遷移のエントリーポイント(参考ウェブページ2のTransitionTo設計[2](@ref))
public void setState(OrderState state) {
System.out.printf("【状態変更】%s → %s%n",
currentState.getClass().getSimpleName(),
state.getClass().getSimpleName());
this.currentState = state;
}
// 現在の状態に操作を委任(参考ウェブページ3のコンテキスト設計[3](@ref))
public void pay() { currentState.pay(this); }
public void cancel() { currentState.cancel(this); }
// ... 他の操作の委任メソッド
}
// クライアント呼び出しの例
public class Client {
public static void main(String[] args) {
OrderContext order = new OrderContext();
order.pay(); // ✅ 正常支払い
order.ship(); // ✅ 発送操作
order.pay(); // ❌ IllegalStateExceptionをスロー
try {
order.cancel(); // ❌ 発送済みの注文はキャンセルできません
} catch (Exception e) {
System.out.println("操作失敗: " + e.getMessage());
}
}
}
テンプレートメソッドパターン#
Java デザインパターン —— テンプレートメソッドパターン【Template Method Pattern】_java テンプレートパターン - CSDN ブログ
抽象クラスで、そのメソッドを実行するためのテンプレートを公開します。サブクラスは必要に応じてメソッドをオーバーライドできますが、呼び出しは抽象クラスで定義された方法で行われます。簡単に言えば、テンプレートメソッドパターンは、操作のアルゴリズムの骨組みを定義し、いくつかのステップをサブクラスに遅延させることで、サブクラスがアルゴリズムの構造を変更せずに、特定のステップを再定義できるようにします。このタイプのデザインパターンは行動型パターンに属します。コアの考え方は「不変をカプセル化し、可変を拡張する」です。
構造
-
抽象クラス
- テンプレートメソッド:アルゴリズムの骨組みを定義します(通常は
final
修飾子でサブクラスによるオーバーライドを防ぎます)。基本メソッドと抽象メソッドを含みます。 - 基本メソッド:一般的なステップの実装(例:
boilWater()
水を沸かす)、直接継承するかデフォルト実装を提供します。 - 抽象メソッド:サブクラスが実装しなければならないステップ(例:
brew()
コーヒーを淹れる) - フックメソッド(Hook Method):オプションのステップ(例:
customerWantsCondiments()
トッピングを追加するかどうか)、サブクラスが選択的にオーバーライドできます。
- テンプレートメソッド:アルゴリズムの骨組みを定義します(通常は
-
具体的サブクラス
- 抽象クラスを継承し、抽象メソッドを実装するかフックメソッドをオーバーライドし、具体的なロジックを提供します(例:
Americano
がコーヒーの淹れ方の詳細を実装)。
- 抽象クラスを継承し、抽象メソッドを実装するかフックメソッドをオーバーライドし、具体的なロジックを提供します(例:
実装
// 抽象クラス
public abstract class CoffeeMaker {
// テンプレートメソッド(アルゴリズムの骨組みを定義)
public final void makeCoffee() {
boilWater();
brew();
pourInCup();
if (needCondiments()) {
addCondiments();
}
}
// 基本メソッド(一般的なステップ)
private void boilWater() { System.out.println("水を沸かす"); }
// 抽象メソッド(サブクラスが実装しなければならない)
protected abstract void brew();
// フックメソッド(オプションのステップ)
protected boolean needCondiments() { return true; }
protected void addCondiments() {} // デフォルトの空実装
}
// 具体的サブクラス
public class Americano extends CoffeeMaker {
@Override
protected void brew() { System.out.println("アメリカーノを淹れる"); }
@Override
protected boolean needCondiments() { return false; } // トッピングを追加しない
}
ストラテジーパターン#
ストラテジーパターン(Strategy Pattern)は、一連の同じタイプのアルゴリズムを定義し、異なるクラスにカプセル化します。各アルゴリズムは、現在のシーンに応じて相互に置き換えることができ、アルゴリズムの変化をそれを使用するクライアント(つまりアルゴリズムの呼び出し者)から独立させます。
- アルゴリズムを相互に置き換え可能にカプセル化
- アルゴリズムを使用するユーザーから独立させる
ユーザーが支払い時に異なる支払い方法を使用する場合を例にとると、通常はユーザーがどの支払い方法を使用するかを判断し、対応する支払いページにリダイレクトする必要があります。このような欠点も明らかです:
- 拡張性が低い
- その後、任意のロジックを変更すると、現在のメソッドが変更されます
3 層以上の if-else のロジック判断コードは、ガード文、ストラテジーパターン、ステートパターンなどを使用して実装できます。
ストラテジーパターンは、インターフェースを作成し **(ストラテジーインターフェースを定義)、それに異なる実装を加える(ストラテジーインターフェースを実装)** ことで、コードの拡張性を高めます。
インターフェースクラスはビジネス戦略の定義のみを担当し、各戦略の具体的な実装は実装クラスに個別に配置され、ファクトリークラス Factory は具体的な実装クラスを取得するだけで、具体的な呼び出しコードはビジネスロジックの編成を担当します。これらの実装は、実装ではなくインターフェースに基づくプログラミングを使用し、単一責任、オープン・クローズド原則を満たし、機能的に高い内聚性と低い結合度を達成し、メンテナンス性、拡張性、コードの可読性を向上させます。
ファクトリーパターンの作成
public class PaymentFactory {
private static final Map<PayTypeEnum, Payment> payStrategies = new HashMap<>();
static {
payStrategies.put(PayTypeEnum.WX, new WxPayment());
payStrategies.put(PayTypeEnum.ALIPAY, new AlipayPayment());
payStrategies.put(PayTypeEnum.BANK_CARD, new BankCardPayment());
}
public static Payment getPayment(PayTypeEnum payType) {
if (payType == null) {
throw new IllegalArgumentException("支払いタイプが空です。");
}
if (!payStrategies.containsKey(payType)) {
throw new IllegalArgumentException("支払いタイプはサポートされていません。");
}
return payStrategies.get(payType);
}
}
使用
Order order = 注文情報
PayResult payResult = PaymentFactory.getPayment(payType).pay(order);
if (payResult == PayResult.SUCCESS) {
System.out.println("支払い成功");
} else if (payType == 支払い方法) {
System.out.println("支払い失敗");
}
オブザーバーパターン#
図解デザインパターン — Graphic Design Patterns
オブザーバーパターンは、ソフトウェアデザインパターンの一種で、行動型パターンの一つです。このパターンは、オブジェクトとオブジェクトの間に依存関係を確立するために使用され、** あるオブジェクトの状態が変化すると、それに依存するすべてのオブジェクトが通知され、自動的に更新されます。** ここで、変化が起こるオブジェクトは観察対象と呼ばれ、通知されるオブジェクトはオブザーバーと呼ばれます。一つの観察対象は複数のオブザーバーに対応でき、これらのオブザーバー間には相互の関係がなく、必要に応じてオブザーバーを追加または削除できるため、システムの拡張が容易になります。これがオブザーバーパターンの動機です。
オブザーバーパターンには、主に 2 つの役割があります:
- Subject: 対象(テーマ)
- ConcreteSubject: 具体的な対象で、オブザーバーを追加、削除、通知するメソッドを提供します。テーマの状態が変化すると、オブザーバーリストを遍歴し、各オブザーバーのメソッドを呼び出して通知します。
- Observer: オブザーバー
- ConcreteObserver: 具体的なオブザーバーで、更新メソッドを定義し、テーマの状態が変化すると、すべての依存するオブザーバーが通知を受け取り、適切な操作を実行します。
実装
オブザーバーパターン(Observer) Java 実装_java implements observer-CSDN ブログ
Subject 抽象テーマ役割クラス
public abstract class Subject {
/**
* 登録されたオブザーバーオブジェクトを保存するためのリスト
*/
private List<Observer> list = new ArrayList<>();
/**
* オブザーバーオブジェクトを登録します
* @param observer オブザーバーオブジェクト
*/
public void attach(Observer observer) {
list.add(observer);
System.out.println("オブザーバーが追加されました");
}
/**
* オブザーバーオブジェクトを削除します
* @param observer オブザーバーオブジェクト
*/
public void detach(Observer observer) {
list.remove(observer);
}
/**
* 登録されたすべてのオブザーバーオブジェクトに通知します
*/
public void notifyObservers(String newState) {
for (Observer observer : list) {
observer.update(newState);
}
}
}
ConcreteSubject 具体的なテーマ
public class ConcreteSubject extends Subject {
private String state;
public String getState() {
return state;
}
public void change(String newState) {
state = newState;
System.out.println("テーマの状態は:" + state);
// 状態が変化したので、各オブザーバーに通知
this.notifyObservers(state);
}
}
Observer 抽象オブザーバー役割クラス
public interface Observer {
/**
* 更新インターフェース
* @param state 更新された状態
*/
void update(String state);
}
ConcreteObserver 具体的なオブザーバー役割クラス
public class ConcreteObserver implements Observer {
// オブザーバーの状態
private String observerState;
@Override
public void update(String state) {
/**
* オブザーバーの状態を更新し、テーマの状態と一致させます
*/
observerState = state;
System.out.println("状態は:" + observerState);
}
}
クライアントクラス
public class Client {
public static void main(String[] args) {
// テーマオブジェクトを作成
ConcreteSubject subject = new ConcreteSubject();
// オブザーバーオブジェクトを作成
Observer observer = new ConcreteObserver();
// オブザーバーオブジェクトをテーマオブジェクトに登録
subject.attach(observer);
// テーマオブジェクトの状態を変更
subject.change("新しい状態");
}
}
利点
- 表示層とデータロジック層の分離を実現し、安定したメッセージ更新伝達メカニズムを定義し、更新インターフェースを抽象化することで、さまざまな異なる表示層を具体的なオブザーバー役割として持つことができます。
- 観察対象とオブザーバー間に抽象的な結合を確立します。
- ブロードキャスト通信をサポートします。
- 「オープン・クローズド原則」の要件を満たします。
欠点
- オブザーバーが多数存在する場合、すべてのオブザーバーに通知するのに多くの時間がかかります。
- オブザーバーと観察対象の間に循環依存がある場合、観察対象がそれらの間で循環呼び出しを引き起こし、システムがクラッシュする可能性があります。
- オブザーバーが観察対象の状態がどのように変化したかを知るためのメカニズムがなく、単に観察対象が変化したことを知るだけです。
応用
一対一または一対多のオブジェクト相互作用シーンには、オブザーバーパターンを使用できます。
拡張#
MVC (Model-View-Controller)
MVC パターンは、アーキテクチャパターンで、3 つの役割(モデル(Model)、ビュー(View)、コントローラー(Controller))を含みます。オブザーバーパターンは MVC パターンを実装するために使用でき、オブザーバーパターンの観察対象は MVC パターンのモデル(Model)であり、オブザーバーは MVC のビュー(View)であり、コントローラー(Controller)は両者の間の仲介者(Mediator)として機能します。モデル層のデータが変更されると、ビュー層は自動的に表示内容を変更します。
-
Model:アプリケーションのデータ層またはビジネスロジック層を表します。アプリケーションの状態を管理し、データベースや他の永続ストレージと直接やり取りします。
-
View:ユーザーインターフェースで、データをユーザーに表示し、ユーザーの入力を受け取ります。通常はコントローラーとだけ通信します。
-
Controller:モデルとビューの間の仲介者として機能します。ユーザーの入力(HTTP リクエストなど)を受け取り、これらの入力を処理(モデルのメソッドを呼び出すことを含む可能性があります)し、どのビューを表示するかを決定します。
モバイル開発における iOS Swift(UIKit)と Android(Activity/Fragment)
MVC、MVP、MVVM の概要
MVC (Model-View-Controller)
-
Model:アプリケーションのデータ層またはビジネスロジック層を表します。アプリケーションの状態を管理し、データベースや他の永続ストレージと直接やり取りします。
-
View:ユーザーインターフェースで、データをユーザーに表示し、ユーザーの入力を受け取ります。通常はコントローラーとだけ通信します。
-
Controller:モデルとビューの間の仲介者として機能します。ユーザーの入力(HTTP リクエストなど)を受け取り、これらの入力を処理(モデルのメソッドを呼び出すことを含む可能性があります)し、どのビューを表示するかを決定します。
モバイル開発における iOS Swift(UIKit)と Android(Activity/Fragment)
MVVM (Model-View-ViewModel)
-
Model:前の 2 つのパターンと同様に、データとビジネスロジックを担当します。
-
View:ユーザーインターフェースで、バインディングメカニズムを介して ViewModel と対話します。View は ViewModel の存在を知らず、データをどのように表示するかのみに関心があります。
-
ViewModel:Model と View を接続する橋渡しです。View に公開される公共のプロパティとコマンドを持ち、データバインディングとコマンドバインディングの方法で View と対話します。ViewModel は具体的な View を知らないため、単体テストを実行しやすくなります。
JavaScript フレームワークの Vue.js、Angular、React(React はコンポーネント設計に傾いていますが、状態管理ライブラリの Redux と組み合わせて MVVM に似たパターンを実現できます)
2 つのパターンの主な違いは:
1、データバインディング:MVVM の顕著な特徴は双方向データバインディングで、データの変化が UI に自動的に同期され、ユーザーが入力したデータが直接データモデルにフィードバックされることを可能にします。一方、MVC パターンでは、この同期は通常コントローラーを介して手動で実装する必要があります。
2、責任の分離:MVC と MVVM はどちらも責任の分離の原則を強調していますが、MVVM は ViewModel を導入することで、View と Model 間の直接的な依存をさらに減らし、コードのテスト可能性と保守性を向上させます。
3、View と Controller の関係:MVC では、View と Controller の間に直接的な相互作用がありますが、MVVM では、View は主に ViewModel と対話し、View がビジネスロジックに直接依存することを減らし、UI デザインをより柔軟にします。