[15] デザインパターン ~~~ 動作パターン ~~~ 状態パターン (Java)

【学習難易度:★★★☆☆、使用頻度:★★★☆☆】

4.1. パターンの動機付け

  • 多くの場合、オブジェクトの動作は、値から抽出される 1 つ以上の動的に変化するプロパティに依存します。そのようなプロパティは状態と呼ばれ、そのようなオブジェクトはステートフル オブジェクトと呼ばれます。このようなオブジェクトが外部イベントと対話すると、その内部状態が変化し、それに応じてシステムの動作も変化します。
  • UML で状態図を使用すると、オブジェクトの状態の変化を記述することができます。

ケース1

      「人には悲しみもあれば喜びもあり、月には曇りや晴れの満ち欠けがある。」人間を含む多くのものには複数の状態があり、状態が異なれば異なる動作をし、また、これらの状態は特定の条件下では相互に変化します。条件。水と同じように、凍って氷になったり、加熱されて蒸発して水蒸気になったり、水が流れたり、氷が削られたり、蒸気が拡散したりすることができます。図 1 に示すように、UML 状態図を使用して H2O の 3 つの状態を説明できます。
ここに画像の説明を挿入

      ソフトウェア システムでは、水のように複数の状態を持つオブジェクトがあり、これらの状態は特定の状況下で相互に変換でき、オブジェクトは状態が異なると異なる動作をします。複数の状態を持つこれらのオブジェクトをより適切に設計するために、状態パターンと呼ばれる設計パターンを使用できます。

ケース 2 (銀行システムにおけるアカウント クラスの設計)

      Sunny Software Company は、銀行用のクレジット カード ビジネス システムを開発する予定です。銀行口座 (Account) は、システムの中核クラスの 1 つです。分析を通じて、Sunny Software Company の開発者は、システム内に口座の 3 つの状態があることを発見しました。 、および in 異なる状態のアカウントには異なる動作があり、詳細は次のとおりです。

  • (1) アカウントの残高が 0 以上の場合、アカウントの状態は通常の状態 (Normal State) であり、この時点でユーザーはアカウントに入金またはアカウントから引き出すことができます。
  • (2) 口座残高が0未満かつ-2000以上の場合、口座の状態は当座貸越状態(Overdraft State)となり、このとき利用者は口座への入出金が可能ですが、利息は毎日計算する必要があります。
  • (3) 口座残高が -2000 の場合、口座の状態は制限状態 (Restricted State) となり、この時点ではユーザーは口座への入金のみが可能であり、口座からの出金はできず、利息は支払われません。また、日単位で計算されます。
  • (4) 天びんに応じて、上記 3 つの状態を相互に変換できます。

      Sunny Software Company の開発者は銀行口座クラスを分析し、図 2 に示す UML 状態図を作成しました。
ここに画像の説明を挿入

      図 2 では、NormalState は通常の状態を表し、OverdraftState は当座貸越の状態を表し、RestrictedState は制限された状態を表します。これら 3 つの状態では、アカウント オブジェクトの動作は異なります。入金にはメソッド deposit() が使用され、withdraw() は使用されます出金の場合はcomputeInterest()で利息の計算を、stateCheck()で状態遷移を行うかどうかの判断を行い、入出金操作後の残高に応じた状態遷移を実装しています。州。異なる状態のオブジェクトのさまざまな動作とオブジェクト状態間の相互変換を実現するために、Sunny Software Company の開発者は比較的大きなアカウント クラス Account を設計しました。コードの一部は次のとおりです。

class Account {
    
    
	private String state; //状态
	private int balance; //余额
	......
	
	//存款操作	
	public void deposit() {
    
    
		//存款
		stateCheck();	
	}
	
	//取款操作
	public void withdraw() {
    
    
		if (state.equalsIgnoreCase("NormalState") || state.equalsIgnoreCase("OverdraftState ")) {
    
    
			//取款
			stateCheck();
		}
		else {
    
    
			//取款受限
		}
	}
	
	//计算利息操作
	public void computeInterest() {
    
    
		if(state.equalsIgnoreCase("OverdraftState") || state.equalsIgnoreCase("RestrictedState ")) {
    
    
			//计算利息
		}
	}
	
