JavaScriptでの自動依存性注入

エントリー



誰もが知っているように、javascriptは自分の足で簡単に撃つことができる言語です。 この言語をほぼ5年間使用してきましたが、javascriptが高レベルの抽象化を作成するための非常に貧弱なツールを提供するという事実にしばしば遭遇しました。 そして、本格的なMVVM / MVPアプリケーションを作成するとき、主な問題は、 SOLID原則の完全な実装は言うまでもなく、コードと抽象化をきれいに保つことが難しいという事実に出くわします。



時間が経つにつれて、私は私を助けることができる主なパターンの1つが依存性注入であることを理解するようになりました。 そして、JSで彼と実験することにしました。

もちろん、JSはこのパターン(同じ反射神経の基本的な不在)を完全に追跡するためのツールを提供していません。そこで、JSのようなユニークな環境にこのパターンを適応させることで達成したいいくつかの受け入れ基準を自分で設定することにしました。



1.考えられるすべてのグローバル変数を取り除きます。 (共通ライブラリを除く)

2.コードを変更せずにアプリケーションの動作をアップグレードまたは変更する機能。

3.完全な依存関係マップを用意します。

4.アプリケーションの構造内の「暗黙」をすべて削除します。

5.テスト100%でカバーできるコードを作成する



DIマネージャーをどのように見たいかを数日間考えた後、文字通り1晩でそれを書きました。 次に、週末に小さなアプリケーション(WYSIWYGテンプレートエディター)を作成して、アプリケーションを作成するこのアプローチのボトルネックを調べました。 その結果、アプリケーションのすべてのコンポーネントへのアクセスを提供し、JSON configを使用してコンポーネントをアセンブルできる小さなマネージャーになりました。



お願いします。 すぐに警告します-これは古典的なDependency Injectionパターンではなく、JS環境と私のニーズに非常に適合しているため、仕様を読むためにキックを送信する必要はありません。 私は批判に非常に喜んでいます。



使用例



事例1


ユーザーに挨拶するGreeterClassクラス、メソッド、およびウェルカムテキストは、注入によって設定されます。

var GreeterClass = function(){ this.say = function(){ var method = this._getGreetMethod(); var greet = this._getTextMsg(); method(greet); }; }; SERVICES['constructor']['greet-class'] = GreeterClass; //      DI
      
      





クラスの依存関係について説明します。

 SERVICES['dependency']['greet-class'] = { 'greetMethod' : {'object' : 'alert'}, 'textMsg' : {'value' : 'Hello world'} };
      
      





インスタンスGreeterClassクラスを照会し、sayメソッドを呼び出します。

 DI.get('greet-class').say();
      
      





結果:





UPD


この記事はコードについてではなく、コードを整理する方法についてですが、ここで何が起こったのかを説明する価値があると思います。 通話後:
 DI.get('greet-class').say();
      
      





DIでは次のプロセスが発生します。

1.サービスのリストで「greet-class」を探し、その後インスタンス化されます。

2.依存関係がロードされます。

3.依存関係名と一致する名前を持つメソッドが「greet-class」にあるかどうかを確認するための検査が進行中です。

4.そのようなメソッドが観察されない場合、依存関係の名前とプレフィックス_getの種類に一致する名前で作成されます。 呼び出されると、このメソッドは挿入された依存関係を返します。

5.そのようなメソッドが存在する場合、それらが呼び出され、依存関係が引数として渡されます。



つまり、._ getGreetMethod()および。 _getTextMsg()およびartificialは、DIマネージャーで動的に作成されます。

より明確にするために、定義済みのメソッドを使用して例を作成しました。

 SERVICES['constructor']['stack'] = function(){ var stack = []; this.flush = function(){ console.log(stack); }; this.push = function(el){ /*** some actions ***/ stack.push(el); return this; }; } SERVICES['dependency']['stack'] = { 'push' : [ {'value' : 1}, {'value' : 2}, {'value' : 3} ] }; DI.get('stack').flush(); // [1,2,3]
      
      





ここで、DIは各依存関係のネイティブプッシュメソッドを呼び出しました。



事例2


出力方法を変更するタスクがあるとしましょう:

 SERVICES['dependency']['greet-class'] = { 'greetMethod' : {'object' : 'console.log'}, 'textMsg' : {'value' : 'Hello world'} };
      
      







結果:





抽象化を変更せずに実装を変更しました。これが達成されたものです。



事例3


greetMethodに単純なオブジェクトが注入されるようになりましたが、依存関係を持つ別のサービスにすることもできます。

DIには他にもいくつかの責任があります。 たとえば、 「マルチオン」のようなものにすることができます



例:

 SERVICES['config']['greet-class'] = { 'singleton' : true } DI.get('greet-class') === DI.get('greet-class'); // true
      
      







事例4


