1つの方法は、キャッシュinterceptorを使用することです。依存関係を管理するためにUnityまたはWindsorのような依存性注入コンテナを使用すると、そこからいくつかの追加値を得ることができます。
あなたはこのインタフェースと実装を持っている画像をダウンロードするための仮定:
public interface IImageDownloader
{
BitmapImage GetImage(Uri uri);
}
public class ImageDownloader : IImageDownloader
{
public BitmapImage GetImage(Uri uri)
{
// Your implementation
return null;
}
}
あなたはIImageDownloader
を必要とするときImageDownloader
のインスタンスを作成する(例として、ユニティを使用して)あなたのコンテナを伝えることができます。
container.RegisterType<IImageDownloader, ImageDownloader>();
これまでのところ、これはキャッシングとは関係ありません。それは、他のクラスが具体的なクラスの代わりにインターフェース(IImageDownloader
)に依存するように、依存性注入を使用しているだけです。
しかし、あなたは傍受の追加の利点を得る。コンテナは、クラスの周りの他の振る舞いを「ラップ」することができます。ここでthis documentationに基づく例です:
今
public class ImageDownloadCacheInterceptionBehavior : IInterceptionBehavior
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Uri uriArgument = (input.Arguments.Count > 0) ? input.Arguments[0] as Uri : null;
if (uriArgument == null)
{
return getNext()(input, getNext);
}
var key = CreateKey(uriArgument);
using (var cache = MemoryCache.Default)
{
var cached = cache[key] as BitmapImage;
if (cached == null)
{
cached = getNext()(input, getNext).ReturnValue as BitmapImage;
if(cached!=null)
cache.Add(key, cached, DateTimeOffset.Now.AddHours(1));
}
return input.CreateMethodReturn(cached);
}
}
private string CreateKey(Uri uri)
{
return "ImageCache" + uri;
}
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public bool WillExecute => true;
}
インターセプターを使用するようにUnityに伝えるために:
container.AddNewExtension<Interception>();
container.RegisterType<IImageDownloader, ImageDownloader>(
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<ImageDownloadCacheInterceptionBehavior>()
);
これは単なる典型的なキャッシュ動作です。 Uri
からキャッシュキーを作成していて、そのアイテムがキャッシュにあればそれが返されます。そうでない場合は、呼び出されているメソッドを呼び出し続け、完了したら戻り値をキャッシュに戻してから返します。
この作業の主な利点は、キャッシングの動作がImageDownloader
クラスにないことです。そのクラスには1つの仕事があります - イメージをダウンロードする - それは簡単で簡単にテストできます。その方法でキャッシングを実行しなければならない場合は、より長く複雑になります。インターセプトでは、に属していないクラスに振る舞いを追加して、クラスに振り分けることができます。
たとえば、いくつかのケースではキャッシュしたいが他のケースではキャッシュしない場合はどうすればよいでしょうか?あなたは、bool shouldCache
またはTimeSpan cacheDuration
のようなパラメータを使ってメソッドに奇妙なオーバーロードを書き込むことになります。
このようにしても、キャッシュの動作は複雑ですが、別々にしておくことができます。
もう1つのアプリケーションは例外処理です。すべてのクラスに独自のtry/catch/log
メソッドがある場合は、苦痛を伴うことがあります。しかし、その例外処理をそのままにして、あなたのクラスに自分のことをさせることができます。インターセプタは例外を捕捉してログに記録できます。あなたがそれを使用し始めると、あなたはそれが動作するように取得しているとき
このすべては、単に余分な作業の多くのように思えます。しかし、一度あなたがコードを複雑にするのを助けるために奇跡を働かせることができます。
最初の課題は、特定のタイプのアプリケーションで依存性注入コンテナを使用する方法を学習することです。 Here's an articleをWPFアプリケーションで設定してください。
組み込み依存関係注入または依存関係注入との互換性を宣言しているフレームワーク(ASP.NET Core、Angular)の数を確認すると、パターンの値が明らかになります。
BitmapImageまたはBitmapFrameのインスタンスをキャッシュする簡単なバインディングコンバーター(string/UriからImageSourceへ)はどうですか? – Clemens