一歩戻って、ここでより大きな画像を見てみましょう。
IDatabase
の責任は何ですか?
いくつかの異なる操作があります。
- 接続文字列を解析する
- データベース(外部システム)との接続を開く
- データベースにメッセージを送信します。メッセージはデータベースにその状態を変更するように命令します
- データベースから応答を受信し、呼び出し元が使用できる形式に変換します
- 接続を閉じる
このリストを見ると、「これはSRPに違反していないのか?」しかし、私はそうは思わない。すべての操作は、データベース(外部システム)へのステートフル接続を管理する単一のまとまりのある概念の一部です。接続を確立し、接続の現在の状態(特に他の接続で行われた操作に関連して)を追跡し、接続の現在の状態をいつコミットするかを通知します。この意味で、APIとして機能しますほとんどの呼び出し元が気にしない多くの実装の詳細を隠します。たとえば、HTTP、ソケット、パイプ、カスタムTCP、HTTPSを使用していますか?コードの呼び出しは気にしません。メッセージを送信して応答を取得するだけです。これはカプセル化の良い例です。
よろしいですか?これらの操作のいくつかを分割できませんでしたか?たぶん、しかし利点はありません。それらを分割しようとすると、接続を開いたままにするか、現在の状態を管理する中央オブジェクトが必要になります。他のすべての操作は同じ状態に強く結合されており、それらを分離しようとすると、いずれにしても接続オブジェクトに戻って委任されることになります。これらの操作は自然かつ論理的に状態に結合されており、それらを分離する方法はありません。デカップリングはできれば素晴らしいのですが、この場合、実際にはできません。少なくとも、DBと通信するためのまったく異なるステートレスプロトコルがないと、実際にはACIDコンプライアンスなどの非常に重要な問題がはるかに難しくなります。また、これらの操作を接続から切り離そうとすると、何らかの「任意の」メッセージを送信する方法が必要になるため、発信者が気にしないプロトコルの詳細を公開する必要があります。データベースに。
ステートフルプロトコルを扱っているという事実は、最後の選択肢(接続文字列をパラメーターとして渡す)をかなりしっかりと除外していることに注意してください。
接続文字列を設定する必要は本当にありますか?
はい。あなたはできません開く接続文字列を持つまでの接続を、そしてあなたは、接続をオープンするまでは、プロトコルと何もできません。そのため、接続オブジェクトがなくても接続オブジェクトを使用しても意味がありません。
接続文字列を要求する問題をどのように解決しますか?
解決しようとしている問題は、オブジェクトを常に使用可能な状態にすることです。オブジェクト指向言語で状態を管理するためにどのようなエンティティが使用されていますか?インターフェースではなくオブジェクト。インターフェイスには管理する状態がありません。解決しようとしている問題は状態管理の問題であるため、ここではインターフェイスは実際には適切ではありません。抽象クラスははるかに自然です。そのため、コンストラクターで抽象クラスを使用します。
また、接続も開かれる前は役に立たないため、コンストラクターで実際に接続を開くことを検討することもできます。protected Open
接続を開くプロセスはデータベース固有であるため、抽象メソッドが必要になります。ConnectionString
接続が開かれた後に接続文字列を変更しても意味がないため、この場合はプロパティを読み取り専用にすることをお勧めします。(正直なところ、とにかく読み取り専用にします。別の文字列との接続が必要な場合は、別のオブジェクトを作成します。)
インターフェースが必要ですか?
接続を介して送信できる利用可能なメッセージと返送できる応答の種類を指定するインターフェイスが役立つ場合があります。これにより、これらの操作を実行するコードを記述できますが、接続を開くロジックには結合されません。しかし、それがポイントです:接続を管理することは、「どのメッセージを送信でき、どのメッセージをデータベースとやり取りできますか?」のインターフェースの一部ではないため、接続文字列はその一部であってはなりませんインターフェース。
このルートに進むと、コードは次のようになります。
interface IDatabase {
void ExecuteNoQuery(string sql);
void ExecuteNoQuery(string[] sql);
//Various other methods all requiring ConnectionString to be set
}
abstract class ConnectionStringDatabase : IDatabase {
public string ConnectionString { get; }
public Database(string connectionString) {
this.ConnectionString = connectionString;
this.Open();
}
protected abstract void Open();
public abstract void ExecuteNoQuery(string sql);
public abstract void ExecuteNoQuery(string[] sql);
//Various other methods all requiring ConnectionString to be set
}