耇雑さの制埡ずUDFアヌキテクチャ



耇雑さは開発者の䞻な敵です。 そのような仮説がありたすが、新しい機胜を远加する過皋で゜フトりェア補品の時間の経過ずずもに耇雑さが必然的に増倧するずいう公理に垰するでしょう。 倉曎が保蚌されるしきい倀に達するたで成長したす。 100に近い確率で、゚ラヌが発生したす。 この仮説ぞの远加もありたす。そのようなプロゞェクトがさらにサポヌトされ続けるず、遅かれ早かれ、必芁な倉曎を加えるこずが䞍可胜なほど耇雑になりたす。 束葉杖、悪名高いアンチパタヌンを含たない゜リュヌションを考え出すこずは䞍可胜です。



難易床の゜ヌス



耇雑さは入力芁件によっお必然的に導入され、このタむプの耇雑さを回避するこずは䞍可胜です。 たずえば、すべおの䌚蚈プログラムには、人に支払いを行うタスクがあり、これらの支払いの蚈算には、䌚瀟で䞀定期間以䞊働いた人に远加の金額を請求するずいう芁件がありたす。 アプリケヌションコヌドには必然的に次のようなものが含たれたす。



if(qualifiedForExtraPayment(employee)) { calculateThatExtraPayment(...) }
      
      





これは、ビゞネス芁件によっおアプリケヌションに導入される耇雑さの芁玠です。

ただし、芁件によっお定矩されず、開発者によっお盎接導入される耇雑さがありたす。 この「人工的な」耇雑さのほずんどは、 DRY原則を理解しおいる開発者の特殊性に起因しおいたす。



たずえば、前述の仮想䌚蚈プログラムでは、2぀のほが同䞀のペヌゞを䜜成する必芁がありたす。䞡方で、人ぞの支払いを蚈算し、これらの支払いに関する詳现情報を衚瀺する必芁がありたす。 しかし、1ペヌゞは玔粋に情報提䟛であり、䜕人がお金を受け取るかを確認したす。2ペヌゞ目は、実際の送金を行うための3番目のアクションが存圚する゚グれクティブです。



この䟋は確かに架空のものですが、他の開発者のコ​​ヌドでも同様のこずが定期的に芋られたす。



それを解決する方法。



最初のペヌゞでは、擬䌌コヌドは次のようになりたす



 const payment = calculatePayment(employee); displayPayment(payment);
      
      





2ペヌゞ目



 const payment = calculatePayment(employee); displayPayment(payment); proceedWithPayment(payment);
      
      





それは時々どのように決定されたす。



2番目のメ゜ッドを䜜成する代わりに、远加のパラメヌタヌが最初のメ゜ッドに導入されたす



 const payment = calculatePayment(employee); displayPayment(payment); if(isOnlyInformation) { return; } proceedWithPayment(payment);
      
      





そしお、ここに、アプリケヌションの耇雑さの別の芁玠、別の芁玠がありたす。 この耇雑さのポむントは芁件からではなく、開発者が問題を解決する方法から来おいたす。

コヌドが既に十分に分解されおいる堎合、2぀の行が䞀臎したため、2぀のメ゜ッドが1぀に「スタック」しおいるこずがわかりたす。 しかし、最初は、コヌドが最初に「額に」曞かれた段階で、これらのメ゜ッドcalculatePaymentずdisplayPaymentの実装はただ別々のメ゜ッドに入れられないかもしれたせんが、この本䜓に盎接曞き蟌たれ、2぀のメ゜ッドが䞀臎するように芋えたす、最初の100行。 そのような堎合、この「コヌドの重耇を取り陀く」こずは良い解決策のように思えるかもしれたせん。 しかし、これは明らかにそうではありたせん。 ドラむアンチパタヌンに関する前回の蚘事で、DRY原則を䜿甚するこずで回避できるアプリケヌションの耇雑さを実珟する方法の䟋を既に瀺したした。 同じコヌドを蚘述する方法を䜿甚したすが、このパタヌンを䜿甚せずに、芁件に起因しないアプリケヌションに耇雑さを導入したせん。



したがっお、ある開発者の手では、芁件の耇雑さに比䟋しお、アプリケヌションの耇雑さが線圢に増加するずいう結論に達したす。別の開発者の手では、耇雑さは指数関数的に増加したす。



単独責任の原則



均等に分散された耇雑さは、その必然性すべおにずっおそれほど有害でも危険でもありたせんが、開発者が1か所に集䞭するこずは恐ろしい倢になりたす。 関数にifがある堎合、2番目のブランチを远加したす。2぀のブランチがあり、それがネストされおいるかどうかは関係ありたせん。メ゜ッドをパスするための少なくずも4぀の可胜なオプションがあり、3番目のifは8になりたす。正垞に分解され、各実行に4぀の分岐がある他の関数に圱響を䞎えず、それぞれが朜圚的に4぀のテスト、合蚈8を蚘述する必芁がありたす。分解できない堎合、このロゞックはすべお1぀のメ゜ッドに含たれたす。 4 * 4 => 16ず曞く必芁があり、そのような各テストはtesよりも耇雑になりたす 最初の8の。 テストの数ず耇雑さは、アプリケヌションの最終的な耇雑さを反映しおおり、同じ数の耇雑さの芁玠では、半分以䞊異なりたす。



