スパゲッティコヌドに察する自動機


「西掋スパゲッティが倧奜き、スパゲッティコヌドが嫌い」

「スパゲッティコヌド」は、認知的芳点ず矎的芳点の䞡方から蒞し暑いカオスである゜フトりェアを蚘述するための理想的な衚珟です。 この蚘事では、スパゲッティコヌドを砎壊するための3点蚈画に぀いお説明したす。


私たちは皆、誰かのコヌドを読むのがどれほど難しいか知っおいたす。 これは、タスク自䜓が耇雑であるか、コヌドの構造が「創造的」すぎるためです。 倚くの堎合、これら2぀の問題は密接に関連しおいたす。

課題は耇雑なタスクであり、通垞、革新的な発芋以倖はそれらを単玔化できたせん。 ただし、゜フトりェア構造自䜓が䞍必芁な耇雑さを远加するこずがあり、この問題は解決する䟡倀がありたす。

スパゲッティコヌドのさは、耇雑な条件付きロゞックにありたす。 そしお、倚くのトリッキヌなif-then-elseコンストラクトがないず人生を想像するのは難しいかもしれたせんが、この蚘事ではより良い解決策を瀺したす。


スパゲッティコヌドを䜿甚しお状況を説明するには、たずこれを有効にする必芁がありたす。


クリスピヌパスタ

これで


アルデンテ

料理を始めたしょう。

暗黙の状態


パスタを䜜るには、必ずお湯が必芁です。 ただし、スパゲッティコヌドを含むこのような䞀芋単​​玔な芁玠でさえ、非垞に混乱する可胜性がありたす。

以䞋に簡単な䟋を瀺したす。

(temp < 32) 

このチェックは本圓に䜕をしたすか 明らかに、数倀の行を2぀の郚分に分割しおいたすが、これらの郚分はどういう意味ですか あなたは論理的な仮定を立おるこずができるず思いたすが、問題はコヌドが実際にこれを明瀺的に報告しないこずです。

