Để dễ hình dung ta có thể xét một mô hình ứng dụng đƣợc thiết kế theo mô hình 3-tầng nhƣ hình dƣới. Tầng DL lƣu trữ và truy xuất dữ liệu, đƣợc chia thành hai phần: DataAccess và DataStorage:
DataStorage: lƣu trữ dữ liệu và thực hiện các dịch vụ của hệ quản trị cơ sở dữ liệu nhƣ SQL Server, Oracle, DB2, MySQL, …
DataAccess: gồm các đối tƣợng tác nghiệp (Business Object) và đối tƣợng kết nối, truy cập dữ liệu. Business Object cung cấp dịch vụ từ tầng DL cho tầng trên, nhƣ các dịch vụ: chèn, cập nhật, xóa dữ liệu.
Tầng PL và DL độc lập nhau, trao đổi với nhau qua tầng BL. Do đó, việc thay đổi hệ quản trị cơ sở dữ liệu SQL Server thành Oracle, Oracle thành DB2,… hay ngƣợc lại thì giao diện chƣơng trình vẫn không thay đổi.
Trong cài đặt khung cho tầng truy cập dữ liệu tổng quát, ta xây dựng các Business Object dựa trên đối tƣợng ADO.Net để cung cấp dịch vụ cho tầng BL và bảo đảm sự độc lập giữa hai tầng PL và DL.
Có một cách là xây dựng một lớp dịch vụ có phƣơng thức chứa điều khiển switch để biết đƣợc kiểu của điều khiển truy cập cơ sở dữ liệu nào đƣợc thực hiện, rồi cài đặt mã tƣơng tác tƣơng ứng. Giả sử phƣơng thức này đƣợc cài đặt nhƣ sau :
public override void MyProcedure(proType ProviderType){ switch(proType){ case ProviderType.SqlClient: objSqlCommand.SqlProcedure () break; case ProviderType.OleDb: objOleDbCommand.QleDbProcedure()
103 break; default: … break; } }
Cách thiết kế và cài đặt nhƣ trên có một số nhƣợc điểm: mã lệnh cài đặt nhiều do phải viết lại những đoạn mã tƣơng tự nhau, phức tạp và đôi khi là không cần thiết. Mặt khác khi muốn truy cập cơ sở dữ liệu mới thì phải cài đặt và biên dịch lại lớp này. Giải quyết vấn đề này bằng cách sử dụng các mẫu Factory Method, Singleton và Null Object để cài đặt một Framework cho tầng truy cập dữ liệu tổng quát. Biểu đồ lớp cho bài toán đƣợc trình bày trong hình dƣới, trong đó, lớp DataAccessBaseClass và các lớp kế thừa nó là các Business Object chứa các dịch vụ cung cấp cho tầng BL tƣơng tác với cơ sở dữ liệu.
Hình 4.18. Biểu đồ lớp của tầng truy cập dữ liệu
Lớp DataAccessBaseClass là lớp Product gồm các thuộc tính và phƣơng thức tổng quát để tƣơng tác với các hệ cơ sở dữ liệu, các phƣơng thức này là các phƣơng thức đƣợc nạp chồng.
Các lớp OdbcDataAccess, OleDbDataAccess, OracleDataAccess, SqlDataAccess, NullObject (là các lớp ConcreteProduct) đƣợc kế thừa từ lớp DataAccessBaseClass và cài đặt chi tiết phƣơng thức GetDataProviderConnection() (trả về đối tƣợng chuỗi kết nối đến hệ cơ sở dữ liệu tƣơng ứng) và GetDataProviderDataAdapter() (tạo một Adapter ứng với hệ cơ sở dữ liệu nào đó). Các lớp này đƣợc xây dựng dựa theo mẫu Singleton để đảm bảo tính duy nhất của bản
104
thể. Lớp NullObject cài đặt phƣơng thức thực thi các ứng xử mặc định ngoài các hành vi của các đối tƣợng của các lớp còn lại.
Phƣơng thức GetDataAccessLayer() là phƣơng thức static đƣợc nạp chồng, nhận một tham số có kiểu ProviderType (gồm một trong các giá trị : Odbc, OleDb, Oracle, Sql) để quyết định đối tƣợng ứng với điều khiển truy cập dữ liệu (Data Provider) nào sẽ đƣợc tạo ra. Nội dung của lớp chứa phƣơng thức này nhƣ sau :
public class ConcreteCreator : Creator{ private ConcreteCreator() {}
//Lấy giá trị được thiết lập trong tệp tin cấu hình
private static string GetAppSetting(string setting){ string val;
try {
val = System.Configuration.ConfigurationSettings. AppSettings[setting].ToString();
}
catch (NullReferenceException e){ val = ""; } if (val == null) val = ""; return val; }
/*Lấy chuỗi kết nối database bằng cách đọc trong tệp tin cấu hình (app.config)*/
private static string GetConnectionString(){ string val;
val = "server=" + GetAppSetting("Datasource") + ";database=" + GetAppSetting("Database") +
";uid=" + GetAppSetting("Userid") + ";pwd=" + GetAppSetting("Password") + ((GetAppSetting("Timeout").Length > 0) ?
";Connection Timeout=" + GetAppSetting("Timeout"): ""); return val;
}
/*Xây dựng một data provider của tầng truy cập dữ liệu dựa trên các thiết lập cấu hình ứng dụng. Tập tin cấu hình ứng dụng phải chứa 2 key:
1. "DataProviderType" key : giá trị của nó là một trong các giá trị được định nghĩa (sql,oracle,access,odbc,oledb)
2. "ConnectionString" key : chuỗi kết nối database*/
public static DataAccessBaseClass GetDataAccessLayer(){ if (GetAppSetting("DataProviderType") == null
||GetAppSetting("Datasource") == null || GetAppSetting("Database") == null)
105
throw new ArgumentNullException("Chưa chỉ định
'DataProviderType' hoặc chưa chỉ định 'Server' hoặc chưa chỉ định 'Database' trong tệp tin cấu hình");
DataProviderType dataProvider; try{ dataProvider= (DataProviderType)System.Enum.Parse(typeof(DataProviderTy pe),GetAppSetting("DataProviderType")); } catch(Exception e){
throw new ArgumentException("Kiểu provider cho tầng truy cập dữ liệu không hợp lệ.");
}
return getDataAccessLayer(dataProvider,GetConnectionString()); }
/*Xây dựng một data provider của tầng truy cập dữ liệu dựa Provider được cung cấp. Chuỗi connection được lấy từ thuộc tính ConnectionString của lớp DataAccessBaseClass*/
public static DataAccessBaseClass
GetDataAccessLayer(DataProviderType dataProviderType){ return GetDataAccessLayer(dataProviderType, null); }
/*Xây dựng một data provider của tầng truy cập dữ liệu dựa Provider và chuỗi connection được cung cấp*/
public static DataAccessBaseClass
GetDataAccessLayer(DataProviderType dataProviderType, string connectionString) {
switch (dataProviderType){
case DataProviderType.OleDb:
return new OleDbDataAccess(connectionString); case DataProviderType.Odbc:
return new OdbcDataAccess(connectionString); case DataProviderType.Oracle:
return new OracleDataAccess(connectionString); case DataProviderType.Sql:
return new SqlDataAccess(connectionString); default:
return new NullObject (connectionString); }
}
Để sử dụng Framework này ta chỉ cần gọi phƣơng thức GetDataAccessLayer sử dụng hai tham số DataProviderType và ConnectionString để lấy về đối tƣợng điều khiển thao tác với hệ cơ sở dữ liệu tƣơng ứng. Các tham số đƣợc ngƣời dùng cung cấp khi chạy chƣơng trình hoặc lấy từ tệp cấu hình app.config. Tệp cấu hình đƣợc định dạng dƣới dạng XML và có nội dung nhƣ sau :
<?xml version="1.0" encoding="utf-8" ?> <configuration>
106 <appSettings>
<add key="DataProviderType" value="Sql" /> <add key="Database" value="MyDatabase" /> <add key="Datasource" value="localhost" /> <add key="Userid" value="test" />
<add key="Password" value="123456" /> <add key="Timeout" value="" />
</appSettings> </configuration>
4.4. Tổng kết chƣơng
Vận dụng các nguyên lý và phƣơng pháp thiết kế mẫu phần mềm đã đƣa ra, luận văn mô tả 4 mẫu thiết kế theo định dạng GOF. Đó là các mẫu thiết kế đƣa ra giải pháp cho những vấn đề hay gặp trong phân tích thiết kế hƣớng đối tƣợng. Luận văn cũng đã sử dụng 3 mẫu thiết kế Factory Method, Singleton và Null Object để cài đặt cho tầng truy cập dữ liệu tổng quát, nhƣ một ví dụ cho tính khả dụng và hiệu quả của mẫu thiết kế phần mềm.
Khi các mẫu thiết kế phần mềm đã đƣợc sử dụng rộng rãi và chứng minh tính hiệu quả của nó trong thiết kế phần mềm hƣớng đối tƣợng, đồng thời với sự xuất hiện của thế hệ web 2.0 (dựa trên công nghệ Ajax), ngƣời ta đã ứng dụng và phát triển các mẫu thiết kế cho ứng dụng web trên công nghệ Ajax. Đến nay đã phát triển đƣợc trên 60 mẫu thiết kế Ajax, tuy nhiên vẫn hứa hẹn nhiều tiềm năng phát triển. Đây là một hƣớng nghiên cứu phát triển mới và rất rộng, luận văn chỉ đƣa ra tổng quan về vấn đề này. Việc nghiên cứu sâu hơn xin để cho những quan tâm tiếp sau.