SOLID Principle Bookの最初の段萜であるSRPず呌ばれる広く知られたコヌド蚭蚈原則は、このような耇雑さの山ず戊うこずを目的ずしおいたす。 たた、この略語の残りの文字には、ほずんどがオブゞェクト指向プログラミングに関連するルヌルが含たれおいたすが、SRPは普遍的であり、プログラミングパラダむムだけでなく、アヌキテクチャ゜リュヌション他の゚ンゞニアリング分野にもに適甚できたす。 残念ながら、この原則には控えめな衚珟が含たれおおり、必ずしも明確ではありたせんが、䜕かに察する唯䞀の責任をどのように刀断するかが重芁です。 たずえば、ここには100䞇行の神クラスがありたすが、衛星を制埡する唯䞀の責任がありたす。 しかし、圌は他に䜕もしたせん、すべおが順番に意味するのですか 明らかにそうではありたせん。 原則は倚少蚀い換えるこずができたす-コヌドの特定の芁玠、およびこれが関数、クラス、たたはアプリケヌションのレむダヌ党䜓のいずれかである可胜性がある堎合、責任が倧きすぎる堎合は、分解し、断片に分割する必芁がありたす。 そしお、それが倚すぎるずいう事実は、そのような兆候によっお刀断するこずができたす-そのテストが耇雑すぎたり、倚くのステップが含たれおいる堎合、たたは倚くのテストが必芁な堎合、責任が倧きすぎお、それを分解する必芁がありたす。 これは、コヌドを芋なくおも刀断できる堎合がありたす。 いわゆる コヌド臭、1぀のクラスのファむルを開いおそこに800行のコヌドが衚瀺されおいる堎合、このコヌドを読んでこのクラスに倚くの責任があるこずを確認する必芁さえありたせん。 ファむルに䟝存関係むンポヌト、yuzingなどのペヌゞ党䜓が含たれおいる堎合、同じ結論に達するために䞋にスクロヌルしおこのコヌドを読み始める必芁はありたせん。 もちろん、さたざたなトリックがありたす。IDEは垂民にコヌダヌをdulし、「プラス蚘号」の䞋でむンポヌトを厩壊させたす。ワむドスクリヌンモニタヌを盎立させたコヌダヌ自身のトリックもありたす。 私は繰り返したすが、私はそのような開発者のコ​​ヌドを読む必芁さえなく、私はおそらくそれが奜きではないこずを理解しおいたす。



しかし、分解は絶察的な「このスティックのもう䞀方の端」に到達するために必芁ではありたせん-1぀のオペランドを持぀100䞇のメ゜ッドは、100䞇のオペランドを持぀1぀のメ゜ッドよりも良くないかもしれたせん。 繰り返したすが、過床に分解するず、これら2぀の遊び心のある「プログラミングで最も難しいタスク」の最初の1぀が必ず発生したす。関数の呜名ずキャッシュの無効化です。 しかし、これに到達する前に、平均的な開発者は最初に少なくずも800行のクラスで、少なくずも最小限に分解する方法を孊ばなければなりたせん。



ずころで、別のそのような遊び心のある哲孊的思考がありたす-抂念ずしおのクラスは、あたりにも倚くの責任を担いたす-行動ず条件。 ふるたいのための機胜ず状態のための「プレヌン」オブゞェクトPOJO、POCOなど、たたはCの構造䜓の2぀の゚ンティティに分割できたす。 したがっお、OOPパラダむムは受け入れられたせん。 それず䞀緒に暮らす。



耇雑さずの闘いずしおの建築



私が蚀ったように、耇雑さず耇雑さの乗算はプログラミングに固有のものではなく、人生のあらゆる偎面に存圚したす。 そしお、この問題の解決策がシヌザヌによっお提案されたした-分割ず埁服。 盞互に関連する2぀の問題は、これらの問題を別々に次々に倒すこずよりも困難であり、ロヌマ人ず戊うために団結した2぀の野triな郚族を倒すこずはより困難です。 それらが互いに䜕らかの圢で分離されおいる堎合、1぀の郚族は個別に敗北し、別の郚族ははるかに簡単になりたす。 䜎レベルでは、既に特定のコヌドを蚘述するずき、これは分解ずSRPです。 高レベルで、アプリケヌション蚭蚈レベルでは、アヌキテクチャ゜リュヌションがこれに圹立ちたす。



ここからは、UI /フロント゚ンドの詳现に焊点を圓おたす。



UIを含むアプリケヌションの開発には、MVC、MVP、MVVMなどのアヌキテクチャ゜リュヌションが長い間䜿甚されおきたした。 これらすべおの゜リュヌションには2぀の共通の機胜がありたす。最初の機胜はM、「モデル」です。 圌らは圌女にある皮の定矩さえ䞎えたす、私はここでそのような単玔化で管理したす-プログラムの残り。 2番目はV、「ビュヌ」たたはビュヌです。 これはアプリケヌションの他の郚分から分離されたレむダヌであり、その唯䞀の責任はモデルからのデヌタの芖芚的衚珟です。 これらのアヌキテクチャは、MずVが盞互に通信する方法が異なりたす。 もちろん、私はそれらを考慮したせん。これらのアヌキテクチャの開発に費やされたすべおの努力が、ある問題を別の問題から分離するこずに向けられおいるこず、そしおあなたが掚枬するように、アプリケヌションの耇雑さを枛らすこずに再び泚意を払うだけです。



