はしがき
この章で説明するのは、S.O.L.I.D JavaScript 言語実装の 5 つの原則のうちの 5 つ目である依存関係反転原則 LSP (依存関係反転原則) です。
英語原文:http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/
依存関係逆転の原則
依存関係逆転の原理の説明は次のとおりです:
A. 高レベルのモジュールは低レベルのモジュールに依存すべきではありません。
高レベルのモジュールは低レベルのモジュールに依存すべきではなく、両方とも抽象化に依存する必要があります
B. 抽象化は詳細に依存すべきではありません。
抽象化は詳細に依存すべきではなく、詳細は抽象化に依存する必要があります
依存性反転原則の最も重要な問題は、アプリケーションまたはフレームワークの主要コンポーネントが、重要ではない低レベル コンポーネント実装の詳細から確実に切り離されることです。これにより、プログラムの最も重要な部分が変更の影響を受けないようになります。低レベルのコンポーネントで。
この原則の最初の部分は、高レベル モジュールと低レベル モジュール間の結合方法についてです。従来の分割アーキテクチャでは、高レベル モジュール (プログラムのコア ビジネス ロジックをカプセル化する) は常に何らかの低レベル モジュールに依存します。 -レベルのモジュール (いくつかの基本的なポイント)。依存関係逆転の原則を適用すると、関係が逆転します。低レベル モジュールに依存する高レベル モジュールとは異なり、依存関係の反転により、低レベル モジュールは高レベル モジュールで定義されたインターフェイスに依存します。たとえば、プログラムのデータを永続化する場合、従来の設計では、コア モジュールが永続化モジュールの API に依存し、依存関係逆転の原則に従って再構築した後、コア モジュールで永続化 API インターフェイスを定義する必要があります。永続化実装インスタンスは、コア モジュールによって定義されたこの API インターフェイスを実装する必要があります。
原則の 2 番目の部分では、抽象化と詳細の間の正しい関係について説明します。この部分を理解するには、C 言語を理解しておくと、その適用性がより明らかになるため、より役立ちます。
一部の静的型付け言語とは異なり、C にはインターフェイスを定義するための言語レベルの概念がありません。C では、クラスはヘッダー ファイルの形式で定義されます。ソースファイルが実装する必要があるクラスメンバーのメソッドと変数。すべての変数とプライベート メソッドはヘッダー ファイルで定義されるため、実装の詳細を抽象化するために使用できます。クラスの実装には、インターフェイスの概念を使用して、抽象メソッド (C の抽象基本クラス) のみを定義することで実装されます。
DIP と JavaScript
JavaScript は動的言語であるため、分離するために抽象化する必要はありません。したがって、抽象化が詳細に依存すべきではないという変更は JavaScript には大きな影響を与えませんが、高レベルのモジュールが低レベルのモジュールに依存すべきではないという大きな影響はあります。
静的型付け言語のコンテキストで依存関係逆転の原理を議論する場合、結合の概念には意味論的および物理的結合が含まれます。つまり、上位モジュールが下位モジュールに依存する場合、意味インターフェイスを結合するだけでなく、下位モジュールで定義された物理インターフェイスも結合します。言い換えれば、高レベルのモジュールはサードパーティのライブラリから分離するだけでなく、ネイティブの低レベル モジュールからも分離する必要があります。
これを説明するには、.NET プログラムに、低レベルの永続化モジュールに依存する非常に便利な高レベルのモジュールが含まれていると想像してください。作成者が同様のインターフェイスを永続性 API に追加する必要がある場合、依存関係逆転の原則が使用されているかどうかに関係なく、低レベル モジュールの新しいインターフェイスを再実装しない限り、高レベル モジュールを他のプログラムで再利用することはできません。
JavaScript では、依存関係逆転の原則の適用は、高レベルのモジュールと低レベルのモジュール間のセマンティックな結合に限定されます。たとえば、DIP は、低レベルで定義された暗黙的なインターフェイスを結合する代わりに、必要に応じてインターフェイスを追加できます。モジュール。
これを理解するために、次の例を見てみましょう:
var mapOptions = {
中央: 新しい google.maps.LatLng(options.latitude,options.longitude),
ズーム: 12、
MapTypeId: google.maps.MapTypeId.ROADMAP
}、
map = new google.maps.Map(this[0], mapOptions),
pos = new google.maps.LatLng(options.latitude,options.longitude);
var marker = new google.maps.Marker({
位置: pos、
タイトル: options.title,
アイコン: options.icon
});
marker.setMap(map);
options.feed.update(function(緯度, 経度) {
マーカー.setMap(null);
var newLatLng = new google.maps.LatLng(緯度, 経度);
marker.position = newLatLng;
マーカー.setMap(マップ);
map.setCenter(newLatLng);
});
これを返します;
};
var updater = (function() {
// プライベート プロパティ
return {
更新: 関数(コールバック) {
updateMap = コールバック;
}
};
})();
$("#map_canvas").trackMap({
緯度: 35.044640193770725、
経度: -89.98193264007568、
アイコン: 'http://bit.ly/zjnGDe',
タイトル: '追跡番号: 12345'、
フィード: アップデータ
});
上記のコードでは、現在追跡されている位置情報を表示するために、1 つの DIV を Map に変換する小型の JS クラスがあります。trackMap 関数は、3 番目の Google Maps API と Location feed の 2 つの依存関係があります。これは、アイコンの位置が更新されるときにコールバックを使用して(初期化時に提供される)、緯度および精度経度を転送します。Google Maps API は、インターフェイスを汚染するために使用されます。
フィード オブジェクトのインターフェイスは、装備されている可能性もありますが、装備されていない trackMap 関数の要求を解除する設定も可能です。実際、他の角の色は非常に個別であり、単一の異なる実装では不要であり、Google マップに依存しています。このメッセージには Google Maps API が組み込まれており、異なる地表プロバイダーを切り替える必要がある場合に、異なるプロバイダーに適合できるように trackMap 関数を書き換えることができます。Google マップのクラスの会話を翻訳するには、トラックマップ関数を再書き込みして、1 つのインターフェイス (抽象化された地形図提供プロバイダのインターフェイス) に会話を合わせる必要があります。 Google Maps API の 1 つのオブジェクト、次のように再構築後の trackMap 関数:
オプション = $.extend({}, デフォルト, オプション);
options.provider.showMap(
これ[0]、
オプション.緯度、
オプション.経度、
オプション.アイコン、
options.title);
options.feed.update(function(緯度, 経度) {
options.provider.updateMap(緯度, 経度);
});
これを返します;
};
$("#map_canvas").trackMap({
緯度: 35.044640193770725、
経度: -89.98193264007568、
アイコン: 'http://bit.ly/zjnGDe',
タイトル: '追跡番号: 12345'、
フィード: アップデーター、
プロバイダ: trackMap.googleMapsProvider
});
リターン {
showMap: function(要素, 緯度, 経度, アイコン, タイトル) {
var mapOptions = {
中央: 新しい google.maps.LatLng(緯度、経度)、
ズーム: 12、
mapTypeId: google.maps.MapTypeId.ROADMAP
},
pos = new google.maps.LatLng(緯度, 経度);
マップ = new google.maps.Map(element, mapOptions);
マーカー = new google.maps.Marker({
位置: pos、
title: タイトル,
アイコン: アイコン
});
マーカー.setMap(map);
}、
updateMap: function(緯度, 経度) {
marker.setMap(null);
var newLatLng = new google.maps.LatLng(緯度,経度);
marker.position = newLatLng;
marker.setMap(map);
map.setCenter(newLatLng);
}
};
})();
依存性注入はいつ行われますか?
少し無関係です。実際、依存性注入の概念は依存性反転の原理と混同されることがよくありますが、この違いを明確にするために次のように説明する必要があります。
依存関係の注入は、制御反転の特殊な形式であり、コンポーネントが依存関係を取得する方法を意味します。依存関係の挿入とは、コンポーネントが依存関係を取得するのではなく、依存関係がコンポーネントに提供されることを意味します。これは、依存関係のインスタンスを作成し、ファクトリを通じて依存関係を要求し、サービス ロケーターまたはコンポーネント自体の初期化を通じて依存関係を要求することを意味します。依存関係反転の原則と依存関係の注入はどちらも依存関係に焦点を当てており、反転に使用されます。ただし、依存関係逆転の原則は、コンポーネントが依存関係を取得する方法には焦点を当てておらず、高レベルのモジュールが低レベルのモジュールからどのように分離されるかにのみ焦点を当てています。ある意味、依存性反転の原則は、制御反転の別の形式です。ここで反転されるのは、どのモジュールがインターフェイスを定義するかということです (低レベルの定義から高レベルの定義へ)。
概要
これは 5 つの原則の最後の記事です。この 5 つの記事では、JavaScript でのさまざまな原則をさまざまな観点から説明しました。 (おじさん注:実は、ちょっと地味ではあるものの、別のレベルから見ると、一般原則は実はどの言語でも同じなんじゃないかと感じています。)