	//状态检查和转换操作
	public void stateCheck() {
    
    
		if (balance >= 0) {
    
    
			state = "NormalState";
		}
		else if (balance > -2000 && balance < 0) {
    
    
			state = "OverdraftState";
		}
		else if (balance == -2000) {
    
    
			state = "RestrictedState";
		}
        else if (balance < -2000) {
    
    
            //操作受限
        }
	}
	......
}

      上記のコードを分析すると、次の問題が簡単に見つかります。

  • (1) ほぼすべてのメソッドには、メソッドが状態に存在するかどうか、およびメソッドを特定の状態でどのように実装するかを判断するための状態判断ステートメントが含まれているため、コードが非常に長くなり、保守性が悪くなります。
  • (2) 比較的複雑な stateCheck() メソッドがあり、状態遷移用の if...else if...else... ステートメントが多数含まれており、コード テストは難しく、保守も容易ではありません。
  • (3) システムの拡張性が悪く、凍結状態(Frozen State、入金も出金もできない状態)など、新たな状態を追加する必要がある場合、拡張のために元のコードを大幅に修正する必要があるとても面倒です。

      これらの問題を解決するには、ステート モードを使用します。ステート モードでは、各ステートでのオブジェクトの動作と各ステート クラスでのステート遷移ステートメントをカプセル化し、これらのステート クラスを使用して長い条件遷移を分散します。システムをより柔軟かつスケーラブルにするために、状態モデルは上記の問題をある程度解決できます。

4.2. スキーマ定義

      状態パターン: オブジェクトの内部状態が変化したときにオブジェクトの動作を変更できるようにし、オブジェクトはそのクラスを変更しているように見えます。その別名は Objects for States で、状態モードはオブジェクト動作モードです。

4.3. スキーマ構造

状態パターンには次の役割が含まれます。

  • コンテキスト:コンテキスト クラスとも呼ばれる環境クラスは、複数の状態を持つオブジェクトです。環境クラスの状態の多様性とさまざまな状態でのオブジェクトの動作により、状態は分離されて別の状態クラスを形成します。抽象状態クラス State のインスタンスは環境クラスに保持され、このインスタンスは現在の状態を定義します。具体的な実装では、State サブクラスのオブジェクトになります。
  • 状態:環境クラスの特定の状態に関連する動作をカプセル化するインターフェイスを定義するために使用される抽象状態クラス。さまざまな状態に対応するメソッドが抽象状態クラスで宣言され、これらのメソッドはそのサブクラスで実装されます。異なる状態のオブジェクトの動作は異なる場合があり、異なるサブクラスのメソッドの実装は異なる場合があり、同じメソッドを抽象状態クラスに記述することができます。
  • ConcreteState: 抽象状態クラスのサブクラスである具体状態クラス。各サブクラスは環境クラスの状態に関連する動作を実装します。各具体状態クラスは環境の特定の状態に対応します。異なる具体状態クラスは異なる動作を持ちます。違う。
    ここに画像の説明を挿入

      状態モードでは、さまざまな状態にあるオブジェクトの動作をさまざまな状態クラスにカプセル化します。システムをより柔軟でスケーラブルにし、同時に各状態の共通の動作をカプセル化するには、状態が抽象化され、抽象状態クラスの役割が導入され、典型的なコードは次のとおりです。

abstract class State {
    
    
    //声明抽象业务方法,不同的具体状态类可以不同的实现
	public abstract void handle();
}

      抽象状態クラスで宣言されたビジネス メソッドは、抽象状態クラスのサブクラス、つまり具体状態クラスに実装されます。異なる具体状態クラスは、まったく異なるメソッド実装を提供できます。実際の使用では、状態クラスに複数の If が含まれる場合があります。特定の状態クラスの一部のビジネス メソッドの実装はまったく同じですが、これらのメソッドを抽象状態クラスに移動してコードの再利用を実現できます。特定の状態クラスの一般的なコードは次のとおりです。

class ConcreteState extends State {
    
    
	public void handle() {
    
    
		//方法具体实现代码
	}
}

      環境クラスは、抽象状態クラスへの参照を維持します。setState() メソッドを通じて、さまざまな状態オブジェクトを環境クラスに挿入でき、その後、状態オブジェクトのメソッドが環境クラスのビジネス メソッドで呼び出されます。典型的なコードは次のとおりです。

