「状態」は、初級レベルから中級レベルのプログラミングに進むすべての開発者が経験する一般的なプログラミング用語です。では、「州」という用語は正確には何を意味するのでしょうか。
一般に、オブジェクトの状態は、オブジェクトまたはその一部の現在のスナップショットにすぎません。一方、コンピュータ サイエンスでは、プログラムの状態は、以前に保存された入力に関する位置として定義されます。このコンテキストでは、「状態」という用語は、科学と同じように使用されます。気体、液体、固体などのオブジェクトの状態は、現在の物理的性質とコンピューター プログラムの状態を表します。現在の値または内容を反映します。
保存された入力は、変数または定数としてコンピュータ プログラムに保存されます。プログラムの状態を評価する際に、開発者はこれらの入力に含まれる値を調べることがあります。プログラムの状態は、実行中に変化する可能性があります - 変数が変化する可能性があり、メモリ値が変化する可能性があります。たとえば、ループで使用されるような制御変数は、反復ごとにプログラムの状態を変更します。プログラムの現在の状態を調べることは、コードベースをテストまたは分析するために使用できます。
より単純なシステムでは、状態管理は if-else、if-then-else、try-catch ステートメント、またはブール フラグで処理されることがよくあります。ただし、プログラムで考えられる状態が多すぎる場合、これは役に立ちません。それらは、理解、保守、およびデバッグが困難な、不格好で複雑なコードにつながる可能性があります。
if-else-clauses または boolean の欠点の 1 つは、それらがかなり広範囲になる可能性があることです。また、多くの異なるクラスのコードを書き直す必要があるため、別の状態を追加することは困難です。メイン メニュー、ゲーム ループ、および完了画面を含むゲームを作成するとします。
たとえば、ビデオ プレーヤーを作成してみましょう。
class Video: def __init__(self, source): self.source = source self.is_playing = False self.is_paused = False self.is_stopped = True # A video can only be played when paused or stopped def play(self): if not self.is_playing or self.is_paused: # Make the call to play the video self.is_playing = True self.is_paused = False else: raise Exception( 'Cannot play a video that is already playing.' ) # A video can only be paused when it is playing def pause(self): if self.is_playing: # Make the call to pause the video self.is_playing = False self.is_paused = True else: raise Exception( 'Cannot pause a video that is not playing' ) # A video can only be stopped when it is playing or paused def stop(self): if self.is_playing or self.is_paused: # Make the call to stop the video self.is_playing = False self.is_paused = False else: raise Exception( 'Cannot stop a video that is not playing or paused' )
上記のコード スニペットは、単純なビデオ プレーヤー アプリケーションの if-else 実装であり、再生中、一時停止中、停止中の 3 つの基本的な状態があります。ただし、さらに状態を追加しようとすると、コードは急速に複雑になり、肥大化し、反復的になり、理解とテストが難しくなります。別の状態「巻き戻し」を追加したときのコードを見てみましょう。
class Video: def __init__(self, source): self.source = source self.is_playing = False self.is_paused = False self.is_rewinding = False self.is_stopped = True # A video can only be played when it is paused or stopped or rewinding def play(self): if self.is_paused or self.is_stopped or self.is_rewinding: # Make the call to play the video self.is_playing = True self.is_paused = False self.is_stopped = False self.is_rewinding = False else: raise Exception( 'Cannot play a video that is already playing.' ) # A video can only be paused when it is playing or rewinding def pause(self): if self.is_playing or self.is_rewinding: # Make the call to pause the video self.is_playing = False self.is_paused = True self.is_rewinding = False self.is_stopped = False else: raise Exception( 'Cannot pause a video that is not playing or rewinding' ) # A video can only be stopped when it is playing or paused or rewinding def stop(self): if self.is_playing or self.is_paused or self.is_rewinding: # Make the call to stop the video self.is_playing = False self.is_paused = False self.is_stopped = True self.is_rewinding = False else: raise Exception( 'Cannot stop a video that is not playing or paused or rewinding' ) # 4. A video can only be rewinded when it is playing or paused. def rewind(self): if self.is_playing or self.is_paused: # Make the call to rewind the video self.is_playing = False self.is_paused = False self.is_stopped = False self.is_rewinding = True else: raise Exception( 'Cannot rewind a video that is not playing or paused' )
State-pattern がなければ、update メソッドや draw メソッドを含め、コード全体でプログラムの現在の状態を調べる必要があります。設定画面など、4 つ目の状態を追加する場合は、多数の個別のクラスのコードを更新する必要があり、不便です。ここで、ステート マシンのアイデアが役に立ちます。
ステート マシンは、コンピューター サイエンスにおいて目新しい概念ではありません。これらは、ソフトウェア ビジネスで使用される基本的な設計パターンの 1 つです。コーディング指向というよりもシステム指向であり、ユースケースをモデル化するために使用されます。
Uber でタクシーを雇う簡単な実際の例を見てみましょう。
画面 1 は、このユース ケースのすべてのユーザーが最初に見る画面であり、自己完結型です。画面 2 は画面 1 に依存しており、画面 1 に正確なデータを与えるまで画面 2 に進むことはできません。同様に、画面 3 は画面 2 に依存していますが、画面 4 は画面 3 に依存しています。ドライバーが旅行をキャンセルしない場合は、画面 4 に移動し、現在の旅行が終了するまで別の旅行を計画することはできません。
激しい雨が降っていて、あなたの旅行を受け入れるドライバーがいないか、あなたの地域であなたの旅行を終えることができるドライバーが見つからないとしましょう。ドライバーが利用できないことを警告するエラー通知が表示され、画面 3 にとどまります。画面 2、画面 1、さらには最初の画面に戻ることもあります。
あなたはタクシー予約プロセスの別の段階にいます。現在の段階で指定されたアクションが成功した場合にのみ、次のレベルに進むことができます。たとえば、画面 1 で間違った場所を入力すると、画面 2 に進むことができず、画面 2 で旅行オプションを選択しない限り画面 3 に進むことができませんが、旅行がすでに予約されていない限り、常に前の段階に戻ります。
上記の例では、タクシーの予約プロセスをいくつかのアクティビティに分割しました。それぞれのアクティビティは、予約のステータスに基づいて別のアクティビティを呼び出すことが許可されている場合と許可されていない場合があります。これをモデル化するためにステート マシンが使用されます。原則として、これらの各段階/状態は自律的である必要があり、現在の段階/状態が正常に終了したかどうかにかかわらず、次の段階を呼び出す必要があります。
より専門的な言い方をすれば、ステート マシンを使用すると、前の例のタクシー予約アクティビティのように、大規模で複雑なアクションを一連の個別の小さなアクティビティに分割できます。
イベントは小さなタスクを接続し、ある状態から別の状態に移行することを遷移と呼びます。バックエンドでの予約の作成、請求書の発行、ユーザー分析データの保存、データベースへの予約データのキャプチャ、旅行終了後の支払いのトリガーなど、通常、ある状態から別の状態に変化した後にいくつかのアクションを実行します。 .
したがって、ステート マシンの一般式は次のようになります。
現在の状態 + 何らかのアクション / イベント = 別の状態
単純なビデオ プレーヤー アプリケーション用に設計されたステート マシンがどのようになるかを見てみましょう。
そして、次のようにトランジションを使用してコードで実装できます。
from transitions import Machine class Video: # Define the states PLAYING = 'playing' PAUSED = 'paused' STOPPED = 'stopped' def __init__(self, source): self.source = source # Define the transitions transitions = [ # 1. A video can only be played when it is paused or stopped. {'trigger': 'play', 'source': self.PAUSED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.STOPPED, 'dest': self.PLAYING}, # 2. A video can only be paused when it is playing. {'trigger': 'pause', 'source': self.PLAYING, 'dest': self.PAUSED}, # 3. A video can only be stopped when it is playing or paused. {'trigger': 'stop', 'source': self.PLAYING, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.PAUSED, 'dest': self.STOPPED}, ] # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self.STOPPED } def play(self): pass def pause(self): pass def stop(self): pass
ここで、巻き戻しなどの別の状態を追加したい場合に備えて、次のように簡単に行うことができます。
from transitions import Machine class Video: # Define the states PLAYING = 'playing' PAUSED = 'paused' STOPPED = 'stopped' REWINDING = 'rewinding' # new def __init__(self, source): self.source = source # Define the transitions transitions = [ # 1. A video can only be played when it is paused or stopped. {'trigger': 'play', 'source': self.PAUSED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.STOPPED, 'dest': self.PLAYING}, {'trigger': 'play', 'source': self.REWINDING, 'dest': self.PLAYING}, # new # 2. A video can only be paused when it is playing. {'trigger': 'pause', 'source': self.PLAYING, 'dest': self.PAUSED}, {'trigger': 'pause', 'source': self.REWINDING, 'dest': self.PAUSED}, # new # 3. A video can only be stopped when it is playing or paused. {'trigger': 'stop', 'source': self.PLAYING, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.PAUSED, 'dest': self.STOPPED}, {'trigger': 'stop', 'source': self.REWINDING, 'dest': self.STOPPED}, # new # 4. A video can only be rewinded when it is playing or paused. {'trigger': 'rewind', 'source': self.PLAYING, 'dest': self.REWINDING}, #new {'trigger': 'rewind', 'source': self.PAUSED, 'dest': self.REWINDING}, # new ] # Create the state machine self.machine = Machine{ model = self, transitions = transitions, initial = self.STOPPED } def play(self): pass def pause(self): pass def stop(self): pass def rewind(self): pass
このように、ステート マシンが複雑な実装を簡素化し、間違ったコードを書かないようにする方法を理解できます。ステート マシンの機能を学んだので、次に、ステート マシンを使用する理由とタイミングを理解することが重要です。
ステート マシンは、個別の状態を持つアプリケーションで利用できます。各ステージは、プロセス フローを終了するだけでなく、1 つ以上の後続の状態につながる可能性があります。ステート マシンは、ユーザー入力またはインステート計算を使用して、次に入るステートを選択します。
多くのアプリケーションでは、「初期化」段階が必要であり、その後にさまざまなアクションを可能にするデフォルト状態が続きます。以前と現在の入力、および状態はすべて、実行されるアクションに影響を与える可能性があります。その後、システムが「シャットダウン」されたときに、クリーンアップ措置を実行できます。
ステート マシンは、非常に複雑なジョブをより小さな独立した単位に分解できれば、これらの単位をより抽象的に概念化し、管理するのに役立ちます。状態が別の状態に遷移できるタイミングと、遷移が発生したときに何が起こるかを記述するだけで済みます。セットアップ後に遷移がどのように発生するかを気にする必要はありません。その後は、どのようにではなく、いつ、何を考えればよいのです。
さらに、ステート マシンを使用すると、非常に予測可能な方法でステート プロセス全体を確認できます。遷移が設定されると、管理ミスや誤った状態遷移について心配する必要がなくなります。状態マシンが適切に構成されている場合にのみ、不適切な遷移が発生する可能性があります。ステート マシンのすべての状態と遷移を包括的に把握できます。
ステート マシンを使用しないと、考えられるさまざまな状態でシステムを視覚化できないか、コンポーネントを故意または無意識に緊密に結合しているか、状態遷移をシミュレートするために多くの if-else 条件を記述しています。すべての条件と分岐の可能性を検証するためにすべてのテスト ケースを作成する必要があるため、単体テストと統合テストが複雑になります。
ステート マシンは、意思決定アルゴリズムを開発する能力に加えて、アプリケーション計画の機能的な形態です。アプリケーションが複雑になるにつれて、効果的な設計の必要性が高まります。
状態図とフローチャートは有用であり、設計プロセス全体で必要になる場合があります。ステート マシンは、アプリケーションの計画だけでなく、作成も簡単です。
以下は、現代のコンピューティングにおけるステート マシンの主な利点の一部です。
ステート マシンのすべてが良いわけではなく、欠点や課題につながることもあります。ステート マシンに関する一般的な問題の一部を次に示します。
ステート マシンを使用する場合、システムには次の 2 つの論理コンポーネントが理想的です。
ステート マシンは、状態遷移を駆動するインフラストラクチャと考えることができます。状態遷移を検証し、遷移前、遷移中、遷移後に構成されたアクションを実行します。ただし、これらのアクションでどのようなビジネス ロジックが実行されるかを認識しないようにする必要があります。
したがって、一般的には、正しい抽象化を使用して、ステート マシンをコア ビジネス ロジックから分離することをお勧めします。そうしないと、コードの管理が困難になります。
ステート マシン ロジックを慎重に使用する必要があるその他の実際のシナリオを次に示します。
以下は、私たちの日常生活におけるステート マシンの概念から恩恵を受ける実用的なアプリケーションの一部です。
たとえば、オンラインの e コマース サイトから商品を購入すると、注文、梱包、発送、キャンセル、配送、支払い、払い戻しなど、さまざまな段階を経ます。移行は、物が倉庫または物流センターを通過するときに自動的に発生し、ユーザーがキャンセルしたり払い戻しを希望したりするときなど、さまざまな段階でスキャンされます。
ステート マシンの概念は、プログラミングに非常に役立ちます。より複雑なユース ケース アプリの開発プロセスを合理化するだけでなく、必要な開発作業を削減します。現代の出来事をよりシンプルかつエレガントに把握することができ、正しく適用すると奇跡が起こる可能性があります.