概要
遅延デコレータ(Lazy Decorator)パターンを適用し、データベース接続のインスタンス生成を遅延させる方法について説明する。
遅延デコレータパターンは、リソースの節約やパフォーマンスの向上が期待できるデザインパターン。
書籍AdaptiveCodeの7章で登場するが、書籍で紹介されているコードをクラス図にした場合と同じ構造になるように例示した。
また遅延評価で用いたLazy<T>についても解説する。
参照コード
GPT4で出力させたものに、投稿主がコメントを入れたもの
using System;
using System.Data.SqlClient;
public interface IDatabaseConnection
{
void Connect();
}
//具象Component
public class SqlDatabaseConnection : IDatabaseConnection
{
private readonly string _connectionString;
public SqlDatabaseConnection(string connectionString)
{
_connectionString = connectionString;
}
public void Connect()
{
// NOTE: サンプルコードなので適切な設定が必要
using (SqlConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
Console.WriteLine("Connection opened successfully.");
// ここでデータベース操作を行う
}
}
}
//遅延コンポーネント(遅延デコレータ)
public class LazySqlDatabaseConnection : IDatabaseConnection
{
private readonly Lazy<IDatabaseConnection> _lazyConnection;
public LazySqlDatabaseConnection(Lazy<IDatabaseConnection> lazyConnection)
{
_lazyConnection = lazyConnection;
}
public void Connect()
{
_lazyConnection.Value.Connect();
}
}
public class DatabaseClient
{
private readonly IDatabaseConnection _connection;
public DatabaseClient(IDatabaseConnection connection)
{
_connection = connection;
}
public void Run()
{
_connection.Connect();
}
}
class Program
{
static void Main()
{
string connectionString = "your_connection_string_here";
var lazyConnection = new Lazy<IDatabaseConnection>(() => new SqlDatabaseConnection(connectionString));
var lazyConnectionInstance = new LazySqlDatabaseConnection(lazyConnection);
var client = new DatabaseClient(lazyConnectionInstance);
client.Run();
}
}
Connection opened successfully.
クラス図
解説
効果
DatabaseClientは、遅延デコレータパターンを用いることで、Lazy<IDatabaseConnection>でなく、標準的なインターフェイスであるIDatabaseConnectionを参照できている。
特徴
他のデコレータパターンと違いとしてデコレータが直接IDatabaseConnection型を参照するのでなく、Lazy<IDatabaseConnection>型を参照している点。
Lazy<T>について下記項目で説明する。
Lazy<T>
Lazy<T> は、.NET Frameworkおよび.NET Coreにおいて、遅延評価するためのクラス。遅延評価は、オブジェクトのインスタンス生成を実際にその値が必要になるまで遅らせることで、パフォーマンスの向上やリソースの節約に役立つ。
基本的な使い方
// 1. Lazy<T> のインスタンスを作成します。ここで、T は遅延評価するオブジェクトの型です。
// 以下の例では、高コストなオブジェクト ExpensiveObject のインスタンス生成を遅延させます。
Lazy<ExpensiveObject> lazyExpensiveObject = new Lazy<ExpensiveObject>();
// 2. 実際にオブジェクトの値が必要になったとき、Value プロパティを使用してアクセスします。
// これにより、オブジェクトのインスタンスが初めて生成されます。
ExpensiveObject expensiveObject = lazyExpensiveObject.Value;
// 3. 以降、Value プロパティにアクセスするたびに、同じインスタンスが返されます。
// オブジェクトは一度だけ生成され、キャッシュされます。
ExpensiveObject sameExpensiveObject = lazyExpensiveObject.Value;
上記のコードはLazy<T>を使うことで、遅延デコレータを使わなくても遅延実行を実現している。
Func<T> 型のデリゲートを渡す
Lazy<T> を使用するときに、オブジェクトの生成方法をカスタマイズできる。そのためには、Lazy<T> のコンストラクタに Func<T> 型のデリゲートを渡す。このデリゲートは、オブジェクトが実際に必要になったときに呼び出され、インスタンスの生成方法を定義する。
// オブジェクトの生成方法をカスタマイズする例
Func<ExpensiveObject> objectFactory = () => new ExpensiveObject(/* 任意の引数 */);
Lazy<ExpensiveObject> customLazyExpensiveObject = new Lazy<ExpensiveObject>(objectFactory);
ExpensiveObject customExpensiveObject = customLazyExpensiveObject.Value;
参照コードのケース
string connectionString = "your_connection_string_here";
var lazyConnection = new Lazy<IDatabaseConnection>(() => new SqlDatabaseConnection(connectionString));
var lazyConnectionInstance = new LazySqlDatabaseConnection(lazyConnection);
var client = new DatabaseClient(lazyConnectionInstance);
LazySqlDatabaseConnectionのコンストラクタにLazy<SqlDatabaseConnection>型でなくLazy<IDatabaseConnection>型のインスタンスを渡す必要があるので、 Func 型のデリゲートにする必要がある。
ユースケース
ユースケース
例えばメインページを開くときは、重たい処理を入れたくない場合が多いと思う。
そこでメインページを開くときにはコンストラクタでデータベースコネクションのインスタンスだけフィールドに渡しておき、それぞれ対応するユーザーアクションがあったときに実行する、といったケースで使えそう。
追加しやすい
遅延デコレータは既存のクラス(IDatabaseConnectionインターフェースやSqlDatabaseConnectionクラス)に影響を与えずに追加できるので、使い勝手もよさそう。