class Context {
    
    
	private State state; //维持一个对抽象状态对象的引用
	private int value; //其他属性值,该属性值的变化可能会导致对象状态发生变化
 
    //设置状态对象
	public void setState(State state) {
    
    
		this.state = state;
	}
 
	public void request() {
    
    
		//其他代码
		state.handle(); //调用状态对象的业务方法
		//其他代码
	}
}

      環境クラスは実際には状態を持つオブジェクトであり、環境クラス内の状態に関連するコードを抽出し、それを特別な状態クラスにカプセル化するだけです。状態モード構造図では、環境クラスContextと抽象状態クラスStateの間に一方向の関連関係があり、ContextにStateオブジェクトが定義されている。実際の使用では、それらの間にはより複雑な関係が存在する可能性があり、状態とコンテキストの間に依存関係や関連性が存在する場合もあります。
      状態モードを使用するプロセスでは、オブジェクトの状態を相互に変換することもできます。状態変換を実現するには、通常 2 つの方法があります: (1) 環境クラスが状態間の遷移を担当
      ます。 , 環境クラス 状態管理(State Manager)としても機能します 環境クラスのビジネスメソッドでは、いくつかの属性値を判定することで状態遷移を実現します 属性判定と状態遷移を実現するための特別なメソッドを提供することもできます、次のコード スニペットに示すように:


      public void changeState() {
    
    
		//判断属性值,根据属性值进行状态转换
      if (value == 0) {
    
    
			this.setState(new ConcreteStateA());
		}
		else if (value == 1) {
    
    
			this.setState(new ConcreteStateB());
		}

	}

      (2) 特定の状態クラスは状態間の遷移を担当します。特定の状態クラスのビジネス メソッドで環境クラスのいくつかの属性値を判断し、次に従って環境クラスの新しい状態オブジェクトを設定できます。同様に、属性値と状態遷移の判断を担当する特別なメソッドを提供することも可能です。この時点で、次のコード スニペットに示すように、状態クラスは環境クラスの属性値にアクセスする必要があるため、状態クラスと環境クラスの間に依存関係または関連関係が存在します。

	……
      public void changeState(Context ctx) {
    
    
		//根据环境对象中的属性值进行状态转换
      if (ctx.getValue() == 1) {
    
    
			ctx.setState(new ConcreteStateB());
		}
		else if (ctx.getValue() == 2) {
    
    
			ctx.setState(new ConcreteStateC());
		}
        ......
	}
    ……

4.4. タイミング図

ここに画像の説明を挿入

4.5. コード分析

      Sunny Software Company の開発者は、アカウント状態の変換問題を解決するためにステート モードを使用しており、クライアントは簡単な入出金操作を行うだけで、残高に応じてシステムが自動的に対応する状態に切り替わります。図 4 に示します。
ここに画像の説明を挿入

4.5.1 生産

      図 4 では、Account は環境クラスの役割として機能し、AccountState は抽象状態の役割として機能し、NormalState、OverdraftState、および RestrictedState は具体的な状態の役割として機能します。完全なコードは次のとおりです。

package com.zyz.demo;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/28 21:50
 * @Description:
 */


//银行账户:环境类

class Account {
    
    
    private AccountState state; //维持一个对抽象状态对象的引用
    private String owner; //开户名
    private double balance = 0; //账户余额

    public Account(String owner,double init) {
    
    
        this.owner = owner;
        this.balance = balance;
        this.state = new NormalState(this); //设置初始状态
        System.out.println(this.owner + "开户,初始金额为" + init);
        System.out.println("---------------------------------------------");
    }

    public double getBalance() {
    
    
        return this.balance;
    }

    public void setBalance(double balance) {
    
    
        this.balance = balance;
    }

    public void setState(AccountState state) {
    
    
        this.state = state;
    }

    public void deposit(double amount) {
    
    
        System.out.println(this.owner + "存款" + amount);
        state.deposit(amount); //调用状态对象的deposit()方法
        System.out.println("现在余额为"+ this.balance);
        System.out.println("现在帐户状态为"+ this.state.getClass().getName());
        System.out.println("---------------------------------------------");
    }

