Hãy lùi lại một bước và nhìn vào bức tranh lớn hơn ở đây.
Là gì IDatabase
's trách nhiệm?
Nó có một vài hoạt động khác nhau:
- Phân tích chuỗi kết nối
- Mở kết nối với cơ sở dữ liệu (hệ thống bên ngoài)
- Gửi tin nhắn đến cơ sở dữ liệu; các thông báo ra lệnh cho cơ sở dữ liệu thay đổi trạng thái của nó
- Nhận phản hồi từ cơ sở dữ liệu và chuyển đổi chúng thành định dạng mà người gọi có thể sử dụng
- Đóng kết nối
Nhìn vào danh sách này, bạn có thể nghĩ, "Điều này có vi phạm SRP không?" Nhưng tôi không nghĩ rằng nó làm. Tất cả các hoạt động là một phần của một khái niệm gắn kết, duy nhất: quản lý một kết nối trạng thái với cơ sở dữ liệu (một hệ thống bên ngoài) . Nó thiết lập kết nối, nó theo dõi trạng thái hiện tại của kết nối (đặc biệt là liên quan đến các hoạt động được thực hiện trên các kết nối khác), nó báo hiệu khi nào thực hiện trạng thái hiện tại của kết nối, v.v. Theo nghĩa này, nó hoạt động như một API ẩn nhiều chi tiết triển khai mà hầu hết người gọi sẽ không quan tâm. Ví dụ: nó có sử dụng HTTP, ổ cắm, đường ống, TCP, HTTPS tùy chỉnh không? Gọi mã không quan tâm; nó chỉ muốn gửi tin nhắn và nhận được phản hồi. Đây là một ví dụ tốt về đóng gói.
Chúng tôi có chắc không Chúng ta không thể tách ra một số các hoạt động này? Có thể, nhưng không có lợi ích. Nếu bạn cố tách chúng ra, bạn vẫn sẽ cần một đối tượng trung tâm giữ kết nối mở và / hoặc quản lý trạng thái hiện tại. Tất cả các hoạt động khác được kết hợp chặt chẽ với cùng một trạng thái và nếu bạn cố tách chúng ra, cuối cùng chúng sẽ ủy thác trở lại đối tượng kết nối. Các hoạt động này được kết hợp một cách tự nhiên và hợp lý với nhà nước và không có cách nào để tách chúng ra. Decoupling là tuyệt vời khi chúng ta có thể làm điều đó, nhưng trong trường hợp này, chúng ta thực sự không thể. Ít nhất không phải không có một giao thức không trạng thái rất khác để nói chuyện với DB và điều đó thực sự sẽ khiến các vấn đề rất quan trọng như tuân thủ ACID trở nên khó khăn hơn nhiều. Ngoài ra, trong quá trình cố gắng tách các hoạt động này khỏi kết nối, bạn sẽ buộc phải tiết lộ chi tiết về giao thức mà người gọi không quan tâm, vì bạn sẽ cần một cách gửi một loại tin nhắn "tùy ý" đến cơ sở dữ liệu.
Lưu ý rằng thực tế chúng ta đang xử lý một giao thức trạng thái khá nghiêm ngặt quy định thay thế cuối cùng của bạn (truyền chuỗi kết nối dưới dạng tham số).
Chúng ta có thực sự cần chuỗi kết nối được thiết lập không?
Đúng. Bạn không thể mở kết nối cho đến khi bạn có chuỗi kết nối và bạn không thể làm gì với giao thức cho đến khi bạn mở kết nối. Vì vậy, thật vô nghĩa khi có một đối tượng kết nối mà không có một đối tượng.
Làm thế nào để chúng ta giải quyết vấn đề yêu cầu chuỗi kết nối?
Vấn đề chúng tôi đang cố gắng giải quyết là chúng tôi muốn đối tượng luôn ở trạng thái có thể sử dụng được. Loại thực thể nào được sử dụng để quản lý trạng thái trong các ngôn ngữ OO? Đối tượng , không phải giao diện. Giao diện không có nhà nước để quản lý. Bởi vì vấn đề bạn đang cố gắng giải quyết là vấn đề quản lý nhà nước, giao diện không thực sự phù hợp ở đây. Một lớp trừu tượng là tự nhiên hơn nhiều. Vì vậy, sử dụng một lớp trừu tượng với một nhà xây dựng.
Bạn cũng có thể muốn xem xét thực sự mở kết nối trong khi xây dựng, vì kết nối cũng vô dụng trước khi nó được mở. Điều đó sẽ yêu cầu một protected Open
phương thức trừu tượng vì quá trình mở kết nối có thể là cơ sở dữ liệu cụ thể. Nó cũng là một ý tưởng tốt để làm cho thuộc ConnectionString
tính chỉ đọc trong trường hợp này, vì thay đổi chuỗi kết nối sau khi kết nối mở sẽ là vô nghĩa. (Thành thật mà nói, tôi sẽ làm cho nó chỉ đọc bằng mọi cách. Nếu bạn muốn kết nối với một chuỗi khác, hãy tạo một đối tượng khác.)
Chúng ta có cần một giao diện nào không?
Giao diện chỉ định các tin nhắn khả dụng bạn có thể gửi qua kết nối và các loại phản hồi bạn có thể nhận lại có thể hữu ích. Điều này sẽ cho phép chúng ta viết mã thực thi các hoạt động này nhưng không được kết hợp với logic mở kết nối. Nhưng đó là vấn đề: quản lý kết nối không phải là một phần của giao diện, "Tôi có thể gửi tin nhắn nào và tôi có thể lấy lại tin nhắn nào từ / cơ sở dữ liệu?", Vì vậy, chuỗi kết nối thậm chí không phải là một phần của điều đó giao diện.
Nếu chúng ta đi theo lộ trình này, mã của chúng ta có thể trông giống như thế này:
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
}