マーク・シマンによる.Netへの依存性注入3-アプリケーション、インターセプト、デコレーターの横断的側面

アプリケーション層間の依存関係 | コンストラクター実装、ライフタイム | アプリケーション、インターセプト、デコレータの横断的な側面

以前の2つのノートでは、Webアプリケーションの主要部分を見てきました。 ビジネスロジックを実装するオブジェクト-MyServiceがあります。 データベースとのやり取りを担当するIRepositoryがあります。 十分なロールモデルとロギングがありません。

デコレータ


MVC Webアプリケーションでは、コントローラーメソッドの開始時に権利を確認するのが便利であると考えられています。 例:

[HttpPost] public void DeleteProduct(int id) { if (!Thread.CurrentPrincipal.IsInRole("ProducManager") throw new UnauthorizedAccessException(); this.MyService.DeleteProduct(id); }  1.      

または属性を使用して

 [PrincipalPermission(SecurityAction.Demand,Role = "ProductManager")] [HttpPost] public void DeleteProduct(int id) { this.MyService.DeleteProduct(id); }  2.         

このアプローチには利点があります。 それらの中で最も明らかなことは、権利チェックがMyServiceから取られていることです。 ビジネスロジックのコアはそのタスクのみを実行し、権限のチェックには関与しません。 「科学的に人気のある」言語では、単一責任原則(SOLID)に従います。

しかし、ロールモデルはビジネスロジックの一部ではありませんか? さらに、より複雑なチェックがあります。 たとえば、ProducManagersはすべての製品に対して責任を負うのではなく、製品の特定のカテゴリなどに対してのみ責任を負います。 このようなチェックをコントローラーに配置するのは良くありません。 MyServiceでは、すでにわかっているように、どちらも属していません。 どうする?

デコレータパターンが役に立ちます。

私たちはすでに同意しました


リスト3のコントローラーコードは両方のポイントを満たします。

 public class MyController { private readonly IMyService MyService; public class MyController(IMyService service) { if(service == null) throw new ArgumentNullException(nameof(service)); MyService = service } [HttpPost] public void DeleteProduct(int id) { this.MyService.DeleteProduct(id); } }  3. MyController  IMyService 

MySecurityService:IMyServiceデコレーター(リスト4)を作成できます。これには、権限チェックがあります。 また、コードはLisk置換の原則に従っているため、コントローラーで使用してください。 この場合、MyControllerで何かを変更する必要はありません。
ソリッドのリスコフ置換原理(リスコフは姓です)

クライアントは、抽象化のすべての実装を同等と見なす必要があります。 消費者を破壊することなく、ある実装を別の実装に置き換えることができなければなりません。


 class MySecurityService : IMyService { private readonly IMyService MyService; private readonly IUserInfo UserInfo; public MySecurityService(IMyService service, IUserInfo userInfo) { if(service == null) throw new ArgumentNullException(nameof(service)); if(userInfo == null) throw new ArgumentNullException(nameof(userInfo)); MyService = service; UserInfo = userInfo; } public void DeleteProduct(int id) { if(UserInfo.IsInRole("ProductManager")) throw new UnauthorizedAccessException(nameof(service)); this.MyService.DeleteProduct(id); } }  4.  MySecurityService           DeleteProduct. 

または、属性付き:

 class MySecurityService : IMyService { private readonly IMyService MyService; public MySecurityService(IMyService service) { if(service == null) throw new ArgumentNullException(nameof(service)); MyService = service; } [PrincipalPermission(SecurityAction.Demand,Role = "ProductManager")] public void DeleteProduct(int id) { this.MyService.DeleteProduct(id); } }  5.  MySecurityService    

MyServiceでPrincipalPermission属性を直接使用しないでください。 MyServiceが権利検証の特定の実装に関連付けられない場合が望ましい。

同様に、デコレータを使用して、ロギングとエラー処理を追加できます。 以下は、完全な機能のアセンブリの例(理解のみ)です。デコレータは相互にネストされています。

 IMyService service = new MyService(...); service = new MySecurityService(service, ...); service = new MyLoggerService(service, ...); service = new MyExceptionSaveService(service, ...);  6.     .  . 

デコレータは異なるプロジェクト(異なるアセンブリ)に配置できることに注意してください。 たとえば、Webアプリケーションとモバイルクライアントに別々のエラー処理デコレータを設定できます。

リトリート:

文献では、「傍受」という用語を見つけることができます。 リスト1と2は、本質的に「インターセプション」を実装しています。これらは、制御を取り、何かを実行し、メインコードに制御を与えます。 デコレータは、インターセプトも実装します。

ロギング、セキュリティ、キャッシュなど、アプリケーションの多くの機能に適用されるプログラムの一部 「アスペクト」または「横断的アスペクト」と呼ばれます。

インタビューで、ロギング/キャッシング/権利の検証について尋ねられた場合、次のようなフレーズをまとめることができます。Decoratorパターンに基づいたインターセプトを使用して、アプリケーションの横断的な側面を実装する方が良いと思います。 もちろん、このために、コードではバーバラリスコフを置き換えるという原則を守らなければなりません。

おわりに


典型的なWebアプリケーション開発タスクに注目しました。

-アプリケーションをレイヤーに分割する方法
-クラス間の相互作用を整理する方法
-アプリケーションの横断的な側面を実装する方法

「依存性注入」というトピックをよりよく理解するために、アイライナーを作成しました。 IocコンテナのOT接続を使用する必要がある理由を知ることは、技術の問題になります。

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


All Articles