    public void withdraw(double amount) {
    
    
        System.out.println(this.owner + "取款" + amount);
        state.withdraw(amount); //调用状态对象的withdraw()方法
        System.out.println("现在余额为"+ this.balance);
        System.out.println("现在帐户状态为"+ this. state.getClass().getName());
        System.out.println("---------------------------------------------");
    }

    public void computeInterest()
    {
    
    
        state.computeInterest(); //调用状态对象的computeInterest()方法
    }
}


//抽象状态类

abstract class AccountState {
    
    
    protected Account acc;
    public abstract void deposit(double amount);
    public abstract void withdraw(double amount);
    public abstract void computeInterest();
    public abstract void stateCheck();
}


//正常状态:具体状态类

class NormalState extends AccountState {
    
    
    public NormalState(Account acc) {
    
    
        this.acc = acc;
    }

    public NormalState(AccountState state) {
    
    
        this.acc = state.acc;
    }

    @Override
    public void deposit(double amount) {
    
    
        acc.setBalance(acc.getBalance() + amount);
        stateCheck();
    }
    @Override
    public void withdraw(double amount) {
    
    
        acc.setBalance(acc.getBalance() - amount);
        stateCheck();
    }

    @Override
    public void computeInterest()
    {
    
    
        System.out.println("正常状态,无须支付利息!");
    }

    /**
     * 状态转换
     */
    @Override
    public void stateCheck() {
    
    
        if (acc.getBalance() > -2000 && acc.getBalance() <= 0) {
    
    
            acc.setState(new OverdraftState(this));
        }
        else if (acc.getBalance() == -2000) {
    
    
            acc.setState(new RestrictedState(this));
        }
        else if (acc.getBalance() < -2000) {
    
    
            System.out.println("操作受限!");
        }
    }
}

//透支状态:具体状态类

class OverdraftState extends AccountState
{
    
    
    public OverdraftState(AccountState state) {
    
    
        this.acc = state.acc;
    }

    @Override
    public void deposit(double amount) {
    
    
        acc.setBalance(acc.getBalance() + amount);
        stateCheck();
    }

    @Override
    public void withdraw(double amount) {
    
    
        acc.setBalance(acc.getBalance() - amount);
        stateCheck();
    }

    @Override
    public void computeInterest() {
    
    
        System.out.println("计算利息!");
    }

    /**
     * 状态转换
     */
    @Override
    public void stateCheck() {
    
    
        if (acc.getBalance() > 0) {
    
    
            acc.setState(new NormalState(this));
        }
        else if (acc.getBalance() == -2000) {
    
    
            acc.setState(new RestrictedState(this));
        }
        else if (acc.getBalance() < -2000) {
    
    
            System.out.println("操作受限!");
        }
    }
}

//受限状态:具体状态类

class RestrictedState extends AccountState {
    
    
    public RestrictedState(AccountState state) {
    
    
        this.acc = state.acc;
    }

    @Override
    public void deposit(double amount) {
    
    
        acc.setBalance(acc.getBalance() + amount);
        stateCheck();
    }

    @Override
    public void withdraw(double amount) {
    
    
        System.out.println("帐号受限,取款失败");
    }

    @Override
    public void computeInterest() {
    
    
        System.out.println("计算利息!");
    }

    /**
     * 状态转换
     */
    @Override
    public void stateCheck() {
    
    
        if(acc.getBalance() > 0) {
    
    
            acc.setState(new NormalState(this));
        }
        else if(acc.getBalance() > -2000) {
    
    
            acc.setState(new OverdraftState(this));
        }
    }
}

4.5.2 クライアント

package com.zyz.demo;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/28 21:53
 * @Description:
 */
public class Client {
    
    
    public static void main(String args[]) {
    
    
        Account acc = new Account("段誉",0.0);
        acc.deposit(1000);
        acc.withdraw(2000);
        acc.deposit(3000);
        acc.withdraw(4000);
        acc.withdraw(1000);
        acc.computeInterest();
    }
}

4.5.3 テスト結果

ここに画像の説明を挿入