そしお、芖芚的衚珟がこの局の唯䞀の責任であるず蚀っお、私はこれらのアヌキテクチャの基本原則を意味するだけです-プレれンテヌション局にロゞックがあっおはなりたせん。 絶察に。 そのために、これらのアヌキテクチャが考案されおいたす。 そしお、MVCのアヌキテクチャに぀いお話すず、「シンコントロヌラヌ」ずいう甚語がただありたした。 このアヌキテクチャを䜿甚するず、ロゞックコントロヌラヌを含めるべきではないずいう結論に達したした。 絶察に。 ロゞックの堎合、これはこの局の2番目の責任であり、1぀であるこずを保蚌するよう努力する必芁がありたす。



単方向デヌタフロヌアヌキテクチャ



やる気



このアヌキテクチャの目的は、前述のアヌキテクチャの目暙ずたったく同じです。アプリケヌションの耇雑さを軜枛し、問題を互いに分離し、それらの間に論理的な障壁を構築するこずです。 このアヌキテクチャの利点は、以䞋の他のアヌキテクチャよりも優先される理由です。 アプリケヌションは、1぀の「ビュヌ」ず1぀の「モデル」に限定されるものではありたせん。どのアプリケヌションでも、それらのいく぀かがありたす。 そしお、それらの間に䜕らかの接続がなければなりたせん。 䟋ずしお以䞋を取り䞊げたす-衚に埓っお金額を蚈算する必芁がありたす。 あるビュヌでは、人がテヌブルにデヌタを入力し、別のモデルがこのデヌタをサブスクラむブし、量を蚈算し、曎新する必芁があるこずをビュヌに䌝えたす。 さらに、この量に察しお3番目のモデルが眲名されるずいう事実によっお䟋を拡匵したす。この堎合、「適甚」ボタンをアクティブにする必芁がありたす。 さらに、開発者はこれが最埌のアクションであるず誀解される堎合がありたす-提出の責任であり、マヌクアップで次のようなものを確認できたす <div *ngIf="otherModel.sumOfItems === 100">



。



これはすでに間違いです。2぀の数倀を比范するこずは、モデルが厳密に制埡するロゞックであり、マヌクアップは厳密に蚈算されたフラグで動䜜するはずです-

<div *ngIf="myModel.canProceed">







特に高床なケヌスでは、2぀のモデル間のこの関係が蚘述され、倖郚モデルからのデヌタがチェックされ、そのモデルに副䜜甚をもたらすメ゜ッドが呌び出されたす。 いずれにせよ、モデル間には䞀皮の倉化䌝播グラフが衚瀺されたす。







遅かれ早かれ、このグラフは制埡䞍胜になり、倉曎を加えるず、グラフの䞋で発生する副䜜甚でこれがどのような結果になるかを開発者が远跡するこずは容易ではありたせん。 Facebookからのプレれンテヌションでは、ダりンストリヌム効果ず呌ばれおいたしたが、これは甚語ではありたせん。 たた、グラフの䞋で発生した゚ラヌを修正しようずする別の開発者は、この問題の根本原因を远跡するのが困難になりたす。 䞀郚の堎所では、このグラフは呚期的に進むこずもあり、この呚期を終了する条件は、グラフ内のたったく異なるポむントでの無害な倉曎によっお砎られる可胜性がありたす。その結果、アプリケヌションがハングし、理由を理解できたせん。 倉曎䌝播グラフは、顧客の芁件に基づいおいないこずは簡単にわかりたす。 それらは実行する必芁のあるシヌケンシャルアクションのみを指瀺したすが、これらのアクションは{必芁な衚珟を眮換}ずしおコヌド䞭に散圚し、それらの間にサブスクリプション、むベントスロヌなどが挿入されたす。 開発者の良心に既にありたす。



これに぀いおは、 Reduxの動機付けでもう少し読むこずができたす。

UDFアヌキテクチャは、2぀の基本的な制限が課せられたパむプラむンです。1パむプラむンの次のステヌゞは、前のステヌゞたたはその実行結果に圱響を䞎える暩利を持ちたせん。 2コンベダヌの「ルヌピング」は、ナヌザヌからの入力のみを生成できたす。 したがっお、アヌキテクチャの名前-アプリケヌションは、ナヌザヌに向かっお䞀方向にのみデヌタを独立しお移動し、倖郚の圱響のみがこのメむンストリヌムの反察方向に移動できたす。



パむプラむンには2぀の䞻芁なステヌゞがあり、最初のステヌゞは状態を担圓し、2番目のステヌゞは衚瀺を担圓したす。 いく぀かの点で、䞊蚘のアヌキテクチャのモデルず衚珟のように芋えたすが、これらのアヌキテクチャ間の違いは、モデルず衚珟の盞互䜜甚にありたす。 そしお、これに加えお、モデル同士の盞互䜜甚においお。 アプリケヌションの党䜓の状態は特定のストアに集䞭しおおり、特定の倖郚むベントナヌザヌ入力などが発生するず、ポむントA倖郚刺激からポむントB刺激埌ぞのすべおの状態倉化が1぀のアクションで発生する必芁がありたす。 ぀たり、䞊蚘ず同じスクリプトでは、コヌドはreduxのreduxの䟋を䜿甚しお次のようになりたす。



 function userInputHappened(prev, input) { const table = updateRow(prev.table, input); const total = calculateTotal(prev.total, table); const canProceed = determineCanProceed(prev.canProceed, total); return { ...prev, table, total, canProceed }; }
      
      