私は依存関係の置換を見つけます:

 DI.get('greet-class').say(); // Hello world DI.get('greet-class', {'textMsg' : {'value' : 'Bye world'}}).say(); //Bye world
      
      







事例5


DIの概念に適合しない「ハック」を作成する機能(必要な場合があります)。

 SERVICES['dependency']['greet-class'] = { 'greetMethod' : {'value' : function(txt){document.body.innerHTML = txt}}, 'textMsg' : {'value' : 'Hello world'} }; DI.get('greet-class').say();
      
      







結果:





まとめ



そして、これがテストアプリケーションのDI構成です。

/ *ハッキングなしではまだ* /

 DEPENDENCY['application'] = { 'template-manager' : { 'addWidgetModel' : [ { 'service' : 'widget-model', 'dependency' : { 'domainObject' : {'instance' : function(){return WidgetDO(incomingWidget);}}} /*TODO: remove this hack*/ }, { 'service' : 'widget-model', 'dependency' : { 'domainObject' : {'instance' : function(){return WidgetDO(incomingWidget2);}}} /*TODO: remove this hack*/ } ], 'toolsManager' : { 'service' : 'widget-manager', 'dependency' :{ 'addRenderer' : { 'service' : 'text-tools-renderer', 'dependency' : { 'richView' : { 'service-constructor' : 'rich-view', 'dependency': { 'setEventManager' : { 'service' : 'event-manager', 'dependency' : { 'setContext' : {'poll' : 'rich-view'} } }, 'template' : {'value' : 'code/template/tools.html'} } } } }, 'addHandler' : {'instance' : 'TextToolsHandler'}, 'containerRenderer' : { 'service' : 'rich-view', 'dependency': { 'setEventManager' : { 'service' : 'event-manager', 'dependency' : { 'setContext' : {'poll' : 'rich-view'} } }, 'template' : {'value' : 'code/template/tools-container.html'} } } } }, 'editorManager' : { 'service' : 'widget-manager', 'dependency' :{ 'addRenderer' : { 'service' : 'text-editor-renderer', 'dependency' : { 'globalEventManager' : {'service' : 'global-event-manager'}, 'richView' : { 'service-constructor' : 'rich-view', 'dependency': { 'setEventManager' : { 'service' : 'event-manager', 'dependency' : { 'setContext' : {'poll' : 'rich-view'} } }, 'template' : {'value' : 'code/template/editor.html'} } } } }, 'addHandler' : {'instance' : 'TextEditorHandler'}, 'containerRenderer' : { 'service' : 'rich-view', 'dependency': { 'setEventManager' : { 'service' : 'event-manager', 'dependency' : { 'setContext' : {'poll' : 'rich-view'} } }, 'template' : {'value' : 'code/template/editor-container.html'} } } } }, 'applicationRenderer' : { 'service' : 'rich-view', 'dependency': { 'setEventManager' : { 'service' : 'event-manager', 'dependency' : { 'setContext' : {'poll' : 'rich-view'} } }, 'template' : {'value' : 'code/template/application.html'} }} }, 'widget-manager' : {}, 'widget-model' : { 'eventManager' : { 'service' : 'event-manager', 'dependency' : { 'setContext' : {'poll' : 'widget-model'} } } }, 'global-event-manager' : { 'context' : {'object' : 'window'} } }; SERVICES['config'] = { 'global-event-manager' : { 'singleton' : true } };
      
      





うわー、たくさんのネストと通貨? まあ、そのようなカードでさえ理解できないとき、これをすべて理解する方法を想像してください。

私の意見では、それは非常に便利で、アプリケーション全体のマップをすぐに見ることができ、すべてをロックすることができます。そして最も重要なこと-このアプローチは正しいコードを書くことを強制します。



別の非常に重要な点は、この構成はサーバー上で生成でき、さまざまなパラメーターとは異なる可能性があることです。たとえば、特権ユーザーの場合、構成は標準のものと異なる場合があり、その結果、別のアプリケーションが表示されます。



私はこのアプローチがそれ自体を正当化すると信じていますが、アプローチとマネージャー自身の両方に対する客観的な批判を聞きたいです。



GIThubのDIコード多くの点は「もっと簡単にできる」と言わざるを得ませんが、現時点ではSamsung SmartTVのアプリケーションに取り組んでいるので、いくつかの場所で「適応」されています。 また、 KISSの原則を遵守しようとしました。 当然、DIがそれ自体を正当化する場合、2つのドライバーを追加してJSONとXMLで構成を読み取ります。



上記のデモアプリケーションは 、Webkitで直接作成され、他のブラウザーではテストされていません。 ああ。



PS:私はすでに職場でこのアプローチを使用しています。象としてはうれしいです。 完全な幸福のために、それはある種の契約管理者をつなぐことだけに残っています。



*ケース1が更新されました



All Articles