露出可能なパターン。 広がりによる独立した注入

ディスポーザブルパターン( IDisposableインターフェイス)は、インスタンスへのすべての参照が失われ、ガベージコレクターがそれを利用する前であっても、 Disposeメソッドを呼び出すことでオブジェクトが占有するリソースを解放する可能性を意味します(信頼性のために、 Dispose呼び出しはファイナライザーで複製されることがよくあります)。



しかし、オブジェクトへの参照が完全に初期化されるまで利用可能になる場合、逆のExposableパターンもあります。 つまり、インスタンスは既にメモリ内に存在し、部分的に初期化され、他のオブジェクトがそれを参照しますが、最終的に作業の準備をするには、 Exposeメソッドを呼び出す必要があります。 繰り返しますが、この呼び出しはコンストラクターで実行できます。これは、ファイナライザーのDispose呼び出しと正反対です。



それ自体では、そのような逆対称性の存在は美しく自然に見えますが、この記事で実際にこれがどこで役立つかを明らかにしようとします。





参考のために、 C#にはusingディレクティブがあります-Disposeメソッドを安全に呼び出すための構文シュガー。



using(var context = new Context()) { // statements }
      
      





同等に



 var context = new Context(); try { // statements } finally { if (context != null) context .Dispose(); }
      
      





唯一の違いは、最初のケースではコンテキスト変数が読み取り専用になることです。



作業単位+使い捨て可能+暴露可能=再生可能単位



オブジェクトが1回限りの使用を目的としており、その寿命が通常短い場合、 DisposeパターンにはしばしばUnit of Workパターンが付随します。 つまり、それらは作成され、すぐに使用され、すぐに使用中のリソースが解放され、それ以上の使用には適さなくなります。



たとえば、このようなメカニズムは、ORMフレームワークを介してデータベースエンティティにアクセスするためによく使用されます。



 using(var context = new DbContext(ConnectionString)) { persons =context.Persons.Where(p=>p.Age > minAge).ToList(); }
      
      





データベースへの接続が開かれ、必要な要求が行われた後、すぐに閉じられます。 接続を常に開いたままにしておくことは、接続リソースが制限されていることが多く、一定の非アクティブ期間後に接続が自動的に閉じられるため、悪い習慣と見なされます。



すべて順調ですが、負荷が不均一なサーバーがある場合、ピーク時にDbContextオブジェクトのそのようなインスタンスが大量に作成され、ガベージコレクターがより頻繁に呼び出されるため、サーバーが消費するメモリと速度に影響を与え始めます。



ここでは、 DisposableパターンとExposableパターンを一緒に使用すると役立ちます 。 オブジェクトを常に作成および削除する代わりに、1つのオブジェクトを作成して、その中のリソースを占有および解放するだけで十分です。



  context.Expose(); persons = context.Persons.Where(p=>p.Age > minAge).ToList(); context.Dispose();
      
      





もちろん、このコードはExposeメソッドを提供しないため、既存のフレームワークでは機能しませんが、原則そのものを示すことが重要です-オブジェクトを再利用でき、必要なリソースを動的に復元できます。



*コメントで述べたように、パフォーマンスの向上は非常に議論の余地があるため、これはおそらく最良の例ではありません。 しかし、問題のパターンの本質をよりよく理解するために、次の理由を示します。



通常の意味では、 Disposableはオブジェクトの初期化解除と完全な破棄です。 ただし、 Disposeを呼び出した後も、リンクが残る場合があります。 多くの場合、プログラマがこれを提供している場合、ほとんどのプロパティとメソッドへのアクセスは例外をスローしますが、インスタンスをキーとして簡単に使用し、 ToStringEquals、およびその他のメソッドを呼び出すことができます。 使い捨てパターンの理解を広げてみませんか? スリープに必要なリソースが少なくなったら、 Disposeでオブジェクトをスタンバイ状態にします。 しかし、この状態から推測するメソッドが必要です。 すべてが非常に論理的かつ論理的です。 つまり、 Disposableパターンの一般化があり、オブジェクトの拒否のシナリオは彼の特別な場合にすぎません。



露出可能なパターンを介した独立した注入



重要! 以下を完全に理解するには、 Sparrowテキストエディターの例を使用して、 Aero Frameworkライブラリのソースコードバックアップリンク )をダウンロードすることを強くお勧めします。また、以前の一連の記事をよく理解することをお勧めします。



ローカライズの例での拡張バインディングとxamlマークアップ

Xamlコンテキストインジェクター

Xamlアプリケーションでのチーム指向ナビゲーション

xamlの改善:バインド可能なコンバーター、スイッチコンバーター、セット

C#での砂糖の注入

Aeroフレームワークを介したコンテキストモデルパターン



ユニットコンテナを使用してビューモデルをコンストラクタに注入する古典的な方法は次のとおりです。



 public class ProductsViewModel : BaseViewModel { public virtual void ProductsViewModel(SettingsViewModel settingsViewModel) { // using of settingsViewModel } } public class SettingsViewModel : BaseViewModel { public virtual void SettingsViewModel(ProductsViewModel productsViewModel) { // using of productsViewModel } }
      
      





ただし、 SettingsViewModelが作成されるまでProductsViewModelを初期化することはできず、その逆も同様であるため、このようなコードは例外をスローします。



ただし、 Aero FrameworkライブラリでExposable -patternを使用すると、閉じた依存関係の問題をエレガントに解決できます。



 public class ProductsViewModel : ContextObject, IExposable { public virtual void Expose() { var settingsViewModel = Store.Get<SettingsViewModel>(); this[Context.Get("AnyCommand")].Executed += (sender, args) => { // safe using of settingsViewModel } } } public class SettingsViewModel : ContextObject, IExposable { public virtual void Expose() { var productsViewModel = Store.Get<ProductsViewModel>(); this[Context.Get("AnyCommand")].Executed += (sender, args) => { // safe using of productsViewModel } } }
      
      





これにより、状態保存メカニズム(以下で説明するSmart State )により、相互に参照する両方のビューモデルを安全に初期化できます。つまり、独立した直接注入の原理を実装できます。



スマート状態



今、非常に珍しいですが、同時に状態を維持するための便利なメカニズムになります。 Aeroフレームワークを使用すると、この種の問題を解決するために、非常にエレガントで卓越した簡潔な表現が可能になります。



ライブラリアプリケーションの例であるSparrowエディターのデスクトップバージョンを起動します。 以下は、ドラッグまたはサイズ変更(表示状態)できる通常のウィンドウです。 複数のタブを作成したり、テキストファイルを開いたりして、それらのテキストを編集することもできます(論理状態)。



その後、エディターを閉じ(ウィンドウの十字をクリックし)、再度実行します。 プログラムは、閉じられたときと同じ視覚的および論理的な状態で正確に開始されます。つまり、ウィンドウのサイズと位置は同じになり、作業タブは開いたままになり、その中のテキストでさえ閉じたままになります! 一方、一見したところビューモデルのソースコードには、状態を維持するための補助ロジックが含まれていません。



よく見ると、 Sparrowアプリケーションの例のビューモデルはDataContract属性でマークされており、一部のプロパティはDataMember属性でマークされているため、シリアル化および逆シリアル化メカニズムを使用して論理状態を保存および復元できます。



このために必要なことは、アプリケーションの起動中に必要に応じてフレームワークを初期化することだけです。



 Unity.AppStorage = new AppStorage(); Unity.App = new AppAssistent();
      
      





デフォルトでは、シリアル化はファイルで行われますが、独自の実装を簡単に作成し、シリアル化されたオブジェクトをデータベースなどに保存できます。 これを行うには、 Unity.IApplicationインターフェイスから継承する必要があります( AppStorageはデフォルトで実装されています )。 Unity.IApplicationAppAssistent )インターフェースについては、 シリアル化中の文化的設定に必要であり、ほとんどの場合、標準実装に制限できます。



シリアル化をサポートするオブジェクトの状態を保存するには、添付されたスナップショットメソッドを呼び出すか、オブジェクトが共通コンテナーにある場合はStore.Snapshot呼び出しを使用します。



論理的な状態を維持することを考え出しましたが、多くの場合、ウィンドウのサイズと位置、コントロールの状態、その他のパラメーターなど、ストレージとビジュアルが必要です。 このフレームワークは、非標準ですが非常に便利なソリューションを提供します。 このようなパラメーターをコンテキストオブジェクト(ビューモデル)に格納し、シリアル化のための個別のプロパティとしてではなく、暗黙的に、キーが「仮想」プロパティの名前である辞書として格納するとどうなりますか?



この概念に基づいて、 スマートプロパティのアイデアが生まれました。 スマートプロパティの値は、ディクショナリのように、キー名でインデクサーを介してアクセスできる必要があります。従来のgetまたはsetはオプションであり、存在しない場合があります。 この機能はSmartObjectクラスに実装されており、 このクラスからContextObjectが継承され、拡張されます。



デスクトップ版で書くだけです:



 public class AppViewModel : SmartObject // ContextObject {}
      
      





 <Window x:Class="Sparrow.Views.AppView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModels="clr-namespace:Sparrow.ViewModels" DataContext="{Store Key=viewModels:AppViewModel}" WindowStyle="{ViewModel DefaultValue=SingleBorderWindow}" ResizeMode="{Binding '[ResizeMode, CanResizeWithGrip]', Mode=TwoWay}" Height="{Binding '[Height, 600]', Mode=TwoWay}" Width="{ViewModel DefaultValue=800}" Left="{ViewModel DefaultValue=NaN}" Top="{Binding '[Top, NaN]', Mode=TwoWay}" Title="{ViewModel DefaultValue='Sparrow'}" Icon="/Sparrow.png" ShowActivated="True" Name="This"/>
      
      





その後、アプリケーションの終了時にウィンドウのサイズと位置が自動的に保存され、起動時に正確に復元されます! 同意して、これはこの種の問題を解決するための驚くべき簡潔さです。 ビューモデルまたはBechainコードでは、コードを1行追加する必要はありませんでした。



*他のいくつかのxaml-プラットフォームのわずかなニュアンスと制限、およびバイパスする方法については、 Aero Frameworkを介した元の記事Context Model Patternを参照してください。



ポリモーフィズムメカニズムのおかげで、インデクサーも使用するIDataErrorInfoインターフェイスの実装によるプロパティ値の検証は、スマートステートの概念に非常にエレガントに適合します。



まとめ



メイントピックから外れているように見えるかもしれませんが、そうではありません。 この記事と以前の記事で説明したすべてのメカニズムとExposableパターンの使用により、非常にクリーンで簡潔なビューモデルを作成できます。



 public class HelloViewModel : ContextObject, IExposable { public string Message { get { return Get(() => Message); } set { Set(() => Message, value); } } public virtual void Expose() { this[() => Message].PropertyChanged += (sender, args) => Context.Make.RaiseCanExecuteChanged(); this[Context.Show].CanExecute += (sender, args) => args.CanExecute = !string.IsNullOrEmpty(Message); this[Context.Show].Executed += async (sender, args) => { await MessageService.ShowAsync(Message); }; } }
      
      





つまり、複数のプロパティと1つのExposeメソッドのみがビューモデルで宣言され、残りの機能はラムダ式で記述されることが簡単に起こります。 さらに継承が計画されている場合は、メソッドを仮想修飾子でマークするだけです。



All Articles