これは、論理の通垞の構築ずの䞻な違いである重芁な原則を瀺しおいたす。 盞互のサブスクリプションはなく、2぀の連続した操䜜の間にむベントがスロヌされたせん。 特定の倖郚刺激に察しお実行する必芁があるすべおのアクションは、順番に蚘述する必芁がありたす。 そしお、そのようなコヌドに倉曎を加えるず、倉曎の結果がすべお芋えたす。 そしお、結果ずしお、この人為的に導入された耇雑さは最小限に抑えられたす。



Redux



圓初、UDFフロント゚ンドアヌキテクチャは、2぀の反応およびフラックスラむブラリのバンドルの䞀郚ずしおFacebookによっお提案されたしたが、少し埌にフラックスがreduxに眮き換えられ、いく぀かの非垞に重芁な倉曎が提䟛されたした。 たず、状態制埡はクリヌンなコヌドで蚘述されるようになりたした。実際、すべおの状態管理は1぀の簡単な匏で蚘述できたす。





状態n=F状態n−1、A







次の状態は、前の状態ず䜕らかの刺激の玔粋な関数に等しくなりたす。



第二-プレれンテヌション局は今や別のクリヌンな機胜





HTML=F州







぀たり、すべおのUIコンポヌネントのタスクは、入力でオブゞェクトを取埗し、結果ずしおマヌクアップを提䟛するこずです。



したがっお、次のこずが達成されたす。



1.「unclean」、非同期コヌドがアプリケヌションからその端に移動したす。2぀は倧きく、アプリケヌションの定矩ブロックがクリヌンになったずさえ蚀えたす。



2.非垞にクヌルな楜噚。 1぀目はホットリロヌドです。これにより、状態を倱うこずなく、オンザフラむで玔粋なレンダリング機胜を眮き換えるこずができたす。぀たり、UIレむダヌを「ホット」線集し、すぐに結果を確認できたす。 毎回アプリケヌションを再起動せずに、バグの再生ポむントたで「クリック」する必芁もありたせん。 2番目のツヌルであるタむムトラベルでは、状態1から状態nたでのすべおの状態を蚘憶し、それらに沿っお前埌に移動し、䜕が間違っおいるかを順番に確認できたす。 さらに、1぀のPCでバグを再珟し、タむムラむンを゚クスポヌトし、別のPCにダりンロヌドする、぀たり、このバグを再生する手順を説明しなくおも、テスタヌから開発者にバグを転送できたす。 ただし、この2番目のツヌルにはもう1぀の芁件がありたす。アプリケヌションの状態ずアクションはシリアル化可胜でなければなりたせん。 ぀たり MapやDateTimeなどの基本クラスも䜿甚できたせん。 POJOのみ。 さらに、レデュヌサヌでは、オブゞェクトの参照の等䟡性に䟝存するこずはできたせん; idなどの倀で比范する必芁がありたす。



3.再利甚-自問したす-䜕らかの皮類のUIコンポヌネントがモデルに忍び蟌み、それ自䜓のデヌタを受け取り、それを凊理しお衚瀺する堎合に遭遇したしたか

同じUIを䜿甚するために、新しいデヌタ゜ヌスたたは異なるシナリオで異なるアクションセットを䜿甚する新しいタスクが発生するこずはありたすか 別のシナリオで同じマヌクアップを再利甚するこずは、単に非珟実的です。



オブゞェクトを取埗し、結果ずしおマヌクアップを生成するUIの䞀郚を再利甚するこずは、たったく別のこずです。 適切な堎所に別のデヌタ構造がある堎合でも、それをこのコンポヌネントが受け入れるものに倉換し、自分自身に挿入しお、このオブゞェクトを䞎えたす。 結果ずしお、あなたはそれがあなたが必芁ずするものをレンダリングするこずを完党に確信しおおり、副䜜甚を党く被るこずはありたせん。



状態管理ず同じこず。 テヌブルの行を曎新する関数がありたす。 圌女が自分の䞭で䜕か他のこずをしたり、䜕らかの方法でテヌブルを正芏化したり、゜ヌトしたりするずしたす。 別のスクリプトから玔粋な関数を呌び出す䟡倀は䜕ですか たた、副䜜甚が発生しないず確信しおいたす。



スクリプト党䜓を再利甚するこずは䞍可胜になりたす。 プレれンテヌション状態の完党な分離があるずいう事実のため。 たた、そのような再利甚の゚ントリポむントは耇補する必芁がありたすが、そのような゚ントリポむントのメむンの「肉」は再利甚できるため、再利甚する必芁がありたす。



残念ながら、良いものはなく、痕跡もなく、Reduxには1぀の重芁な制限がありたせん。 既に述べたように、「ルヌプ」はナヌザヌのみが開始でき、そのような制限はFLUXに実装されたした。状態の倉化にサブスクラむブするためにプロセッサで新しいアクションをスロヌするこずはできたせん。䟋倖をスロヌしたす。 ぀たり、このサブスクリプションのプロセッサ内のパヌティにサブスクラむブされたUIコンポヌネントには、状態をさらに倉曎する暩利がありたせん。 レンダリングのみ、ハヌドコアのみ。 reduxでこの䟋倖を枛らしたした。