4.6. パターン分析

  • 状態パターンは、オブジェクトの状態の変化と、各状態でオブジェクトがどのように異なる動作をするかを記述します。
  • 状態パターンの鍵は、オブジェクトの状態を具体的に表す抽象クラスを導入することです。このクラスは抽象状態クラスと呼ばれ、オブジェクトの各特定状態クラスはこのクラスを継承し、異なる特定状態で異なる型を実装します。クラス: さまざまな状態間の遷移を含む状態の動作。
    状態パターン構造では、環境クラスと抽象状態クラスの役割を理解する必要があります。
  • 環境クラスは実際には状態を持つオブジェクトであり、場合によっては環境クラスが状態マネージャー (State Manager) として機能し、環境クラス内で状態を切り替えることができます。
  • 抽象状態クラスは、抽象クラスまたはインターフェイスにすることができます。異なる状態クラスは、この親クラスを継承する異なるサブクラスです。状態クラスは、環境クラスに複数の状態があり、次の 2 つの条件も満たすために生成されます。トグルが必要な場合、オブジェクトは状態ごとに異なる動作をします。したがって、さまざまなオブジェクトの下での動作を抽出して特定の状態クラスにカプセル化することができるため、内部状態が変化したときに環境クラス オブジェクトの動作を変更できます。オブジェクトはそのクラスを変更しているように見えますが、実際には Switch が原因です。別の具象状態クラスの実装に変更します。環境クラスは任意の特定の状態クラスに設定できるため、抽象状態クラス用にプログラムされ、プログラムの実行時に任意の特定の状態クラスのオブジェクトを環境クラスに設定できるため、環境クラスを変更できます。内部状態と動作の変化。

4.7. 例

TCPConnection
      の例は、TCP プロトコル実装の簡略化されたバージョンを示す「デザイン パターン」からのものです。TCP 接続には多くの可能な状態があり、状態間の遷移には対応する論理的な前提条件があります。これは、状態パターン、構造図
:
ここに画像の説明を挿入

タイミング図
ここに画像の説明を挿入

4.8. 利点

状態パターンの利点

  • 変換ルールをカプセル化します。
  • 可能な状態を列挙するには、状態を列挙する前に状態タイプを決定する必要があります。
  • 特定の状態に関連するすべての動作をクラスに入れると、新しい状態を簡単に追加できます。オブジェクトの動作を変更するには、オブジェクトの状態を変更するだけで済みます。
  • 状態遷移ロジックを、条件付きステートメントの巨大なブロックではなく、状態オブジェクトに統合できるようにします。
  • 複数の環境オブジェクトが状態オブジェクトを共有できるため、システム内のオブジェクトの数が減ります。

4.9. 欠点

状態パターンの欠点

  • ステート モードを使用すると、システム クラスとオブジェクトの数が必然的に増加します。
  • 状態パターンの構造と実装は比較的複雑であり、不適切に使用すると、プログラムの構造やコードが混乱する可能性があります。
  • 状態モデルは「開始と終了の原則」をあまりサポートしていません。状態を切り替えることができる状態モデルの場合、新しい状態クラスを追加するには、状態遷移を担当するソース コードを変更する必要があり、そうでないと新しい状態に切り替えることができません。状態クラスの動作を変更するには、対応するクラスのソース コードも変更する必要があります。

4.10. 適用環境

状態パターンは次の場合に使用できます。

  • オブジェクトの動作はその状態 (プロパティ) に依存し、状態の変化に応じて関連する動作を変更することができます。
  • コードにはオブジェクトの状態に関する条件文が多数含まれており、これらの条件文が出現するとコードの保守性や柔軟性が低下し、状態の追加や削除が容易にできなくなるため、オブジェクト間の結合が強化されます。クライアント クラスとクラス ライブラリ。オブジェクトの動作はこれらの条件ステートメントに含まれており、これらの条件はオブジェクトのさまざまな状態に対応します。

4.11. スキーマの適用

      状態モードは、ワークフローやゲームなどのソフトウェアで広く使用されており、これらのシステムの中核機能設計でも使用できます。たとえば、政府の OA オフィス システムでは、決裁文書には複数の状態があります。 ; 処理中; 進行中 承認; 審査中; 完了などの状態と、承認ステータスが異なると承認の動作が異なります。状態パターンを使用して、ワークフロー オブジェクト (バッチなど) の状態遷移と、さまざまな状態でのその動作を記述します。

4.12. スキーマ拡張

