コアテクノロジー

6か月前、プログラミングテクノロジーに関する投稿( habrahabr.ru/post/163881 )を書きました。これは、私だけでなく仕事を加速するのに大いに役立ちました。



前回は練習と通常の開発モデルとの比較に専念していましたが、今回は技術の理論的基礎についてお話したいと思います。



説明を簡単にするために、Context-Object-Request-Eventシステムからコンテキストを破棄し、タスクの設定と、それらがオブジェクト、イベント、リクエストにどのように関連するかについて説明します。







問題の記述と流れるような抽象化





プログラミングでは、頻繁に流れる抽象化に対処する必要があります。 たとえば、データベースへの接続が落ちる可能性があることをコードで考慮していない場合、問題が発生します。



流れる抽象化にはさらに根本的な問題があり、複雑な動作のモデリングに関係しています。 タスクに追加の要件が追加されると、作成されたばかりのコードが使用できなくなり、すべてをやり直す必要が生じることがよくあります。 これもまた進行中の抽象化です。問題の解決策を、問題の観点からではなく、変数、リスト、リンク、オブジェクト、デザインパターンといったプログラミング言語の観点からモデル化しました。 要件がわずかに変更されたため、コード全体が使用できなくなりました。



これは、プログラミング言語が、定式化で使用するタスク記述言語と類似していないためです。



実際のタスクのほとんどは、「AがBを実行するとき」と「Cを実行する必要がある。Dの方法で実行する」という2つのパターンに分類されることを確認します。

次の表記法を導入します。Aはイベント、Bはイベントへの反応、Cはアクションの要求、Dは実行方法です。



最初のケースの例:

-ユーザーがこのボタンをクリックしたら、このアニメーションを表示しましょう(うーん、会議のように)

-デザイナーのアーニャがインターフェースのプロトタイプを描くとき、​​私はそれを見て、改善についての私の考えを表現したい

-クリックごとに統計を収集しましょう

-5回目の実行ごとにバナーを表示します



2番目の場合:

-このアイデアのデザインを描く必要があります。 アーニャにやらせてください。 まあ、または、Vova。

-現在のアカウントの値をデータベースに保存する



次の点に注意してください:最初の場合のタスクの本質は、Aが発生したときにBを実行することであり、Aが何であるか、どの特定のコンテキストが発生したかなどは関係ありません。 また、このタスクは、イベントAが発生するタスクとは完全に分離されており、他の目標に専念しています。 2番目のケースでは、逆です。 タスクCがどのように行われるかはまったく関係ありません。少なくとも何らかの形で適切な方法で行われることが重要です。



これを理解することが重要なのはなぜですか? 次のjsの抽象的なゲームコード(またはその他のもの)を検討してください。

addMoney: function(amount) { this.balance+=amount; if(this.balance > 250) { $('.you_win!').animate({css:{opacity: 1}}) } }
      
      