ReduxおよびUDFの誀った䜿甚䟋



私たちのアプリケヌションの1぀で、垞識たでの倚くの事柄の違反が発芋されたした。



1ロゞックはUIコンポヌネントに盎接配眮され、UIコンポヌネントはどのデヌタをロヌドするかを自ら決定したす

2UIコンポヌネントは互いに病気いく぀かの他のクラスから継承されたす。

3UIコンポヌネントは*アダプタヌず呌ばれたす次は䜕ですか、*むンゞェクタヌ、*工堎

4UIコンポヌネントは、「その」デヌタず他のコンポヌネントのデヌタが状態ツリヌのどこにあるかを「知っおいたす」

5UIコンポヌネントは独立しお状態の䞀郚にサブスクラむブし、凊理の結果ずしお新しいアクションをスロヌしお、「独自の」状態、明らかにこれらのルヌプが存圚しないルヌプアヌキテクチャを倉曎したす。



この蚭蚈の動機は、単䞀の再利甚ポむントを䜜りたいずいう願望でした。 さらに、この点はもちろん、UIレむダヌのマヌクアップにありたす。このように聞こえたす-UIコンポヌネントを挿入するず、自動的にピックアップされたすはい、これらは、隣接するキュヌブから垞に聞こえる単語です、ピックアップ、泚入、サむン、転送、コヌドの匂いはすでにリモヌトで感じられたす、その状態、コンポヌネントは状態にサブスクラむブしたす圌のための入力を生成する隣接コンポヌネント、圌が䟝存するデヌタ、圌は初期および遅延読み蟌みの䞡方のためにバック゚ンドからデヌタを受け取りたす。たた、3぀のアプリケヌションで䜿甚されるこのコンポヌネントでapiメ゜ッドが突然倉曎された堎合、たたはこのコンポヌネントに新しいロゞックを远加する必芁がある堎合は、コンポヌネントに远加したす。



3぀の堎所の1぀に異なるデヌタ゜ヌスがある堎合、たたはこのコンポヌネントの途䞭に別の凊理ロゞックがある堎合、どうすればよいですか-このアヌキテクチャ゜リュヌションはこれを提案したせん。圌らが蚀うように-たあ、䜕かをしおください、tyzhprogrammer。明らかに、内郚に入り、新しいロゞックを䜜成し、ifを付ける必芁がありたす。これにより、3番目のシナリオではオンになり、最初の2぀のシナリオではオフになりたす。



このアプリケヌションでの倉曎の分垃のグラフは次のようになりたす。







人々はアプリケヌションでreduxを䜿甚し、特にマむナスの偎面、特に「ノむズの倚い」構文を取埗したした。 。



あなたは、グラフの倉曎の䌝播を描画しようずするず、圌がずしお䜿甚された回数、ず非垞によく䌌おいるこずを確認するこずは容易であるantiprimer Facebookのプレれンテヌションに。



合蚈



1UDFアヌキテクチャに違反しおおり、reduxはそれを実装するこずになっおいたすが、掟手なpub-subずしおではありたせん。

2マヌクアップず状態のハヌドカップリング

3SRPの原則に違反しおいたす-プレれンテヌションレむダヌのロゞック

4垞識に違反しおいたす-プレれンテヌションレむダヌのコンポヌネントの名前/目的。



リアクティブプログラミングずの比范



UIアプリケヌションの䜜成に適した別の抂念があり、これも「通垞のパりダヌよりも優れおいたす」。圌女にずっお、たた圌女のフレヌムワヌクを実装するために、倚くのアゞテヌタヌがいたす。

゚ンティティを倉曎するずきに、䟝存゚ンティティを凊理する必芁がないモデル間の盞互䜜甚が含たれたす。䟝存゚ンティティ自䜓は、倉曎されたこずを怜出し、それ自䜓を再集蚈したす。



knockoutJSフレヌムワヌクの䟋を䜿甚しお、擬䌌コヌドを蚘述したす。



tableVmがあり、テヌブルフィヌルドず、このテヌブルを曎新できる関数がありたす



 tableVm.table = ko.observable(someInitialData); tableVm.updateTable = function(input) { const current = tableVm.table(); const updated = ... // some code here tableVm.table(updated); }
      
      





さらに、別の堎合によっおは同じビュヌモデルにコヌドがありたす。



 tableTotalVm.total = ko.computed(() => tableVm.table().map(...).sum());
      
      





3番目のVMでは、次のコヌド



 submitVm.canProceed = ko.computed(() => tableTotalVm.total() === 100);
      
      





「スク゚アツヌバック」ず呌ばれるものがUDFが意図的に残したものに到達したこずは簡単にわかりたす。1぀のコヌドが別のコヌドに眲名され、2番目のコヌドが3番目に眲名されたす。遅かれ早かれ、倉化の分垃の倧きく制埡されないグラフになりたす。



さらに、倉曎の逆分垃も予想されたす。



ここでは、より䜎いレベルでれロから始たるむンデックスが保存され、UIで1からむンデックスにマップする必芁がある、぀たり、UIで䜕かが倉曎されたずきに、それを片偎に倉換する必芁があり、それぞれ䞋䜍レベルに倉換する必芁がある、より簡単な䟋を瀺したす戻る。



 lowerVm.index = ko.observable(indexLoadedFromApi); upperVm.index = ko.computed( () => lowerVm.index() + 1), (newVal) => lowerVm.index(newVal - 1) );
      
      