共有状態

  • 場合によっては、複数の環境オブジェクトが同じ状態を共有する必要がある場合があります。複数の環境オブジェクト インスタンスがシステム内の 1 つ以上の状態オブジェクトを共有する場合は、これらの状態オブジェクトを環境の静的メンバー オブジェクトとして定義する必要があります。

単純な状態パターンと切り替え可能な状態を含む状態パターン

  1. シンプル ステート モード: シンプル ステート モードとは、各状態が独立しており、状態を切り替える必要がない状態モードを指し、最も単純な状態モードです。この状態モードの場合、各状態クラスは状態の切り替えを気にせずに状態関連の操作をカプセル化します。クライアント上で状態クラスを直接インスタンス化し、状態オブジェクトを環境クラスに設定できます。この単純な状態モードであれば、「オープンクローズ原則」に従います。クライアント側では、抽象状態クラスをプログラムし、特定の状態クラスを設定ファイルに書き込むことができます。同時に、新しい状態を追加することもできます。このクラスは元のシステムにも影響しますが、影響はありません。
  2. 状態を切り替え可能なステート モード: ほとんどのステート モードは状態を切り替えることができるステート モードです。状態切り替えを実装する場合は、環境クラス Context の setState() メソッドを呼び出して、特定の状態クラスで状態変換操作を実行する必要があります。環境クラスのメソッドは状態クラス内で呼び出すことができるため、通常、状態クラスと環境クラスの間には関連または依存関係が存在します。状態の切り替えは、状態クラス内の環境クラスのオブジェクトを参照して、環境クラスの setState() メソッドをコールバックすることで実現されます。状態を切り替えることができるこの状態モードでは、新しい状態クラスを追加するには、他の状態クラスや環境クラスのソース コードを変更する必要がある場合があります。そうしないと、システムは新しく追加された状態に切り替えることができません。

4.13. 概要

  • 状態パターンを使用すると、オブジェクトの内部状態が変化したときにオブジェクトの動作を変更でき、オブジェクトはそのクラスを変更しているように見えます。そのエイリアスは状態オブジェクトであり、状態パターンはオブジェクトの動作パターンです。
  • 状態パターンには 3 つの役割が含まれています: 環境クラスはコンテキスト クラスとも呼ばれ、状態を持つオブジェクトであり、環境クラスで抽象状態クラス State のインスタンスを維持します。このインスタンスは現在の状態を定義します。サブクラスのオブジェクトは初期状態を定義できます。抽象状態クラスは、環境クラスの特定の状態に関連する動作をカプセル化するインターフェイスを定義するために使用されます。具象状態クラスは、環境クラスのサブクラスです。抽象状態クラスであり、各サブクラスは環境に関連する状態を実装します。クラスの状態関連の動作、各特定の状態クラスは環境の特定の状態に対応し、異なる特定の状態クラスは異なる動作を持ちます。
  • 状態パターンは、オブジェクトの状態の変化と、各状態でオブジェクトがどのように異なる動作をするかを記述します。
  • 状態パターンの主な利点は、遷移ルール​​をカプセル化し、可能な状態を列挙することです。特定の状態に関連するすべての動作をクラスに入れ、新しい状態を簡単に追加できます。オブジェクトの状態を変更するだけで済みます。オブジェクトの動作を変更すると、複数の環境オブジェクトが 1 つの状態オブジェクトを共有できるようになり、それによってシステム内のオブジェクトの数が減ります。欠点は、状態パターンの使用によってシステム クラスとオブジェクトの数が増加し、構造とオブジェクトの数が増加することです。ステートパターンの実装が異なる より複雑になる 使い方を誤るとプログラム構造やコードの混乱を招く ステートを切り替えるステートモードは「開閉原理」の要件を満たさない
  • 状態パターンの適用には次が含まれます: オブジェクトの動作はその状態 (プロパティ) に依存し、その関連する動作はその状態の変化に従って変更できます。コードにはオブジェクトの状態に関連する多数の条件文が含まれています。その結果、コードの保守性と柔軟性が低下し、状態の追加や削除が便利にできなくなり、クライアント クラスとクラス ライブラリ間の結合が強化されます。

4.14. オブジェクトの複数の状態とそれらの相互変換を処理する