このコードは非常に悪いです。 なんで? それはお金の論理が表示の論理と混ざり合っているからです。 さらに悪いことに:



 $('.coin').click(function(){ this.balance+=15; if(this.balance > 250) { $('.you_win").animate({css:{opacity: 1}}) } })
      
      







プロトタイプでは、このような例は非常に頻繁に発生します。 タスクのあらゆる側面-デザイン、お金のカウント、アニメーション-をすべて、カヤックに触れてください。それはすぐに混乱に変わり、失敗します。 通常の方法 タスクの内容を説明してください:

-ユーザーがコインをクリックしたときに、残高に追加します

-残高が250を超える場合、獲得したバナーを表示します



タスクを3つのオブジェクトに分割します。1つは表示とUI、2つ目はアカウントの状態、3つ目はユーザーの勝敗を決定します。

 var UI = { handleCoinClick: function() { .... }, showWinAnimation: function() { .... }, ... } var Balance = { addCoins: function() { ... }, ... } var WinWatcher = { watchForWin: function() { .... } ... }
      
      







ここのUIは、表示とクリック-ユーザーインタラクションのみを担当します。



ここで、これらのコンポーネントを何らかの方法で接続する必要があります。 相互に呼び出すのは良くないので、イベントやリクエストに接続します

 var UI = { handleCoinClick: function() { // ,   DOM Init,   ,      .... $('.coin').click(function(){ //       Event_CoinClick ..... }); }, showWinAnimation: function() { // ,        Request_ShowUserWin $('.you_win').animate({opacity: 0}); }, ... } var Balance = { addCoins: function() { // ,    «  » Event_CoinClick this.balance+=15; //   ,     Event_BalanceChanged }, ... } var WinHandler = { watchForWin: function(balance) { // ,   ,    Event_BalanceChanged if(balance > 250) { //   ,    Request_ShowUserWin } } ... }
      
      







ここで、コメントが「…を呼び出す」および「ここにドロップ/リクエストする」というコメントのあるコードをリンクする必要があります。 しかし、ここでは、同じ流れるような抽象化に直面しています。 BalanceメソッドとWinHandlerメソッドをUIから直接呼び出す場合、統計情報やその他の複雑な情報を収集する必要があり、他のタスクに関連する呼び出しがUIメソッドに追加されます。 メソッドはクリーンでなくなります。



したがって、メソッドをシンプルにするようにします。 イベントディスパッチャーに依存関係の解決を提供する



Core.js




前回、オープンソースの実装を行うことを約束しました。 現在、javascript github.com/okneigres/corejsの実装があります



ライブラリはブラウザとNode.jsの両方で機能します



 <script src="core.js"></script> var Game = { }; //  Game.UI = { CoinClickEvent: new Core.EventPoint, handleCoinClick: function() { Core.CatchEvent(Event.DOM.Init); $('.coin').click(function(){ new Game.UI.CoinClickEvent(); }); }, showWinAnimation: function() { Core.CatchRequest(Game.WinHandler.ShowUserWinRequest); $('.you_win').animate({opacity: 0}); }, ... } Game.Balance = { ChangedEvent: new Core.EventPoint, addCoinsOnClick: function() { Core.CatchEvent(Game.UI.CoinClickEvent) this.balance+=15; new Game.Balance.ChangedEvent; } ... } Game.WinHandler = { ShowUserWinEvent: new Core.EventPoint, ShowUserWinRequest: new Core.RequestPoint, watchForWin: function(balance) { Core.CatchEvent(Game.Balance.ChangedEvent) if(balance > 250) { new Game.WinHandler.ShowWinRequest; } } ... } Core.processNamespace(Game);
      
      







このコードを使用すると、何でも簡単に実行できます。新しい機能を追加します。



 //       Game.GatherStat = { sendClick: function() { Core.CatchEvent(Game.UI.CoinClickEvent); $.post('/stat/gather/ajax', {click: 1, date: new Date}); }, sendWin: function() { Core.CatchEvent(Game.WinHandler.ShowUserWinEvent); $.post('/stat/gather/ajax', {win: 1, date: new Date}); } }
      
      







UIのリファクタリング(2つのオブジェクト-UIとUIWinに分割):



 Game.UI = { CoinClickEvent: new Core.EventPoint, handleCoinClick: function() { Core.CatchEvent(Event.DOM.Init); $('.coin').click(function(){ new Game.UI.CoinClickEvent(); }); } }; Game.UIWin = { showWinAnimation: function() { Core.CatchRequest(Game.WinHandler.ShowUserWinRequest); $('.you_win').animate({opacity: 0}); }, ... };
      
      







コードはロジックに厳密に従って記述されているため、コードの操作は簡単です。



結論の代わりに





このようなパラダイムで作業すると、プロジェクトの設計とコンテンツが大幅に簡素化されます。 何度でも改造できますが、タスクのロジックは同じままです。 それからコードを作成してみませんか? 少し練習すれば、そのようなパラダイムで働くことができます。実際、私たちはそれについて考える言葉でタスクを説明するだけなので、それはすべてです。



私の経験では、これにより、インターフェースの設計、さらにはサーバーアプリケーションの設計が大幅に簡素化されます。 タスクが必要とする抽象化レベルでコードを含めることは可能であり、必要です。



All Articles