圌女が氎が固たっおいるかどうかを圌女が確認しおいるこずを本圓に確認したら[おおよそ。 レヌン華氏スケヌルによるず、氎は+32床で凍結したす] 、論理的にはfalseの戻り倀を意味したすか

 if (temp < 32) { // SOLID water } else { // not SOLID water. is (LIQUID | GAS) } 

チェックでは数倀を2぀のグルヌプに分けたしたが、実際には3぀の論理状態がありたす-固䜓、液䜓、気䜓固䜓、液䜓、ガスです

぀たり、次の数倀行


次のように条件チェックで区切られおいたす。

 if (temp < 32) { 


 } else { 


 } 

スパゲッティコヌドの性質を理解するために非垞に重芁であるため、䜕が起こったかに泚目しおください。 ブヌルチェックは、数倀空間を2぀の郚分に分割したしたが、システムをSOLID、LIQUID、GASからの実際の論理構造ずしお分類したせんでした。 代わりに、チェックはスペヌスをSOLID、その他すべおに分割したした。

同様のチェックを次に瀺したす。

 if (temp > 212) { // GAS water } else { // not GAS water. is (SOLID | LIQUID) } 

芖芚的には、次のようになりたす。

 if (temp > 212) { 


 } else { 


 } 

以䞋に泚意しおください。

  1. 可胜な状態の完党なセットはどこにも宣蚀されおいたせん
  2. 条件構造のどこにも、怜蚌可胜な論理状態たたは宣蚀された状態のグルヌプがない
  3. 䞀郚の状態は、条件付きロゞックず分岐の構造によっお間接的にグルヌプ化されたす

このようなコヌドは脆匱ですが、非垞に䞀般的であり、サポヌトに問題を匕き起こすほど倧きくはありたせん。 状況を悪化させたしょう。


ずにかくあなたのコヌドが奜きではなかった

䞊蚘のコヌドは、物質の3぀の状態-固䜓、液䜓、ガスの存圚を意味したす。 しかし、科孊デヌタによるず、実際にはプラズマPLASMAが含たれる4぀の芳枬可胜な状態がありたす実際、他にも倚くありたすが、これで十分です。 誰もプラズマからペヌストを準備しおいたせんが、このコヌドがGithubで公開され、高゚ネルギヌ物理孊を研究しおいる倧孊院生によっお分岐された堎合、この状態も維持する必芁がありたす。

ただし、プラズマを远加する堎合、䞊蚘のコヌドは単玔に次のこずを行いたす。

 if (temp < 32) { // SOLID water } else { // not SOLID water. is (LIQUID | GAS) + (PLASMA?) // how did PLASMA get in here?? } if (temp > 212) { // GAS water + (PLASMA) // again with the PLASMA!! } else { // not GAS water. is (SOLID | LIQUID) } 

叀いコヌドは、倚くのプラズマ状態に远加されるず、elseブランチで砎損する可胜性がありたす。 残念ながら、コヌド構造には、新しい状態の存圚を報告したり、倉曎に圱響を䞎えたりするものは䜕もありたせん。 さらに、バグは目立たない可胜性が高いです。぀たり、バグを芋぀けるこずが最も困難です。 スパゲッティの䞭の虫にはノヌずだけ蚀っおください。

芁するに、問題はこれです。ブヌルチェックは状態を間接的に決定するために䜿甚されたす。 倚くの堎合、論理状態は宣蚀されおおらず、コヌドに衚瀺されたせん。 䞊で芋たように、システムが新しい論理状態を远加するず、既存のコヌドが壊れる堎合がありたす。 これを回避するには、 開発者は個々の条件チェックずブランチを再怜査しお、コヌドパスがすべおの論理状態に察しお有効であるこずを確認する必芁がありたす。 これは、倧きなコヌドフラグメントがより耇雑になるに぀れお劣化する䞻な理由です。

条件付きデヌタチェックを完党に削陀する方法はありたせんが、それらを最小化する手法はコヌドの耇雑さを軜枛したす。

ここで、氎量の非垞に単玔なモデルを䜜成するクラスの兞型的なオブゞェクト指向の実装を芋おみたしょう。 クラスは氎の状態の倉化を管理したす。 この問題の叀兞的な解決策の問題を研究した埌、 Frameず呌ばれる新しい衚蚘法に぀いお議論し、発芋した困難にどのように察凊できるかを瀺したす。

たず氎を沞隰させお......


科孊は、枩床が倉化したずきに物質ができるすべおの可胜な遷移に名前を付けたした。


このクラスは非垞に単玔です特に有甚ではありたせん。 状態間の遷移の課題に答え、目的のタヌゲット状態に適するたで枩床を倉曎したす。

泚私はこの擬䌌コヌドを曞きたした。あなた自身の危険ずリスクでのみあなたの仕事でそれを䜿甚しおください。

 class WaterSample { temp:int Water(temp:int) { this.temp = temp } // gas -> solid func depose() { // If not in GAS state, throw an error if (temp < WATER_GAS_TEMP) throw new IllegalStateError() // do depose while (temp > WATER_SOLID_TEMP) decreaseTemp(1) } // gas -> liquid func condense() { // If not in GAS state, throw an error if (temp < WATER_GAS_TEMP) throw new IllegalStateError() // do condense while (temp > WATER_GAS_TEMP) decreaseTemp(1) } // liquid -> gas func vaporize() { // If not in LIQUID state, throw an error if (!(temp < WATER_GAS_TEMP && temp > WATER_SOLID_TEMP)) throw new IllegalStateError() // do vaporize while (temp < WATER_GAS_TEMP) increaseTemp(1) } // liquid -> solid func freeze() { // If not in LIQUID state, throw an error if (!(temp < WATER_GAS_TEMP && temp > WATER_SOLID_TEMP)) throw new IllegalStateError() // do freeze while (temp > WATER_SOLID_TEMP) decreaseTemp(1) } // solid -> liquid func melt() { // If not in SOLID state, throw an error if (temp > WATER_SOLID_TEMP) throw new IllegalStateError() // do melt while (temp < WATER_SOLID_TEMP) increaseTemp(1) } // solid -> gas func sublimate() { // If not in SOLID state, throw an error if (temp > WATER_SOLID_TEMP) throw new IllegalStateError() // do sublimate while (temp < WATER_GAS_TEMP) increaseTemp(1) } func getState():string { if (temp < WATER_SOLID_TEMP) return "SOLID" if (temp > WATER_GAS_TEMP) return "GAS" return "LIQUID" } } 

最初の䟋ず比范しお、このコヌドには特定の改善点がありたす。 たず、ハヌドコヌドされた「マゞック」番号32、212は、状態枩床境界の定数WATER_SOLID_TEMP、WATER_GAS_TEMPに眮き換えられたす。 この倉曎により、間接的ではありたすが、状態がより明確になり始めたす。

たた、このコヌドは、操䜜に䞍適切な状態にあるメ゜ッドの呌び出しを制限する「防埡的プログラミング」チェックも導入しおいたす。 たずえば、氎が液䜓でない堎合、氎は凍結できたせん。これは自然の法に違反したす。 ただし、りォッチドッグ条件を远加するず、コヌドの目的の理解が耇雑になりたす。 䟋

 // liquid -> solid if (!(temp < WATER_GAS_TEMP && temp > WATER_SOLID_TEMP)) throw new IllegalStateError() 

この条件付きチェックは次のこずを行いたす。

  1. temp GAS限界枩床より䜎いかどうかを確認したす
  2. tempが固䜓限界枩床を超えおいるかどうかを確認したす
  3. これらのチェックのいずれかが真でない堎合、゚ラヌを返したす

このロゞックは玛らわしいです。 第䞀に、液䜓状態にあるかどうかは、物質が固䜓たたは気䜓ではないものによっお決定されたす。

 (temp < WATER_GAS_TEMP && temp > WATER_SOLID_TEMP) // is liquid? 

次に、コヌドは氎が液䜓かどうかを確認しお、゚ラヌを返す必芁があるかどうかを調べたす。

 !(temp < WATER_GAS_TEMP && temp > WATER_SOLID_TEMP) // Seriously? 

この状態の二重吊定を初めお理解するのは簡単ではありたせん。 匏の耇雑さをわずかに枛らす単玔化を次に瀺したす。

 bool isLiquidWater = (temp < WATER_GAS_TEMP && temp > WATER_SOLID_TEMP) if (!isLiquidWater) throw new IllegalStateError() 

isLiquidWater状態は明瀺的であるため、このコヌドは理解しやすいです。

珟圚、問題を解決する最善の方法ずしお、 明瀺的な状態を修正する手法を怜蚎しおいたす。 このアプロヌチでは、システムの論理状態が゜フトりェアの物理構造になり、コヌドが改善されお理解が容易になりたす。

フレヌムマシン衚蚘


フレヌムマシン衚蚘法FMNは、さたざたなタむプのマシンを定矩および実装するためのカテゎリカル、方法論、およびシンプルなアプロヌチを定矩するドメむン固有蚀語DSLです。 簡単にするために、フレヌムオヌトマトンを単に「マシン」ず呌びたす。この衚蚘法は、さたざたなタむプステヌトマシン、ストアオヌトマトン、およびオヌトマトンの最䞊の進化—チュヌリングマシンの理論的基準を定矩できるためです。 さたざたなタむプのマシンずそのアプリケヌションに぀いお知るには、 Wikipediaのペヌゞを調べるこずをお勧めしたす。

オヌトマトン理論は興味深いかもしれたせんが非垞に疑わしい声明、この蚘事では、システムの構築ずコヌドの蚘述のためのこれらの匷力な抂念の実甚化に焊点を圓おたす。

この問題を解決するために、Frameは3぀の統合レベルで機胜する暙準化された衚蚘法を導入したす。

  1. ゚レガントで簡朔な構文でフレヌムコントロヌラヌを定矩するためのテキストDSL
  2. Frameが「コントロヌラヌ」ず呌ぶマシンの圢でオブゞェクト指向クラスを実装するための䞀連の参照コヌディングパタヌン
  3. FMNを䜿甚しおグラフィカルに衚珟するのが難しい耇雑な操䜜を衚珟する芖芚衚蚘 - フレヌム芖芚衚蚘FVN

この蚘事では、最初の2぀のポむント、FMNず参照パタヌンを怜蚎し、FVNの議論は今埌の蚘事のために残したす。

フレヌムは、いく぀かの重芁な偎面を持぀衚蚘法です。

  1. FMNには、オヌトマトンの抂念に関連する第1レベルのオブゞェクトがありたすが、オブゞェクト指向蚀語では䜿甚できたせん。
  2. FMN仕様では、FMN衚蚘の実装方法を瀺す暙準コヌドを擬䌌コヌドで定矩しおいたす。
  3. FMNはたもなくオブゞェクト指向蚀語でコンパむル䜜業䞭できるようになりたす

泚リファレンス実装は、FMN衚蚘の絶察等䟡性ず、オブゞェクト指向蚀語で実装するための簡単な方法を瀺すために䜿甚されたす。 任意の方法を遞択できたす。

ここで、Frameの2぀の最も重芁な第1レベルのオブゞェクト、 Frame EventsずFrame Controllersを玹介したす。

フレヌムむベント


FrameEventsは、FMN衚蚘の単玔さの䞍可欠な郚分です。 FrameEventは、少なくずも次のメンバヌ倉数を持぀構造䜓たたはクラスずしお実装されたす。


FrameEventクラスの擬䌌コヌドは次のずおりです。

 class FrameEvent { var _msg:String var _params:Object var _return:Object FrameEvent(msg:String, params:Object = null) { _msg = msg _params = params } } 

フレヌム衚蚘では、FrameEventオブゞェクトを識別する@蚘号を䜿甚したす。 必芁な各FrameEvent属性には、それにアクセスするための特別なトヌクンがありたす。

 @|message| :  -    _msg @[param1] :  []      @^ :              _return 

倚くの堎合、FrameEventの動䜜を指定する必芁はありたせん。 ほずんどのコンテキストは䞀床に1぀のFrameEventのみで機胜するため、属性セレクタヌのみを䜿甚するように衚蚘を確実に簡玠化できたす。 したがっお、アクセスを簡玠化できたす。

 |buttonClick| // Select for a "buttonClick" event _msg [firstName] = "Mark" // Set firstName _params property to "Mark" ^ = "YES" // Set the _return object to "YES" 

このような衚蚘は、最初は奇劙に思えるかもしれたせんが、むベントのこのような単玔な構文がFMNコヌドの理解を倧幅に簡玠化する方法がすぐにわかりたす。

フレヌムコントロヌラヌ


フレヌムコントロヌラは、オブゞェクト指向クラスであり、フレヌムマシンを実装するために明確に定矩された方法で順序付けられおいたす。 コントロヌラタむプは、 プレフィックスで識別されたす。

 #MyController 

これは、次のオブゞェクト指向の擬䌌コヌドず同等です。

 class MyController {} 

明らかに、このクラスは特に有甚ではありたせん。 圌が䜕かできるように、コントロヌラヌはむベントに応答するために少なくずも1぀の状態を必芁ずしたす。

コントロヌラは、さたざたなタむプのブロックを含むように構造化されおいたす。ブロックは、ブロックタむプの名前を囲むダッシュで識別されたす。

 #MyController<br> -block 1- -block 2- -block 3- 

コントロヌラヌには、各ブロックのむンスタンスを1぀だけ含めるこずができ、ブロックタむプには特定のタむプのサブコンポヌネントのみを含めるこずができたす。 この蚘事では、状態のみを含むこずができる-machine-ブロックのみを調べたす。 状態は、 $プレフィックストヌクンによっお識別されたす。

ここでは、1぀の状態のみを持぀マシンを含むコントロヌラヌのFMNが衚瀺されたす。

 #MyController // controller declaration -machine- // machine block $S1 // state declaration 

䞊蚘のFMNコヌドの実装は次のずおりです。

 class MyController { // -machine- var _state(e:FrameEvent) = S1 // initialize state variable // to $S1 func S1(e:FrameEvent) { // state $S1 does nothing } } 

マシンブロックの実装は、次の芁玠で構成されたす。

  1. _state倉数。珟圚の状態の関数を参照したす。 コントロヌラの最初の状態関数で初期化されたす。
  2. 1぀以䞊の状態メ゜ッド

フレヌム状態メ゜ッドは、次のシグネチャを持぀関数ずしお定矩されたす。

 func MyState(e:FrameEvent); 

マシンブロックの実装のこれらの基本を定矩した埌、FrameEventオブゞェクトがマシンずどの皋床盞互䜜甚するかを確認できたす。

むンタヌフェヌスナニット


マシンの動䜜を制埡するFrameEventsの盞互䜜甚は、フレヌム衚蚘法のシンプルさずパワヌの本質です。 ただし、FrameEventがどこから来たのかずいう質問にはただ答えおいたせん。どのようにしおコントロヌラヌにアクセスしお制埡するのでしょうか。 1぀のオプション倖郚クラむアント自䜓がFrameEventsを䜜成および初期化し、_stateメンバヌ倉数が指すメ゜ッドを盎接呌び出すこずができたす。

 myController._state(new FrameEvent("buttonClick")) 

より良い代替方法は、_stateメンバヌ倉数ぞの盎接呌び出しをラップする共通むンタヌフェヌスを䜜成するこずです。

 myController.sendEvent(new FrameEvent("buttonClick")) 

ただし、オブゞェクト指向゜フトりェアを䜜成する通垞の方法に察応する最も手間のかからない方法は、クラむアントに代わっおむベントを内郚マシンに送信する䞀般的な方法を䜜成するこずです。

 class MyController { func buttonClick() { FrameEvent e = new FrameEvent("buttonClick") _state(e) return e._return } } 

Frameは、呌び出しをFrameEventsの共通むンタヌフェむスに倉換するメ゜ッドを含むむンタヌフェむスブロックの構文を定矩したす。

 #MyController -interface- buttonClick ... 

interfaceブロックには他にも倚くの機胜がありたすが、この䟋では、この仕組みの抂芁を説明したす。 シリヌズの以䞋の蚘事でさらに説明したす。

それでは、フレヌムオヌトマトンの操䜜を匕き続き孊習したしょう。

むベントハンドラヌ


車を定矩する方法を瀺したしたが、ただ䜕かを行うための衚蚘法がありたせん 。 むベントを凊理するには、1凊理する必芁のあるむベントを遞択できるこず、2実行䞭の動䜜にそれを添付するこずが必芁です。

むベントを凊理するためのむンフラストラクチャを提䟛するシンプルなフレヌムコントロヌラヌを次に瀺したす。

 #MyController // controller declaration -machine- // machine block $S1 // state declaration |e1| ^ // e1 event handler and return 

䞊蚘のように、 _msgむベントの_msg属性にアクセスする_msgに、FMN衚蚘は垂盎線の角かっこを䜿甚したす。

 |messageName| 

FMNは、returnステヌトメントを衚す指数トヌクンも䜿甚したす。 䞊蚘のコントロヌラヌは次のように実装されたす。

 class MyController { // #MyController // -machine- var _state(e:FrameEvent) = S1 func S1(e:FrameEvent) { // $S1 if (e._msg == "e1") { // |e1| return // ^ } } } 

ここでは、FMN衚蚘が理解しやすく、コヌディングしやすい実装パタヌンにどれほど明確に察応しおいるかを確認したす。

むベント、コントロヌラヌ、マシン、ステヌト、むベントハンドラヌのこれらの基本的な偎面を蚭定したら、それらの助けを借りお実際の問題の解決に進むこずができたす。

シングルフォヌカスマシン


䞊蚘では、かなり圹に立たないステヌトレスコントロヌラヌを芋たした。

 #MyController 

有甚性のフヌドチェヌンの1぀䞊の段階は、単䞀の状態を持぀クラスです。これは、圹に立たないものの、単に退屈です。 しかし、少なくずも圌は少なくずも䜕かをしおいる 。

最初に、暗黙の状態が1぀だけのクラスがどのように実装されるかを芋おみたしょう。

 class Mono { String status() { return "OFF" } } 

ここでは状態は宣蚀されおおらず、暗瀺されおいたせんが、コヌドが䜕かを実行した堎合、システムは「皌働䞭」状態にあるず仮定したしょう。

たた、重芁なアむデアを玹介したす。むンタヌフェむス呌び出しは、オブゞェクトにむベントを送信するこずに䌌おいるず芋なされたす。 したがっお、䞊蚘のコヌドは、むベントを送信する方法ず芋なすこずができたす| status | 垞に$ Working状態のMonoクラス。

この状況は、むベントバむンディングテヌブルを䜿甚しお芖芚化できたす。


次に、同じ機胜を瀺し、同じバむンディングテヌブルに䞀臎するFMNを芋おみたしょう。

 #Mono -machine- $Working |status| ^("OFF") 

実装は次のようになりたす。

 class Mono { // #Mono // -machine- var _state(e:FrameEvent) = Working // initialize start state func Working(e:FrameEvent) { // $Working if (e._msg == "status") { // |status| e._return = "OFF" return // ^("OFF") } } } 

returnステヌトメントに新しい衚蚘法も導入されおいるこずがわかりたす。これは、匏を評䟡し、結果をむンタヌフェむスに返すこずを意味したす。

 ^(return_expr) 

この挔算子は同等です

 @^ = return_expr 

たたは単に

 ^ = return_expr 

これらの挔算子はすべお機胜的に同等であり、どれでも䜿甚できたすが、 ^(return_expr)は最も衚珟力がありたす。

ストヌブの電源を入れたす


これたでに、状態が0のコントロヌラヌず状態が1のコントロヌラヌを芋おきたした。 それらはただあたり有甚ではありたせんが、私たちはすでに興味深い䜕かの危機にonしおいたす。

パスタを調理するには、たずストヌブの電源を入れる必芁がありたす。 以䞋は、単䞀のブヌル倉数を持぀単玔なSwitchクラスです。

 class Switch { boolean _isOn; func status() { if (_isOn) { return "ON"; } else { return "OFF"; } } } 

これは䞀芋しお明らかではありたせんが、䞊蚘のコヌドは次のむベントバむンディングのテヌブルを実装しおいたす。


比范のために、同じ動䜜のFMNを次に瀺したす。

 #Switch1 -machine- $Off |status| ^("OFF") $On |status| ^("ON") 

これで、Frame衚蚘がコヌドの目的にどのように䞀臎するかがわかりたす。぀たり、コントロヌラヌの䜍眮に基づいお、むベントメ゜ッド呌び出しを動䜜にアタッチしたす。 さらに、実装構造はバむンディングテヌブルにも察応しおいたす。

 class Switch1 { // #Switch1 // -machine- var _state(e:FrameEvent) = Off func Off(e:FrameEvent) { // $Off if (e._msg == "status") { // |status| e._return = "OFF" return // ^("OFF") } } func On(e:FrameEvent) { // $On if (e._msg == "status") { // |status| e._return = "ON" return // ^("ON") } } } 

この衚により、さたざたな状態のコントロヌラヌの目的をすばやく理解できたす。 フレヌム衚蚘構造ず実装パタヌンの䞡方に同様の利点がありたす。

ただし、スむッチには顕著な機胜䞊の問題がありたす。 状態$ Offで初期化されたすが、状態$ Onに切り替えるこずはできたせん これを行うには、 状態倉曎挔算子を入力する必芁がありたす。

状態を倉曎


状態倉曎ステヌトメントは次のずおりです。

 ->> $NewState 

これで、この挔算子を䜿甚しお$ Offず$ Onを切り替えるこずができたす。

 #Switch2 -machine- $Off |toggle| ->> $On ^ |status| ^("OFF") $On |toggle| ->> $Off ^ |status| ^("ON") 

察応するむベントバむンディングテヌブルは次のずおりです。


新しいむベント|トグル| 2぀の状態を単玔に繰り返す倉曎がトリガヌされたす。 状態倉曎操䜜はどのように実装できたすか

どこも簡単です。 Switch2の実装は次のずおりです。

 class Switch2 { // #Switch2 // -machine- var _state(e:FrameEvent) = Off func Off(e:FrameEvent) { if (e._msg == "toggle") { // |toggle| _state = On // ->> $On return // ^ } if (e._msg == "status") { // |status| e._return = "OFF" return // ^("OFF") } } func On(e:FrameEvent) { if (e._msg == "toggle") { // |toggle| _state = Off // ->> $Off return // ^("OFF") } if (e._msg == "status") { // |status| e._return = "ON" return // ^("ON") } } } 

Switch2で最埌の改善を行うこずで、状態を切り替えるこずができるだけでなく、状態を明瀺的に蚭定するこずもできたす。

 #Switch3 -machine- $Off |turnOn| ->> $On ^ |toggle| ->> $On ^ |status| ^("OFF") $On |turnOff| ->> $Off ^ |toggle| ->> $Off ^ |status| ^("ON") 

| toggle |むベントずは異なり、if | turnOn | Switch3がすでにオンになっおいるずき、たたは| offOff |既にオフになっおいるずきに送信され、メッセヌゞは無芖され、䜕も起こりたせん。 このわずかな改善により、クラむアントはスむッチの状態を明瀺的に瀺すこずができたす。

 class Switch3 { // #Switch3 // -machine- var _state(e:FrameEvent) = Off /********************************** $Off |turnOn| ->> $On ^ |toggle| ->> $On ^ |status| ^("OFF") ***********************************/ func Off(e:FrameEvent) { if (e._msg == "turnOn") { // |turnOn| _state = On // ->> $On return // ^ } if (e._msg == "toggle") { // |toggle| _state = On // ->> $On return // ^ } if (e._msg == "status") { // |status| e._return = "OFF" return // ^("OFF") } } /********************************** $On |turnOff| ->> $Off ^ |toggle| ->> $Off ^ |status| ^("ON") ***********************************/ func On(e:FrameEvent) { if (e._msg == "turnOff") { // |turnOff| _state = Off // ->> $Off return // ^ } if (e._msg == "toggle") { // |toggle| _state = Off // ->> $Off return // ^ } if (e._msg == "status") { // |status| e._return = "ON" return // ^("ON") } } } 

スむッチの進化の最埌のステップは、FMNコントロヌラヌの目的を理解するこずがいかに簡単かを瀺しおいたす。関連するコヌドは、フレヌムメカニズムを䜿甚しお実装するこずがいかに簡単かを瀺しおいたす。

Switchマシンを䜜成したら、火を぀けお調理を開始できたす

鳎り具合


オヌトマトンの重芁な偎面は、埮劙ではありたすが、マシンの珟圚の状態は、状況たずえば、電源を入れるたたはデヌタたたは環境の䜕らかの分析の結果であるずいうこずです。マシンが目的の状態に切り替わるず、それが暗瀺されたす。車の知識がなくおも状況は倉わらないずいうこずです。

ただし、この仮定は垞に正しいずは限りたせん。状況によっおは、珟圚の論理状態を刀断するためにデヌタの怜蚌たたは「センシング」が必芁です。

  1. 初期埩元状態 -マシンが䞀定の状態から埩元されたずき
  2. 倖郚状態 -マシンの䜜成、埩元、たたは操䜜時に環境に存圚する「実際の状況」を定矩したす
  3. 揮発性内郚状態 -実行䞭のマシンによっお管理される内郚デヌタの䞀郚がマシンの制埡倖で倉曎できる堎合

これらのすべおの堎合、状況を刀断し、それに応じおマシンの状態を蚭定するには、デヌタ、環境、たたはその䞡方を「プロヌブ」する必芁がありたす。理想的には、このブヌル論理は、正しい論理状態を定矩する単䞀の関数で実装できたす。このパタヌンをサポヌトするために、フレヌム衚蚘には、ナニバヌスをプロヌブしお珟圚の状況を刀断する特別なタむプの機胜がありたす。そのような関数は、状態ぞのリンクを返すメ゜ッドの名前の前の$プレフィックスで瀺されたす

 $probeForState() 

私たちの状況では、このメ゜ッドは次のように実装できたす。

 func probeForState():FrameState { if (temp < 32) return Solid if (temp < 212) return Liquid return Gas } 

ご芧のずおり、このメ゜ッドは正しい論理状態に察応する状態関数ぞの参照を返すだけです。この怜知機胜を䜿甚しお、正しい状態に入るこずができたす。

 ->> $probeForState() 

実装メカニズムは次のようになりたす。

 _state = probeForState() 

状態怜知方法は、所定の方法で状態を管理するためのフレヌム衚蚘の䟋です。次に、FrameEventsを管理するための重芁な衚蚘法に぀いおも孊習したす。

行動の継承ずディスパッチャヌ


動䜜の継承ずディスパッチャヌは匷力なプログラミングパラダむムであり、この蚘事のフレヌム衚蚘に関する最埌のトピックです。

Frameは、デヌタたたは他の属性の継承ではなく、動䜜の継承を䜿甚したす。この状態では、初期状態がむベントを凊理しない堎合たたは、次の蚘事で説明するように、単にむベントを枡したい堎合、FrameEventsは他の状態に送信されたす。この䞀連のむベントは、任意の深さに到達できたす。

このために、マシンはメ゜ッドチェヌンず呌ばれる手法を䜿甚しお実装できたす。ある状態から別の状態にむベントを送信するためのFMN衚蚘は、ディスパッチャ =>です。

 $S1 => $S2 

このFMNステヌトメントは、次のように実装できたす。

 func S1(e:FrameEvent) { S2(e) // $S1 => $S2 } 

状態メ゜ッドを連鎖させるのがどれほど簡単かがわかりたした。この手法をかなり難しい状況に適甚しおみたしょう。

 #Movement -machine- $Walking => $Moving |getSpeed| ^(3) |isStanding| ^(true) $Running => $Moving |getSpeed| ^(6) |isStanding| ^(true) $Crawling => $Moving |getSpeed| ^(.5) |isStanding| ^(false) $AtAttention => $Motionless |isStanding| ^(true) $LyingDown => $Motionless |isStanding| ^(false) $Moving |isMoving| ^(true) $Motionless |getSpeed| ^(0) |isMoving| ^(false) 

䞊蚘のコヌドでは、2぀の基本的な状態-$ Movingず$ Motionless-があり、他の5぀の状態はそれらから重芁な機胜を継承しおいたす。むベントバむンディングは、バむンディングの䞀般的な倖芳を明確に瀺しおいたす。


私たちが孊んだテクニックのおかげで、実装は非垞に簡単になりたす。

 class Movement { // #Movement // -machine- /********************************** $Walking => $Moving |getSpeed| ^(3) |isStanding| ^(true) ***********************************/ func Walking(e:FrameEvent) { if (e._msg == "getSpeed") { e._return = 3 return } if (e._msg == "isStanding") { e._return = true return } Moving(e) // $Walking => $Moving } /********************************** $Running => $Moving |getSpeed| ^(6) |isStanding| ^(true) ***********************************/ func Running(e:FrameEvent) { if (e._msg == "getSpeed") { e._return = 6 return } if (e._msg == "isStanding") { e._return = true return } Moving(e) // $Running => $Moving } /********************************** $Crawling => $Moving |getSpeed| ^(.5) |isStanding| ^(false) ***********************************/ func Crawling(e:FrameEvent) { if (e._msg == "getSpeed") { e._return = .5 return } if (e._msg == "isStanding") { e._return = false return } Moving(e) // $Crawling => $Moving } /********************************** $AtAttention => $Motionless |isStanding| ^(true) ***********************************/ func AtAttention(e:FrameEvent) { if (e._msg == "isStanding") { e._return = true return } Motionless(e) // $AtAttention => $Motionless } /********************************** $LyingDown => $Motionless |isStanding| ^(false) ***********************************/ func LyingDown(e:FrameEvent) { if (e._msg == "isStanding") { e._return = false return } Motionless(e) // $AtAttention => $Motionless } /********************************** $Moving |isMoving| ^(true) ***********************************/ func Moving(e:FrameEvent) { if (e._msg == "isMoving") { e._return = true return } } /********************************** $Motionless |getSpeed| ^(0) |isMoving| ^(false) ***********************************/ func Motionless(e:FrameEvent) { if (e._msg == "getSpeed") { e._return = 0 return } if (e._msg == "isMoving") { e._return = false return } } } 

絊氎機


これで、FMNに関する基本的な知識が埗られ、WaterSampleクラスを状態を䜿甚しお、よりむンテリゞェントな方法で再実装する方法を理解できるようになりたした。たた、倧孊院生の物理孊者にずっお有甚なものにし、新しい$プラズマ状態を远加したす。


FMNの完党な実装は次のようになりたす。

 #WaterSample -machine- $Begin |create| // set temp to the event param value setTemp(@[temp]) // probe for temp state and change to it ->> $probeForState() ^ $Solid => $Default |melt| doMelt() ->> $Liquid ^ |sublimate| doSublimate() ->> $Gas ^ |getState| ^("SOLID") $Liquid => $Default |freeze| doFreeze() ->> $Solid ^ |vaporize| doVaporize() ->> $Gas ^ |getState| ^("LIQUID") $Gas => $Default |condense| doCondense() ->> $Liquid ^ |depose| doDepose() ->> $Solid ^ |ionize| doIonize() ->> $Plasma ^ |getState| ^("GAS") $Plasma => $Default |recombine| doRecombine() ->> $Gas ^ |getState| ^("PLASMA") $Default |melt| throw new InvalidStateError() |sublimate| throw new InvalidStateError() |freeze| throw new InvalidStateError() |vaporize| throw new InvalidStateError() |condense| throw InvalidStateError() |depose| throw InvalidStateError() |ionize| throw InvalidStateError() |recombine| throw InvalidStateError() |getState| throw InvalidStateError() 

ご芧のずおり、初期状態は$ Beginで、メッセヌゞに応答したす| create |そしお䟡倀を保持したすtemp。怜知機胜は最初に初期倀tempをチェックしお論理状態を決定し、次にマシンのこの状態ぞの移行を実行したす。

すべおの物理的状態$固䜓、$液䜓、$ガス、$プラズマは、$デフォルト状態から保護動䜜を継承したす。珟圚の状態に察しお無効なすべおのむベントは、$ Default状態に枡され、InvalidStateError゚ラヌがスロヌされたす。これは、振る舞いの継承を䜿甚しお、単玔な防埡プログラミングを実装する方法を瀺しおいたす。

そしお今、実装

 class WaterSample { // -machine- var _state(e:FrameEvent) = Begin /********************************** $Begin |create| // set temp to the event param value setTemp(@[temp]) // probe for temp state and change to it ->> $probeForState() ^ ***********************************/ func Begin(e:FrameEvent) { if (e._msg == "create") { setTemp(e["temp"]) _state = probeForState() return } } /********************************** $Solid => $Default |melt| doMelt() ->> $Liquid ^ |sublimate| doSublimate() ->> $Gas ^ |sublimate| ^("SOLID") ***********************************/ func Solid(e:FrameEvent) { if (e._msg == "melt") { doMelt() _state = Liquid return } if (e._msg == "sublimate") { doSublimate() _state = Gas return } if (e._msg == "getState") { e._return = "SOLID" return } Default(e) } /********************************** $Liquid => $Default |freeze| doFreeze() ->> $Solid ^ |vaporize| doVaporize() ->> $Gas ^ |getState| ^("LIQUID") ***********************************/ func Liquid(e:FrameEvent) { if (e._msg == "freeze") { doFreeze() _state = Solid return } if (e._msg == "vaporize") { doVaporize() _state = Gas return } if (e._msg == "getState") { e._return = "LIQUID" return } Default(e) } /********************************** $Gas => $Default |condense| doCondense() ->> $Liquid ^ |depose| doDepose() ->> $Solid ^ |ionize| doIonize() ->> $Plasma ^ |getState| ^("GAS") ***********************************/ func Gas(e:FrameEvent) { if (e._msg == "condense") { doCondense() _state = Liquid return } if (e._msg == "depose") { doDepose() _state = Solid return } if (e._msg == "ionize") { doIonize() _state = Plasma return } if (e._msg == "getState") { e._return = "GAS" return } Default(e) } /********************************** $Plasma => $Default |recombine| doRecombine() ->> $Gas ^ |getState| ^("PLASMA") ***********************************/ func Plasma(e:FrameEvent) { if (e._msg == "recombine") { doRecombine() _state = Gas return } if (e._msg == "getState") { e._return = "PLASMA" return } Default(e) } /********************************** $Default |melt| throw new InvalidStateError() |sublimate| throw new InvalidStateError() |freeze| throw new InvalidStateError() |vaporize| throw new InvalidStateError() |condense| throw InvalidStateError() |depose| throw InvalidStateError() |ionize| throw InvalidStateError() |recombine| throw InvalidStateError() |getState| throw InvalidStateError() ***********************************/ func Default(e:FrameEvent) { if (e._msg == "melt") { throw new InvalidStateError() } if (e._msg == "sublimate") { throw new InvalidStateError() } if (e._msg == "freeze") { throw new InvalidStateError() } if (e._msg == "vaporize") { throw new InvalidStateError() } if (e._msg == "condense") { throw new InvalidStateError() } if (e._msg == "depose") { throw new InvalidStateError() } if (e._msg == "ionize") { throw new InvalidStateError() } if (e._msg == "recombine") { throw new InvalidStateError() } if (e._msg == "getState") { throw new InvalidStateError() } } } 

おわりに


オヌトマトンは、゜フトりェアずハ​​ヌドりェアの開発の専門分野でのみ長い間䜿甚されおきたコンピュヌタヌサむ゚ンスの基本抂念です。 Frameの䞻な目的は、オヌトマトンを蚘述するための衚蚘法を䜜成し、コヌドたたは「メカニズム」を実装するための簡単なパタヌンを蚭定するこずです。 Frame衚蚘がプログラマヌのマシンの芋方を倉え、日垞のプログラミングタスクでそれらを実践する簡単な方法を提䟛し、そしおもちろん、コヌドのスパゲッティからそれらを保存するこずを願っおいたす。


タヌミネヌタヌがパスタを食べる鈎朚さん撮圱
今埌の蚘事では、私たちが孊んだ抂念に基づいお、FMN衚蚘法のさらに倧きな力ず衚珟力を䜜成したす。時間が経぀に぀れお、FMNを含み、゜フトりェアモデリングぞの最新のアプロヌチにおける䞍確実な動䜜の問題を解決するビゞュアルモデリングの研究に議論を拡倧したす。

Source: https://habr.com/ru/post/J446330/


All Articles