状態遷移を実現する環境クラスを利用する ステート
      モードで状態遷移を実現する場合、特定の状態クラスが環境クラスContextのsetState()メソッドを呼び出すことで状態遷移操作を実行したり、一律に状態遷移を実現したりすることができます。環境クラス Context によってこの時点で、新しい特定の状態クラスを追加するには、他の特定の状態クラスまたは環境クラスのソース コードを変更する必要がある場合があります。そうしないと、システムは新しく追加された状態に移行できません。しかし、クライアントにとっては、状態クラスを意識する必要はなく、デフォルトの状態クラスを環境クラスに設定することができ、状態変換作業は特定の状態クラスまたは環境クラスに引き継がれて完了します。変換の詳細はクライアントに対して透過的です。

      上記の「銀行口座の状態遷移」の例では、特定の状態クラスを通じて状態遷移を実現しており、各特定の状態クラスにはstateCheck()メソッドが含まれており、そのメソッド内で状態遷移を実現しています。環境クラスを介して状態遷移を実装します。環境クラスは、さまざまな状態間の遷移操作を均一に実装する状態マネージャーとして機能します

      ループ状態を含む簡単な例を通して、環境クラスを使用して状態遷移を実装する方法を説明しましょう。

      Sunny Software Company の開発者は、画面拡大ツールを開発しようとしています。その具体的な機能は次のとおりです:
ユーザーが「虫眼鏡」ボタンをクリックすると画面が 2 倍になり、「虫眼鏡」ボタンをクリックすると画面が 2 倍になります。もう一度「Glass」ボタンを押します。このボタンを押すと、画面はデフォルトのサイズに戻ります。
      画面拡大ツールの設計には状態モードの使用を検討できます。画面の 3 つの状態 (通常状態、2 倍拡大状態、4 倍拡大状態) に対応する 3 つの画面状態クラス NormalState、LargerState、および LargestState を定義します。 screen クラス Screen 環境クラスとして機能するその構造を図 6 に示します。
ここに画像の説明を挿入

この例のコアコードは次のとおりです。

package com.zyz.demo1;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/28 22:12
 * @Description:
 */

//屏幕类

class Screen {
    
    
    //枚举所有的状态,currentState表示当前状态
    private State currentState, normalState, largerState, largestState;

    public Screen() {
    
    
        this.normalState = new NormalState(); //创建正常状态对象
        this.largerState = new LargerState(); //创建二倍放大状态对象
        this.largestState = new LargestState(); //创建四倍放大状态对象
        this.currentState = normalState; //设置初始状态
        this.currentState.display();
    }

    public void setState(State state) {
    
    
        this.currentState = state;
    }

    /**
     * 单击事件处理方法,封转了对状态类中业务方法的调用和状态的转换
     */
    public void onClick() {
    
    
        if (this.currentState == normalState) {
    
    
            this.setState(largerState);
            this.currentState.display();
        }
        else if (this.currentState == largerState) {
    
    
            this.setState(largestState);
            this.currentState.display();
        }
        else if (this.currentState == largestState) {
    
    
            this.setState(normalState);
            this.currentState.display();
        }
    }
}


//抽象状态类

abstract class State {
    
    
    public abstract void display();
}

//正常状态类

class NormalState extends State{
    
    
    @Override
    public void display() {
    
    
        System.out.println("正常大小!");
    }
}


//二倍状态类

class LargerState extends State{
    
    
    @Override
    public void display() {
    
    
        System.out.println("二倍大小!");
    }
}


//四倍状态类

class LargestState extends State{
    
    
    @Override
    public void display() {
    
    
        System.out.println("四倍大小!");
    }


}

      上記のコードでは、すべての状態遷移操作は環境クラス Screen によって実装されており、このとき、環境クラスは状態マネージャーとして機能します。「8 倍の状態クラス」などの新しい状態を追加する必要がある場合は、環境クラスを変更する必要があります。これは「開閉の原則」にある程度違反しますが、他の状態クラスには影響しません。
      テスト用に次のクライアント コードを作成します。

package com.zyz.demo1;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/28 22:13
 * @Description: 客户端
 */
class Client {
    
    
    public static void main(String args[]) {
    
    
        Screen screen = new Screen();
        screen.onClick();
        screen.onClick();
        screen.onClick();
    }
}

出力は次のとおりです。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_43304253/article/details/130986343
おすすめ