Bir adım geriye gidelim ve buradaki büyük resme bakalım.
Nedir IDatabase
bireyin sorumluluk?
Birkaç farklı işlemi vardır:
- Bir bağlantı dizesini ayrıştırma
- Veritabanıyla bağlantı açma (harici bir sistem)
- Veritabanına mesaj gönderme; mesajlar veritabanına durumunu değiştirmek için komut verir
- Veritabanından yanıtlar alın ve bunları arayanın kullanabileceği bir formata dönüştürün
- Bağlantıyı kapatın
Bu listeye baktığınızda, "Bu SRP'yi ihlal etmiyor mu?" Diye düşünüyor olabilirsiniz. Ama sanmıyorum. Tüm işlemler tek ve uyumlu bir kavramın parçasıdır: veritabanına durumsal bir bağlantıyı yönetme (harici bir sistem) . Bağlantıyı kurar, bağlantının mevcut durumunu izler (özellikle diğer bağlantılarda yapılan işlemlerle ilgili olarak), bağlantının geçerli durumunu ne zaman gerçekleştireceğini vb. Gösterir. Bu anlamda bir API görevi görür çoğu arayanın umursamayacağı birçok uygulama ayrıntısını gizler. Örneğin, HTTP, soketler, borular, özel TCP, HTTPS kullanıyor mu? Telefon kodu umursamıyor; sadece mesaj göndermek ve yanıt almak istiyor. Bu kapsülleme için iyi bir örnektir.
Emin miyiz? Bu operasyonların bazılarını ayıramadık mı? Belki, ama faydası yok. Onları bölmeye çalışırsanız, bağlantıyı açık tutan ve / veya mevcut durumun ne olduğunu yöneten merkezi bir nesneye ihtiyacınız olacaktır. Diğer tüm işlemler aynı duruma güçlü bir şekilde bağlanır ve bunları ayırmaya çalışırsanız, yine de bağlantı nesnesine tekrar temsilci atacaklardır. Bu işlemler doğal ve mantıksal olarak devlete bağlıdır ve bunları ayırmanın bir yolu yoktur. Ayırma yapabildiğimiz zaman harika, ama bu durumda aslında yapamayız. En azından DB ile konuşmak için çok farklı, vatansız bir protokol olmadan ve bu aslında ACID uyumluluğu gibi çok önemli sorunları çok daha zor hale getirir. Ayrıca, bu işlemleri bağlantıdan ayırmaya çalışırken, arayanların umursamadığı protokolle ilgili ayrıntıları ortaya çıkarmak zorunda kalacaksınız, çünkü bir çeşit "keyfi" mesaj göndermenin bir yoluna ihtiyacınız olacak veritabanına.
Durum bilgisi olan bir protokolle uğraştığımızın son alternatifinizi (bağlantı dizesini parametre olarak geçirme) oldukça katı bir şekilde dışladığını unutmayın.
Gerçekten ayarlamak için bağlantı dizesine ihtiyacımız var mı?
Evet. Sen olamaz açmak Eğer bir bağlantı dizesi kadar bağlantı ve bağlantı açmak kadar protokolü ile bir şey yapamaz. Bu yüzden , bir tane olmadan bir bağlantı nesnesine sahip olmak anlamsızdır .
Bağlantı dizesini gerektirme sorununu nasıl çözeriz?
Çözmeye çalıştığımız sorun, nesnenin her zaman kullanılabilir bir durumda olmasını istiyoruz. OO dillerinde devleti yönetmek için ne tür bir varlık kullanılır? Nesneler , arayüzler değil. Arayüzlerin yönetilecek durumu yoktur. Çözmeye çalıştığınız sorun bir devlet yönetimi sorunu olduğundan, burada bir arayüz gerçekten uygun değil. Soyut bir sınıf çok daha doğaldır. Yani bir kurucu ile soyut bir sınıf kullanın.
Bağlantı açılmadan önce de işe yaramadığından, kurucu sırasında bağlantıyı gerçekten açmayı da düşünebilirsiniz . protected Open
Bir bağlantı açma işlemi veritabanına özgü olabileceğinden, bu soyut bir yöntem gerektirir . ConnectionString
Bağlantıyı açtıktan sonra bağlantı dizesini değiştirmek anlamsız olacağından , özelliğin yalnızca bu durumda okunması da iyi bir fikirdir . (Dürüst olmak gerekirse, ben yine de onu okutmak istiyorum. Farklı bir dize ile bağlantı istiyorsanız, başka bir nesne yapın.)
Arayüze ihtiyacımız var mı?
Bağlantı üzerinden gönderebileceğiniz mevcut iletileri ve geri alabileceğiniz yanıt türlerini belirten bir arabirim yararlı olabilir. Bu, bu işlemleri yürüten ancak bir bağlantı açma mantığına bağlı olmayan kod yazmamızı sağlayacaktır. Ama asıl nokta: bağlantıyı yönetmek, "Hangi mesajları gönderebilirim ve veritabanına / mesajdan hangi mesajları geri alabilirim?" Arayüzünün bir parçası değildir, bu nedenle bağlantı dizesi bunun bir parçası bile olmamalıdır arayüz.
Bu rotaya gidersek, kodumuz şöyle görünebilir:
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
}