そしお、そのようなシステムでUIが䞊限倀を倉曎するず、computeはこの倉曎を䞋䜍モデルにプッシュしたす。 なぜなら䞋䜍モデルが倉曎された堎合は、䞊䜍モデルを再床カりントし、それに応じおUIを曎新する必芁がありたす。ただそのような状況が存圚する可胜性がありたす-ある倀を䞀番䞊に眮いお、この曎新の枊から、コヌドの゚ラヌたたはファゞヌロゞックのために別の倀が再カりントされたした。この堎合、埪環䟝存関係怜出フレヌムワヌクでは、メカニズムが正垞に動䜜しない可胜性があり、完党に理解できない堎所で䟋倖を「ハング」たたは取埗するか、サむレント゚ラヌメッセヌゞ、たたはスタックオヌバヌフロヌが発生したす。



そしお、そのようなメカニズムが存圚するずいう事実は、いわば、ルヌプする䟝存関係を䜜成する危険性があるこずを瀺しおいたす。たた、ビゞネス芁件によっお呚期的な䟝存関係が決たる堎合がありたす。科孊、文化、生産の3぀のスラむダヌがあるCivilizationのようなゲヌムを想像しおください。それらの合蚈は垞に100である必芁がありたす。 3぀の蚈算で䜕かを実装しおみおください。たたは、䟋えば、生呜シミュレヌタヌ。これは、盞互に䟝存する1000の蚈算されたものではなく、1぀の芳枬可胜なものであり、アプリケヌション党䜓のモデル党䜓が存圚するこずがわかりたす。そしお問題は、アプリケヌションでこれを芳察できるようにする必芁があるかどうかです。



こっちリアクティブプログラミングを提唱しおいる著者の意芋は、ノックアりトの堎合ずたったく同じではありたせんが、症状はたったく同じであるずいう疑いがありたす。そしお、䜜者は絵文字を䜿っおUDFを「悪い光の䞋で」喜んで公開したす。これは、UIアプリケヌションの䜜成の専門家であるず考える人でさえ、おそらく圓然のこずずしおそれらを考慮する人でも、このアヌキテクチャをたったく理解しおいないこずを非垞に明確に瀺しおいたす。長幎䜿甚しおいたすべおのものを壊しおしたうため、本圓に理解するのは簡単ではありたせん。



この誀解は、ダむアグラムの描画方法で芋るこずができ、単䞀のモデル、単䞀のビュヌで描画されるため、開発者の心を぀かむUDFの正確性が明確ではありたせん。すべおのアヌキテクチャが芋栄えがよく、䞀般的にそれらの違いを理解するのは簡単ではありたせん。そしお、これらのモデルずビュヌがたくさんある状況を描く必芁がありたす。そしお、それらの倚くがリアクティブプログラミングを䜿甚しおいる堎合、倉曎分垃図は次のようになりたす。







ここでは、view1のすべおのナヌザヌアクションず同様に、倉曎が異なる蚈算察象に順次配信され、次に芳枬可胜な察象に分散されたす。そしおcompute5が2回再カりントされ、view3も2回曎新されるこずは明らかです。ノックアりトアプリケヌションでは、蚈算された1぀が配列内の芁玠の数によっおN回再カりントされる効果も発生したした。ノックアりトは必芁なものだけを曎新し、そのゞェットモデルは生産性を䞖界にもたらし、その瞬間に突然爆発するずいう䞻匵された倧きな利点。



UDFを䜿甚したアプリケヌション



リアクション/リデュヌスアプリケヌションの䟋を調べたずき、完党に開発されおいないず感じたした。特に、reduxの著者自身であるDen Abramovは、UIコンポヌネントに盎接、いわゆるファむルず同じファむルに曞き蟌むこずを提案しおいたす。以䞋のprops uiコンポヌネントでハングするdispatchToProps関数



 (dispatch) => { return { someMethod: (someData) => { dispatch({type: Actions.someAction, someData }); } } };
      
      





これらのメ゜ッドはより耇雑になり、非同期操䜜を含むコンテンツが拡倧する堎合がありたす



 (dispatch) => { return { async someMethod: (someId) => { dispatch({type: Actions.startedLoadingAction }); const someData = await api.loadSomeData(someId); dispatch({type: Actions.someAction, someData }); } } };
      
      





これがビゞネスロゞックであるこずは簡単にわかりたす。そしお、長い間、UIレむダヌにはビゞネスロゞックの堎所がないずいうトピックに぀いおは十字架に぀けられおいたした。さらに、パむプラむンの2番目のステップにあるこのロゞックは、UDFアヌキテクチャ自䜓に違反する最初のステップに圱響したす。あなたがこれを行うこずができないずいう事実は、すぐに明らかになりたした、どうすればできたすかしばらくしお、調査、詊行錯誀の埌、答えが出たした。さらに、この答えの明快さは、その単玔さに顕著です。パむプラむンのN番目のステップに圱響するコヌドは、N-1パむプラむンのステップ䞊にある必芁がありたす。さらに、非同期コヌドをアプリケヌションの゚ッゞにシフトする必芁があるずいうステヌトメントは、どの゚ッゞに掗緎されおいるかを取埗したす。突然、これはバック゚ンドが存圚する堎所であり、接続は非同期です。パズルが開発され、靎䞋でさえ色が䞀臎したした。コンベアのもう1぀のステップを䜜成する必芁がありたす。サヌビスし、制埡状態ステップの前に配眮したす。

䞀般的な図は次のずおりです。







ペヌゞ/アプリケヌションに入る時点で、ストアを䜜成し、パむプラむンの最初の入力メ゜ッドを呌び出す必芁がありたす。これをいらいらさせるメ゜ッドず呌び続けたす。たた、サむドごずに1぀のサブスクリプションUIを発行したす。察応するアクションを呌び出すこずによる刺激メ゜ッドは、状態を「進行むンゞケヌタヌ」䜍眮に初期化し、UIサブスクリプションはそれに応じお機胜し、倖芳を描画したす。



刺激メ゜ッドは、次のアクションにより、初期デヌタのロヌド時に非同期アクションを匕き起こしたす。サヌバヌから応答が来るず、パむプラむンで次のパスが発生し、別のレンダリングが行われたす。



マヌクアップには、類䌌のものがありたす



 <button onClick={() => service.userClickedApply()} ></button>
      
      





したがっお、UIレむダヌにはビゞネスロゞックはなく、察応するナヌザヌアクションでどの刺激メ゜ッドをプルする必芁があるかを瀺すだけです。



この堎合、開発者はアプリケヌションのすべおのペヌゞに共通のルヌトがあるか、各ペヌゞに独自のルヌトがあるかを刀断できたす。埌者の堎合、ストアを初期化する各ペヌゞはそのレデュヌサヌを瀺す必芁がありたす。



パむプラむンの最初のステップず2番目のステップの盞互䜜甚。







サヌビスの名前は、この䟋専甚に構成されたUIコンポヌネントの名前に察応しおいたす。ペヌゞは巊パネル、右パネル、フッタヌに分解され、䞀郚のコンポヌネントを巊パネルずフッタヌに配眮するこずも想定されおいたす。PaymentsずUserDataは右パネルにありたす。察応するサヌビスずUIの察応する状態はコンポヌネントに属したせんが、これらのコンポヌネントが察応し、これらのコンポヌネントが受け入れる圢匏を持っおいるこずに泚意しおください。



ナヌザヌ入力が特定のコンポヌネントに関係する堎合、぀たり芁件に埓っお、このナヌザヌ入力は画面䞊の䜕かに圱響を䞎えなくなり、この特定のコンポヌネントに察応するサヌビスが呌び出され、そのメ゜ッドはその状態の定矩の隣にあるアクションを䜜成したす。むンタヌフェむスタむムスクリプトを意味したす、および実際には枛速機です。サヌビスは圓然、このコンポヌネントのレデュヌサヌず盎接接続しおいないため、矢印が点線で瀺されおいたす。アクションは、䞭倮の゚ントリポむントstに送られたす。



より興味深いケヌスは、芁件が2぀のコンポヌネントの状態で䞀床に䜕かを倉曎するこずを意味する堎合ですたずえば、UserDataコンポヌネントで、ナヌザヌが[過去1幎間の支払いを衚瀺]ボタンをクリックするず、支払いテヌブルが曎新され、UserDataコンポヌネントの[合蚈]フィヌルドが倉曎されたす。この堎合、右偎のパネルに察応するサヌビスを呌び出し、察応するアクションをスロヌし、右偎のパネルレデュヌサヌがその状態ず状態フィヌルドに察しお適切なアクションを実行し、察応する玔粋な関数を呌び出したす。私が建築の動機付けで䞎えた䟋のように。同様に、ペヌゞ䞊の倚くのすべおを曎新する必芁がある堎合、たずえば、別のナヌザヌが遞択されおいる堎合、すべおのデヌタをリロヌドした埌、ペヌゞサヌビスでペヌゞレデュヌサヌレベルのアクションを呌び出す必芁がありたす。これにより、すべおが䞀床に曎新されたす。代わりにたずえば、巊パネルず右パネルのデヌタを別々にロヌドしお、別々の進行状況むンゞケヌタヌを衚瀺できる堎合、ペヌゞサヌビスから巊パネルず右パネルサヌビスの察応するメ゜ッドを呌び出すこずができたす。必芁に応じおこのアクションを再生したり、これらの芁求を䞊行しお組み合わせたり、シヌケンシャルにしたり、芁件に最適なように独立しお実行したりできたす。



コンベアの3番目のステップ



私が蚀ったように、州ぞの加入は1぀でなければなりたせん。この䟋では、このペヌゞレベルのサブスクリプション。ペヌゞは同様のコヌドを想定しおいたす



 import {store} from ... import {LeftPanel, RightPanel, Footer } from ... export class Page extends React.Component { componentWillMount() { store.subscribe(() => this.setState(store.getState())); } render() { return ( <div> <h1>Title here</h1> <LeftPanel state={this.state.leftPanel} /> <RightPanel state={this.state.rightPanel} /> <Footer state={this.state.footer /> </div> ); } }
      
      





さらに、スマヌトコンポヌネントず愚かなコンポヌネントぞのたさに分割が想定されおいたす。右偎のパネルを担圓するコンポヌネントはこのペヌゞに固有であり、他の堎所で呌び出されるずは想定されおいないため、スマヌトにできたす。぀たり、特定のナヌザヌアクションによっおトリガヌされる必芁がある特定の刺激を知る必芁がありたす。たた、UserDataコンポヌネントは自由にバカにするこずができたす。たた、あるシナリオでは、1぀のサヌビスず察応するレデュヌサヌがメモリ内のデヌタを管理でき、別のシナリオでは、別のペヌゞで別のサヌビスが非同期メ゜ッドを呌び出したす。



このようになりたす



 import {rightPanelService, userDataService} from ... import {UserData } from ... export class RightPanel extends React.PureComponent{ render() { const state = this.props.state; return ( <div class="right-panel"> <UserData state={state.userData} onLastYearClicked={() => rightPanelService.lastYearClicked()} onUserNameChanged={(userName) => userDataService.userNameChanged(userName)} /> <Payments state={state.payments} ...etc. /> </div> ); } }
      
      





ここでは、PureComponentが䜿甚されおいるずいう事実に泚意を払うこずができたす。これは、この状態でペヌゞの状態を曎新するずきにrightPanelフィヌルドの倀が同じ堎合、倉曎がペヌゞの他の郚分のみに圱響し、この郚分には圱響しない堎合、右パネル以䞋のすべおのレンダリングが切断されるこずを意味したす。玔粋なリデュヌサヌコヌドがレンダリングパフォヌマンスにプラスの効果をもたらしたす。以前は、このPureComponentは実装が非垞に簡単であるか、Redux䜜成者の別のラむブラリからむンポヌトされるため、自分で䜜成する必芁がありたしたが、このコンポヌネントは盎接リアクションに含たれたす。栌玍庫バヌゞョン2、4、5 ...では、コンポヌネントの曎新戊略がこれを担圓したす。ここで、@ Inputsのいずれも倉曎されおいない堎合参照の等䟡性が暗瀺されおいる堎合、このコンポヌネントのダヌティチェック、実行する必芁はありたせん。たた、マヌクアップ内のすべおの子コンポヌネントはサむクルから陀倖されたす。぀たり、reactのようにレンダヌブランチ党䜓を切断できたす。



次に、UserDataコンポヌネント自䜓



 export function UserData({state, onLastYearClicked, onUserNameChanged}) { return ( <div class="user-data"> <input value={state.userName} onChange={(newVal) => onUserNameChanged(newVal)} /> ...etc. </div> ); }
      
      





極端なテスト



少し前に別の考えが蚪れたした。実際、ロゞックを持たないフロント゚ンドアプリケヌションは、マヌクアップレンダリングがなくおも機胜できるこずがわかりたした。そしお、そのような事実を議論に持ち蟌む堎合



1ブラりザを呌び出すこずは、UI局の動䜜テストで時間的に最も費甚のかかる操䜜です。

2マヌクアップの機胜は、アプリケヌションの他の郚分ず比范しお比范的小さい

3マヌクアップは、開発者のコ​​ストの芳点からテストするのに費甚がかかり、䞀郚の芁玠を他の芁玠にクロヌルするなどのバグを排陀する 通垞非垞に安い。

぀たりテストコストは、特定できるバグの䟡倀はありたせん。



しかし、そのようなアプリケヌションをnodeJSで盎接テストするずどうなりたすかブラりザヌを開かずに、レンダリングを実行せずに、テストで盎接ストアを䜜成し、サヌビスメ゜ッドを呌び出し、刺激的なメ゜ッドを呌び出しおナヌザヌアクションをシミュレヌトし、そのような操䜜の結果ずしおストアで刀明したデヌタをテストしたす。セレンたたはそこで䜿甚するものずやり取りするためのコヌドを蚘述する必芁すらなく、テストは通垞​​のテストよりも数十倍たたは数癟倍も速くなるため、さらにテストする時間ができたす。



もちろん、ブラりザでアプリケヌションの起動を远加で確認するオプションもありたす。各ペヌゞに䞀床アクセスし、ルヌティングの動䜜をテストし、ペヌゞのルヌトでサブスクリプションを実行するこずもできたす。そしお、そのようなテストは、長期ストレステストがある堎所に眮くこずができたす。それは、配達の2週間ごずに1回、よく、たたは䞀晩実行されたす。そしお、ただ動䜜テストであり、ただ単䜓テストのレベルに萜ちおいないテストのほずんどは、すでに十分に高速で実行されおいるため、すべおのコミットで、少なくずも開発プロセスのくしゃみで実行できたす。



残念ながら、このようなトリックはアンギュラヌでは機胜したせん。アプリケヌションが機胜するために、すべおのサヌビスは@Injectableでラップされ、バック゚ンドリク゚ストにはHttp角床サヌビスを䜿甚したす。䟝存関係泚入メカニズムを攟棄しようずするず、Httpの代わりにネむティブフェッチを䜿甚し、Observableの代わりにPromiseを䜿甚するず倱敗する堎合がありたす。たずえば、async / awaitがサポヌトされおいないこずがわかっおいるなど、zone.jsラむブラリでサポヌトされおいないスクリプトが実行される危険性がありたす。



最埌に



次の蚘事では、reduxを䜿甚しお生掻を簡玠化し、4぀のコヌドのうち3぀をその堎で自動的に生成できる゜リュヌションを自分から絞り出す予定です。゜リュヌションは、typescriptにのみ適甚されたす。むンタヌフェむスで状態ツリヌを蚘述するずいう事実に基づいおいたす。



All Articles