Friday 16 December 2011

Quick Run-through with Bulk Drop Stored Procedures

Why bother with seeking optimized solution which you know that it is just one time deal? Let's use the CURSOR to perform loop and exec SQL one by one. It is long running task but efficient since it's never used again after that. Here is such a simple secret:

declare @name varchar(max)
declare dcursor CURSOR
FOR Select name from sys.procedures Where [type] = 'P' and is_ms_shipped = 0 and [name] not like 'sp[_]%diagram%' and name like 'xxx_%'

open dcursor
fetch next FROM dcursor
    INTO @name

while @@FETCH_STATUS = 0
BEGIN
    fetch next FROM dcursor
    INTO @name

    declare @sql nvarchar(max)
    SET @sql = N'drop procedure ' + @name
    SELECT @sql
    exec(@sql)
END
CLOSE dcursor
DEALLOCATE dcursor

Thursday 4 August 2011

Why do most Vietnamese developers usually retire after 30 years old?

Quite a few years ago, most developers choose IT as a good start to grow up, earn money and become a boss soon after. That's a reason why many IT developers retire after 30 years old. Even worst, if they haven't promoted to a post of manager in that age, they prepare to drop out of his company, burn their bridges behind them with the hope to change their life, such as Stock Exchange, Trading... Some developers take a Master course in another field like MBA just after graduation or join the first company not long ago. This is very common in Vietnam.

Vietnamese people has strength in Math background, one of important factors to make a good developer. However, it’s not enough. Hard-working, persistence, good attitudes, passion, energy… are all that sum up to good performing developer. In the developing countries like Vietnam, China…, people has tendency to choose the less hard working job but high earning professional. Having worked for IT industry for a couple of years, people will feel the stress in doing software with pretty tightening schedule. Normally, developers have to work more than 8 hours a day to get the job done, especially the projects that usually go down the drain.

Most developers do not notice that if they don't have the so-called "passion" to work on something, they cannot success in any area of business, not in addition to IT only. People of all kinds run into same problem as they think that they come to work for earning money mostly. In fact, in any kinds of job, people always find the fun in it to get the job done.

Tuesday 12 April 2011

Vận dụng phạm trù "Cái chung, cái riêng" trong lập trình,

Hẳn chúng ta còn nhớ, khi chúng ta mới lập trình, chúng ta chưa làm mọi thứ theo cách riêng của chúng ta. Các developer thường xây dựng các hàm riêng mà không buồn kiểm tra xem hàm đó đã có trong thư viện chung hay chưa, hoặc đã có ai đó đã viết hàm đó hay chưa. Kết quả là sau khi dự án kết thúc, có rất nhiều đoạn code trùng lặp nhau về mặt chức năng mà thay vào đó có thể tách ra làm các hàm chung. Điều này làm cho toàn bộ mã nguồn dự án phình lên, khó kiểm soát và hình thành nên cái gọi là “rác mã nguồn”…

Qua một thời gian khi chúng ta có kinh nghiệm, chúng ta thường tư duy về “cái chung” nhiều hơn. Chúng ta sẽ thiết kế làm sao để các module có độ dính kết chặt chẽ cao (cohesion), có ảnh hưởng đến nhau ít nhất có thể (loose coupling) và chia sẻ tối đa phần chung (các thư viện, các tài nguyên ảnh…).

Một kiến trúc sư sẽ cố gắng đưa ra một tầm nhìn xa hơn, cố gắng đưa ra “cái chung” là các thư viện đã dùng ở các dự án cũ, hoặc sau khi phân tích yêu cầu bài toán sẽ quyết định tách ra một module chung để từ đó chia sẻ cho các module khác. Rõ ràng “cái chung” có nhiều ưu điểm hơn “cái riêng”. Đây chính là xu hướng mà các dự án nguồn mở luôn hướng tới. “Cái chung”, “cái riêng” cũng được đưa vào lập trình hướng đối tượng trong đó Interface, Abstract Class là các “cái chung”, các lớp kế thừa nó là “cái riêng”, “cái cụ thể”.

Liệu có một viễn cảnh nào mà “cái chung” được phát huy tối đa trong kiến trúc phần mềm. Chắc chắn là không? Tại sao vậy? Nếu mọi thứ phụ thuộc quá nhiều vào “cái chung” như thư viện, tài nguyên… thì khi có một lỗi xảy ra đặc biệt là ở module nhỏ ít quan trọng, nó sẽ kéo theo sự sụp đổ ở các module khác quan trọng hơn… và hình thành nên hiệu ứng domino. Như vậy các kiến trúc sư sẽ cần phải thận trọng khi thiết kế. Khi quyết định tách ra “cái chung” cần xác định tầm quan trọng thực sự của “cái chung” đó, cũng như quan hệ của nó với các phần khác ra sao.

Trong cơ sở dữ liệu, chúng ta đều biết 4 loại chuẩn hóa dữ liệu. Mục đích để làm gì? Rõ ràng là để giảm dư thừa dữ liệu và đảm bảo tính logic được nhất quán, giảm thiểu sự phức tạp của cấu trúc dữ liệu. Đây chính là vận dụng “cái chung” khá hiệu quả vào bài toán cấu trúc dữ liệu. Nhưng chúng ta cũng lại biết có một kỹ thuật gọi là “phá chuẩn” (denormalization), có nghĩa là “cái chung” bị phá vỡ. Tại sao vậy? Vì trong một số trường hợp, phá chuẩn sẽ tăng hiệu suất chương trình và làm đơn giản hóa công việc, giảm thiểu nỗ lực viết code do quá nhiều liên kết giữa các bảng, tức là liên kết giữa các “cái chung” và “cái riêng”. Nhưng có phải là lúc nào cũng nên “phá chuẩn” không? Câu trả lời là không vì còn phụ thuộc vào bài toán hiện hành. Nếu như nhận thấy việc làm dư thừa dữ liệu không đáng kể thì chúng ta có thể phá chuẩn ở một số liên kết…, tức là tập trung xử lý vào những “cái riêng” và cắt đứt quan hệ với “cái chung”. Như vậy xử lý vấn đề “cái chung”, “cái riêng” trong lập trình cần đòi hỏi sự khéo léo và sáng tạo của những người thiết kế.

Một lưu ý nữa là nếu chúng ta chỉ tư duy “cái chung” thì sẽ không có sự sáng tạo nào được đưa ra, sẽ không có sự đột phá cho các phiên bản tiếp theo. Các lập trình viên mới sẽ phải đi theo con đường cũ của các lập trình viên đi trước vì tất cả đều đã được “dọn sẵn” như thư viện chung, tài nguyên chung…Là một Project Manager, bạn phải khơi mào sự sáng tạo từ các tư duy “cái riêng” của các lập trình viên.

Còn bạn, bạn sẽ nghĩ sao về điều này?

Monday 11 April 2011

Why does object.style.background='url()' cause postback?


Look at the following code:

Here is what you figure it out:









Problem is that each time focus is set for TextBox, a postback occurs. You can realize
it if you place a debug point in Page_Load()
protected void Page_Load(object sender, EventArgs e)
    {
    }



It runs into same problem even in every browser. I have no idea of what the reason might be, nor how to solve it. But I came across a workaround when I play with CSS. Here is what I'm gonna show it:

Firstly, place TextBox background reset block into CSS and change style from JavaScript through CssClass:


    



Secondly, replace onfocus="this.style.background = 'url()';" by onfocus="txtOnFocus(this);"


Now, it works just fine.


Saturday 9 April 2011

Khác nhau giữa Page.ClientScript và ScriptManager?

Có 2 cách đăng ký client-script trên code-behind:
1. Thông qua Page.ClientScript
Thí dụ: Page.ClientScript.RegisterStartupScript(..)
2. Thông qua ScriptManager
Thí dụ: ScriptManager.RegisterStartupScript(..)

Một số câu hỏi đặt ra:

1. Hai cách này khác nhau như thế nào?
2. Khi nào thì nên sử dụng ScriptManager thay vì Page.ClientScript?
3. Khi nào thì sử dụng Page.ClientScript có lợi hơn?

Trước hết, hãy cùng xem lại định nghĩa của 2 đối tượng này:

The ScriptManager is the key component that coordinates the use of JavaScript for the Microsoft AJAX Library.
Page.ClientScript can be used to append some small function in aspx page (validation, alert and so on)

Sau đây là các khác nhau chính giữa các hàm đăng ký client-script trên 2 đối tượng Page và ScriptManager:

1. ScriptManager được sử dụng chỉ khi trên trang Web có bật tính năng Ajax và đã khai báo một đối tượng ScriptManager trên đó.
2. Đối với Page.ClientScript, nếu Control đã nằm sẵn trong UpdatePanel ngay từ đầu (lúc nạp trang) và client-script gần như luôn cố định, thì có thể sử dụng các phương thức đăng ký script của đối tượng Page. Nói một cách khác, nếu script không thay đổi “động” (dựa trên thiết kế để xác định khả năng “động” hay “tĩnh”) và không được gọi trực tiếp sau mỗi “async postback”, thì có thể đăng ký với đối tượng Page. Ngược lại nếu script thay đổi động (khi view source sẽ không nhìn thấy scrip này), được đăng ký tại các thời điểm run-time đặc biệt sau mỗi lần “async postback” xảy ra, thì nên sử dụng các phương thức đăng ký của ScriptManager.


Có một sự khác nhau khác liên quan đến vị trí chèn script. Xem xét 2 thí dụ sau:
Page.ClientScript.RegisterStartupScript(GetType(Page), “myscript”, script)

ScriptManager.RegisterClientScriptBlock(Page, GetType(Page), “myscript”, script, True)

Sự khác nhau giữa hai thí dụ trên là: Thí dụ đầu sẽ đặt mã script ở cuối trang. Còn thí dụ sau sẽ đặt mã script ngay sau thẻ .

Khác nhau thứ hai là thí dụ đầu tiên cần phải đưa cặp thẻ vào mã script. Trong khi thí dụ thứ hai các thẻ này sẽ được tự động đưa vào. Giả sử có đoạn code chi tiết như sau:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 

Dim script As String 

If Not Page.IsPostBack Then 
script = "" 

Page.ClientScript.RegisterStartupScript(GetType(Page), "focus4", script) 

script = "alert('Alert message one');" //Không cần đưa vào cặp thẻ 

ScriptManager.RegisterClientScriptBlock(Page, GetType(Page), "item11", script, True) 

End If 

End sub 


Trong thí dụ này sẽ khởi động 2 Message Box khi bắt đầu nạp trang. Theo thứ tự thì “Alert message two” được biên dịch đầu tiên, nhưng lại được gọi sau message “Alert message one” khi chạy run-time.

Thứ tự xuất hiện trên chỉ là sự khác nhau nhỏ về mặt kiến trúc, và có ảnh hưởng một chút đến hành vi của các control. Sự khác nhau chính vẫn là khả năng “động” của mã script khi được đăng ký theo các Control. Một lưu ý hết sức quan trọng là trong các ứng dụng Ajax sử dụng UpdatePanel, các control xuất hiện trong UpdatePanel có thể là động, thí dụ như chuyển từ ViewMode sang EditMode làm xuất hiện InputControl, hoặc các Control được nạp sau (LazyLoad)… Khi đó nếu sử dụng các phương thức đăng ký của đối tượng Page, chương trình sẽ không chạy đúng và gặp các lỗi lạ... Muốn chương trình chạy đúng, hãy luôn sử dụng các phương thức đăng ký của ScriptManager.

Bảo mật nội dung trang web với các thẻ "onselectstart" và "ondragstart"

Thi thoảng gặp những trang web "cứng đầu" không cho select hoặc copy nội dung của nó (vì bảo vệ bản quyền). Trong trường hợp đó chúng ta thường Save As nó về máy, view HTML code ra, tìm cách xóa sạch trơn các script lẫn trong đống hỗn độn dữ liệu và HTML markup. Nhược điểm của cách này là mất khá nhiều thời gian và thường là thiếu kiên nhẫn! Dưới đây sẽ trình bày 1 cách để tìm đúng "huyệt" và sau đó xóa nó đi, khi đó sẽ copy thoải mái.

<body topmargin="0" leftmargin="0" rightmargin="0" ondragstart="return false" onselectstart="return false" > 

Ở đây sẽ tìm tất cả "onselectstart" và xóa đi là OK.

Ngoài ra các đoạn text trong thẻ div cũng có thể được bảo vệ bởi thuộc tính -moz-user-select (mặc định là Auto).

Chỉ việc đổi nó sang None là sẽ bỏ đi áo giáp bảo vệ này.
<div style="-moz-user-select:none;" /> 

Tương tự như vậy, thẻ "ondragstart" sẽ bảo vệ việc kéo nội dung (text, image...) và thả sang vị trí mới (thí dụ local directory).

Về phía người thiết kế web site, lợi dụng đặc điểm này để có thể thiết kế cách bảo mật nội dung site. Có thể thông qua CSS hoặc JavaScript. Đối với JavaScript, hàm dưới đây được xây dựng để tạo ra giáp bảo vệ cho nội dung. Nếu không thể tìm thấy sự file JavaScript (thí dụ đã bị che dấu trên server thông qua HttpHandler) thì lớp bảo vệ càng khó bị phá vỡ.

function disableSelection(target){ 
if (typeof target.onselectstart!="undefined") //IE route 
target.onselectstart=function(){return false} 

else if (typeof target.style.MozUserSelect!="undefined") //Firefox route 
target.style.MozUserSelect="none" 

else //All other route (ie: Opera) 
target.onmousedown=function(){return false} 

target.style.cursor = "default" 
}

Về vấn đề không tự co chiều rộng Cell trên IE8 và Safari

Trong HTML, có những cấu trúc phần tử có độ dài liên tục, thí dụ như đoạn text sau đây:

LongTextLongTextLongTextLongTextLongTextLongTextLongTextLongTextLongTextLongText

Hoặc text box sau có độ dài width = 500px:


Các phần tử như vậy thường gây ra sự đổ vỡ layout, chúng chồng lên các layer nếu đó là các thẻ div (left column, right column...) ngay cả đối với các fluid-layer. Đối với thiết kế dạng table thì có thể hạn chế được sự chồng lấn này.

Bài viết này sẽ nêu lên 1 vấn đề xảy ra trên IE8 và Safari, liên quan đến các phần tử có độ dài lớn này. Sau đó sẽ nêu lên 1 workaround cho vấn đề này.

Trước hết xét đoạn code HTML sau:

Text Text2
LongTextLongTextLongTextLongTextLongTextLongTextLongTextLongTextLongTextLongText

Kết quả khi chạy trên FireFox như dưới đây, 2 cell ở hàng đầu tiên có chứa dữ liệu (Text, Text2) tự co width theo như đúng yêu cầu.




Trên IE8 và Safari, các cell không tự co width, nguyên nhân do dữ liệu ở hàng thứ hai có độ dài liên tục (tương tự như thay bằng long textbox đề cập ở trên):




Hiện nay Safari chưa đưa ra một cách giải thích nào cho vấn đề này, tuy nhiên chúng ta có thể khắc phục được thông qua workaround như sau:
Text Text2
LongTextLongTextLongTextLongTextLongTextLongTextLongTextLongTextLongTextLongText

Chúng ta sẽ đặt độ rộng của Cell ở giữa (hàng 1) thật lớn, đủ để ép 2 Cell ở 2 bên co lại vừa khít với dữ liệu bên trong. Cụ thể sẽ đặt width:5000px vì thực tế chưa có độ phân giải nào có độ rộng vượt quá giá trị này.
Bây giờ bạn thử chạy lại chương trình trên IE8 và Safari, kết quả sẽ giống như trên FireFox.

Happy Coding,

Thursday 7 April 2011

Tìm hiểu về Session, ViewState, ControlState, HttpContext.Current, Shared/Static Object.









Đối tượng có vòng đời ngắn nhất là HttpContext.Current chỉ tồn tại trên Request, sẽ kết thúc khi Server trả về một Response.

Đối tượng ViewState nằm trên cả Request và Response. Đối tượng này được lưu trên Client thông qua biến ẩn HiddenField và được mã hóa để tránh nhòm ngó.

Một đối tượng khác tương đương ViewState là ControlState. ControlState được sử dụng để lưu lại hành vi của Control và cũng được lưu trên Client thông qua biến ấn Hidden Field. Khi tắt ViewState trên Client thì các thuộc tính được lưu trong ViewState sẽ “chết”, trong khi ControlState vẫn tồn tại vì được bảo vệ trong Control.

Đối tượng Session có mức độ phủ rộng hơn ViewState, nó tồn tại trên bất cứ postback nào ở bất cứ trang nào. Đối tượng Session chỉ kết thúc vòng đời của nó khi shutdown server hoặc tắt trình duyệt. Khác với đối tượng ViewState, đối tượng Session được lưu trên Server nên bảo mật hơn. Nhược điểm là đối tượng Session chiếm nhiều tài nguyên Server. Server sẽ quá tải nếu có nhiều Client gửi Request cùng một lúc.

Monday 4 April 2011

20 typical answer from developers to tester

20. "That’s weird…"
 19. "It’s never done that before."
 18. "It worked yesterday."
17. "How is that possible?"
 16. "It must be a hardware problem."
 15. "What did you type in wrong to get it to crash?"
 14. "There is something funky in your data."
 13. "I haven’t touched that module in weeks!"
 12. "You must have the wrong version."
 11. "It’s just some unlucky coincidence."
 10. "I can’t test everything!"
 09. "THIS can’t be the source of THAT."
 08. "It works, but it hasn’t been tested."
 07. "Somebody must have changed my code."
 06. "Did you check for a virus on your system?"
 05. "Even though it doesn’t work, how does it feel?
 04. "You can’t use that version on your system."
 03. "Why do you want to do it that way?"
 02. "Where were you when the program blew up?"
 01. "It works on my machine" 

Saturday 2 April 2011

SubSonic Framework: Kiến trúc thế hệ mới trên .NET

Đối với bất kỳ dự án nào, việc xây dựng Data Access Layer (DAL) luôn là công việc tốn nhiều công sức và thời gian nhất, nhất là khi phải làm việc với cơ sở dữ liệu lớn. Hơn nữa việc tự xây dựng DAL có thể không được an toàn hoặc tiềm tàng các lỗ hổng bảo mật nếu nhóm phát triển thiếu kinh nghiệm. Chính vì lý do đó mà người ta đưa ra nhiều framework mạnh như NHibernate, LINQ, SubSonic... giúp tự động hóa các công việc viết mã, xây dựng một loạt các Business Object nối từ database ra đến giao diện (UI). Trong số các framework này, SubSonic nổi lên với tiềm năng sẽ trở thành một framework được ưa chuộng do dễ sử dụng.
SubSonic là gì?

SubSonic là một framework với rất nhiều công cụ giúp xây dựng kiến trúc dự án (đặc biệt các dự án web) một cách nhanh chóng, trong đó ý tưởng chính là tự động hóa công việc viết mã cho DAL.
Đặc điểm
• SubSonic giảm thời gian viết mã cho người phát triển.
• SubSonic gắn liền với "tự động hóa", được cài đặt để cập nhật thay đổi thiết kế của CSDL và tự động đồng bộ, cập nhật mã cho DAL, để người phát triển có thể tập trung vào những công việc sáng tạo khác.

• SubSonic sử dụng mô hình Entity làm nền tảng và không hỗ trợ DataSet.

• SubSonic thiết kế DAL dựa trên các mô hình nổi tiếng: "Provider Pattern" và "Active Record Pattern". Active Record là một pattern đặc biệt của Entity với 3 trạng thái đặc trưng: IsLoaded, IsNew và IsDirty. Ngoài ra, giữa các ActiveRecord có quan hệ liên kết với nhau giống như liên kết các bảng trong CSDL.
• Dựa vào ORM (Object Relational Mapping), SubSonic xây dựng ánh xạ giữa các Table/View/Stored Procedures với các đối tượng tạo nên DAL. Ngoài ra, SubSonic được thiết kế tương thích với nhiều hệ CSDL (MS SQL, MySQL, Oracle...).

Trước đây khi xây dựng DAL, chúng ta thường sử dụng DataSet hoặc Entity để trao đổi dữ liệu giữa các tầng với nhau. Các Entity hoặc DataSet là kết quả của phép ánh xạ các bảng CSDL (Table) với các đối tượng. Tuy nhiên việc xây dựng các ánh xạ như vậy thường rất vất vả cho các lập trình viên. Thời kỳ đó các công cụ thương mại như CodeSmidth... ra đời giúp các lập trình viên nhanh chóng tạo ra các đối tượng (Entity, DataSet...) từ các bảng. Tuy nhiên hạn chế của CodeSmidth là lập trình viên phải có kiến thức về ngôn ngữ script để định nghĩa ra các Template trước khi tạo ra Entity, DataSet hay một "Object Pattern" nào đó.
Kiến trúc
• Trung tâm của SubSonic framework là DAL Builder, có nhiệm vụ tạo ra một loạt các đối tượng (Model, Controller), nhờ đó giảm tối đa công sức viết mã. Tất cả các đối tượng được tạo ra bởi SubSonic đều được thiết kế theo kỹ thuật "strongly-typed" sao cho dữ liệu được quản lý an toàn và hiệu quả.

• Sau khi đã có các đối tượng được tạo ra từ DAL Builder, SubSonic hỗ trợ một Query Engine cho phép truy vấn dữ liệu động mà không cần phải viết mã truy vấn, nhờ đó giảm được thời gian cho những công việc lặp lại, nhàm chán (đặc biệt là đối với các CSDL lớn) để người phát triển có thể tập trung nhiều hơn vào các công việc sáng tạo khác.

• Bao quanh SubSonic là các tầng tiện ích, thư viện API và ngày càng được hoàn chỉnh với nhiều Control mới. Đáng kể nhất là "giàn giáo" (Scaffold) giúp thiết kế nhanh các trang Admin (xem chi tiết "Scaffold" ở phần cuối).
Cấu hình

Trước hết, chúng ta hãy mở Web.config (hoặc App.config cho dự án không phải web), thiết lập cấu hình cho SubSonic như sau:
<configSections>
  <section name="SubSonicService" type="SubSonic.SubSonicSection, SubSonic" requirePermission="false"/>
</configSections>
Tiếp theo, thiết lập kết nối cơ sở dữ liệu:

<connectionStrings>
  <add name="CommerceDataConnection" connectionString="Data Source=localhost\SQLExpress; Database=CommerceData; Integrated Security=true;" />
</connectionStrings>
Sau 2 bước trên, chúng ta sẽ định nghĩa các Provider, mỗi Provider được kết nối với một cơ sở dữ liệu:
<SubSonicService defaultProvider="CommerceDataProvider">
  <providers>
  <clear />
  <add type="SubSonic.SqlDataProvider, SubSonic" name="CommerceDataProvider" generatedNamespace="Commerce.Store" connectionStringName="CommerceDataConnection" />
  </providers>
</SubSonicService>

Trên đây là thông tin cơ bản nhất để bắt đầu làm việc với SubSonic. Thông tin này được sử dụng cho hai trường hợp:

• Kích hoạt SubSonic để sinh mã cho các object, scaffold... tại thời điểm biên dịch.

• Thực thi các tương tác với CSDL tại thời điểm chạy chương trình.
Query Engine
SubSonic cung cấp dịch vụ Query Engine cho phép tương tác dữ liệu thông qua một loạt các đối tượng và phương thức có sẵn.

Như đã nói ở trên, mỗi Provider thiết lập kết nối với một CSDL. Khi đang thực thi chương trình có thể chuyển đổi dễ dàng các Provider này. Đoạn mã thí dụ dưới đây sẽ chỉ ra cách thức SubSonic làm việc với CSDL như thế nào:

SubSonic.Query query1 = new SubSonic.Query("Products", "CommerceDataProvider"); //Tham số đầu tiên là tên bảng, tham số thứ hai là tên Provider sẽ được kết nối với SubSonic thông qua khai báo trong web.config.
query1.Schema = SubSonic.Query.BuildTableSchema("Products");
query1.WHERE("ProductName", SubSonic.Comparison.Equals, "BlackBerry");
DataSet ds = query1.ExecuteDataSet();
string debugInfo = query1.Inspect(); //Trả về HTML markups bao gồm các thông tin như thời gian thực thi truy vấn, kết quả truy vấn... Thông tin này rất có ích khi cần debug chương trình
Response.Write(debugInfo); //Cuối cùng, ghi thông tin cần debug ra màn hình
Để hiểu thêm về cách thức Query Engine hoạt động, bạn có thể bắt đầu với dự án đơn giản qua video trực tuyến tại địa chỉ: http://www.wekeroad.com/ss_setup2.html
Active Record Pattern
SubSonic sinh mã theo mô hình Active Record Pattern, tức là mỗi một Table hoặc View sẽ được ánh xạ vào một đối tượng (Active Record), đối tượng này tương tác với Table/View/SP thông qua các hàm chuẩn Load(), Save()... Xem thí dụ sau:

product = new Product()
product.Name = "Your product name"
product.Price = 123.45
product.Save()
Các đối tượng này còn đựơc gọi là "Wrapper Object".

Tất cả các đối tượng sinh ra từ SubSonic đều kế thừa ActiveRecord hoặc ActiveList (đối với các đối tượng dạng danh sách - Collection). SubSonic đưa vào ActiveRecord tất cả các phương thức chuẩn, bao gồm: Save(), Delete(), Destroy(), FetchAll(), FetchByParameter()...
SubSonic sinh mã như thế nào?

Có rất nhiều cách sinh mã trong SubSonic, bạn có thể sử dụng một trong các cách sau:
• Command-line: Trực tiếp trên MS Dos hoặc chạy tệp batch. Một cách khác là mở External Tools trong IDE và đặt đường dẫn đến SubSonic, sau đó kéo vào Toolbar như dưới đây:

Trên toolbar có thể thấy 4 chức năng được bổ sung, gồm chức năng tạo DAL, chức năng thiết kế Scaffold (xem chi tiết "Scaffold" ở phần cuối bài viết) và hai chức năng liên quan đến Database Migration hay DB Versioning (quản lý phiên bản cơ sở dữ liệu).

• Macro: Tích hợp SubSonic với Visual Studio 2005. Với macro, bạn có thể tạo ra DAL chỉ với một lần click chuột.

• SubSonic Add-in, SubSonic Custom Tool: Có thể tích hợp SubSonic vào Visual Studio 2005 như một Add-in hoặc Custom Tool.
• SubStage: Đây là một chương trình khá mạnh có thể tạo ra tất cả những gì bạn muốn, bao gồm cả thông tin về Web.config và mã chương trình như Object Wrapper (Model, Controller), Scaffold... Bạn sẽ không phải làm gì cả ngoại trừ "copy/paste" mã vừa tạo ra vào dự án của bạn (xem thêm phần SubStage ở phần cuối)
Partial Class

SubSonic được xây dựng trên .NET 2.0 trở đi, do đó sẽ không phải lo lắng liệu có thể viết đè lên các đối tượng được SubSonic tạo ra, vì .NET phiên bản 2.0 trở đi hỗ trợ việc xây dựng các đối tượng mở rộng (Partial). Mỗi "Wrapper Object" sẽ có 2 phần, một phần do SubSonic tạo ra, phần còn lại (partial class) là nơi bổ sung các đoạn mã mới mà không ảnh hưởng đến hoạt động của toàn bộ hệ thống.
Audit Field
Trong các thiết kế CSDL ngày nay, các trường Audit Field có mặt gần như khắp mọi nơi. Audit Field làm tăng dữ liệu dư thừa nhưng hiệu quả đem lại thì không thể không tính đến, đó là đẩy mạnh tốc độ xử lý của ứng dụng và đảm bảo an toàn dữ liệu. Có thể nhận thấy rõ nhất điều này khi sử dụng Audit Field để xử lý các vấn đề xung đột khi có nhiều người cập nhật cùng một dữ liệu.

SubSonic tận dụng khả năng đem lại của Audit Field và đưa ra thiết kế chuẩn cho các trường Audit Field như sau:
public class ReservedColumnName
{
public const string CREATED_BY = "CreatedBy";
public const string CREATED_ON = "CreatedOn";
public const string DELETED = "Deleted";
public const string IS_ACTIVE = "IsActive";
public const string IS_DELETED = "IsDeleted";
public const string MODIFIED_BY = "ModifiedBy";
public const string MODIFIED_ON = "ModifiedOn";
}

SubSonic khuyến nghị các bảng CSDL nên được thiết kế với các trường dành riêng này. Vậy SubSonic xử lý các trường Audit Field như thế nào?

Lấy thí dụ về trường "IsDeleted", khi bạn gọi lệnh xóa một bản ghi (phương thức Delete() của ActiveRecord), SubSonic sẽ không xóa bản ghi hoàn toàn ra khỏi database, mà sẽ đánh dấu bản ghi này bằng cách gán giá trị IsDeleted = True. Nếu muốn xóahoàn toàn bản ghi này ra khỏi database, phương thức Destroy() sẽ thực hiện điều đó.
So sánh SubSonic với NHibernate

NHibernate không tự sinh ra mã mà cần đến các chương trình bên ngoài (thí dụ CodeSmidth) định nghĩa template cho NHibernate trước khi sinh mã. Ngược lại SubSonic tự động hoá hoàn toàn và có thể cho phép sửa đổi template theo ý muốn. Hơn nữa SubSonic là một dự án nguồn mở hoàn toàn miễn phí và Microsoft dự kiến sẽ tích hợp SubSonic như một thành phần trong bộ công cụ .NET Studio.

So với các framework khác như Hibernate, LINQ... thì SubSonic dễ cấu hình hơn và có kiến trúc đơn giản, ổn định. Chính vì lý do đó mà SubSonic được xem như "framework nhẹ”.
SubSonic + ASP.MVC: Sự kết hợp kiến trúc trên nền .NET
SubSonic dựa trên MVC và MVC vận dụng SubSonic để xây dựng tầng DAL. Đó là một mối quan hệ khăng khít giữa MVC và SubSonic.

Model: Theo định nghĩa của MVC thì Model sẽ quản lý thông tin và thông báo cho các Observer khi thông tin đó thay đổi. Lợi dụng đặc điểm này, SubSonic xây dựng các Model trong đó thiết lập ánh xạ với các Table/View. Các thuộc tính cơ bản của ActiveRecord là: IsLoaded, IsNew, IsDirty sẽ nói lên trạng thái của thông tin được thay đổi ra sao.

Controller: Trong mô hình MVC, Controller chịu trách nhiệm kết nối các tương tác người dùng với phản hồi của ứng dụng. Controller nhận đầu vào từ View và điều khiển Model để thực hiện các tác vụ tương ứng. Controller kết nối các Model khác nhau và quản lý nguồn dữ liệu của các Model đó (do đó còn gọi là Object Data Source Controller). Cũng như Model, với mỗi Table/View, SubSonic tự động tạo ra Controller tương ứng. Ngoài ra, Controller có thể được mở rộng (partial class) với các "business rule" đặc thù cho từng module.

Như vậy, đối với mỗi một Table/View, sẽ có 3 đối tượng được tự động tạo ra bởi SubSonic: Hai đối tượng Model (một kế thừa ActiveRecord và một kế thừa ActiveList) và một đối tượng Controller.
ASP.NET MVC Web Application Template
Microsoft cung cấp hẳn một template dành riêng cho các dự án theo kiến trúc MVC (có thể vào website của Microsoft để tải về MVC Template). Lưu ý, bất kỳ dự án nào không phải MVC vẫn có thể tích hợp được SubSonic. Tuy nhiên việc xây dựng dự án dựa trên MVC Template có thuận lợi là bạn không phải quan tâm đến toàn bộ kiến trúc của dự án, việc duy nhất bạn phải làm là khởi động SubSonic.
Các bạn có thể tải về video hướng dẫn chi tiết cách thức tích hợp SubSonic với MVC Template tại địa chỉ: http://silverlight.services.live.com/58326/Makai%20Intro/video.wmv
Scaffold: Giàn giáo trong xây dựng phần mềm

Giả sử bạn có tới 50 bảng CSDL và các lập trình viên (LTV), kiểm tra viên (KTV) thường xuyên thao tác với dữ liệu các bảng này để kiểm tra công việc của họ. Tuy nhiên bạn không muốn LTV và KTV thao tác dữ liệu trực tiếp trong CSDL vì nhiều rủi ro. Phương pháp an toàn nhất là dựng lên các giao diện "tạm" với đầy đủ các chức năng CRUD (Create, Read, Update, Delete) giống như một trang Admin để bất cứ ai cũng có thể thay đổi được. Tuy nhiên việc xây dựng 50 trang tạm như thế làm tốn thời gian và công sức. Thuật ngữ "Scaffold" ra đời để tái hiện các trang tạm đó giống như các "giàn giáo" trong ngành xây dựng, giúp các LTV, KTV đẩy mạnh tiến độ dự án. Và khi dự án kết thúc, các giàn giáo đó sẽ được gỡ bỏ và rất có thể, một số trong số đó được giữ lại hoặc thậm chí nâng cấp thành các trang Admin.
Một đặc điểm dễ thấy của Scaffold là, không giống như các trang tương tác trực tiếp với khách hàng, các trang Admin không có yêu cầu quá cao về giao diện vì chỉ hoạt động chủ yếu ở "hậu trường", do đó thiết kế cũng rất đơn giản, sơ sài. Ban đầu Scaffold chỉ được sử dụng cho nội bộ nhóm các LTV và KTV trong suốt quá trình phát triển dự án, về sau các phiên bản mới của các framework đều ra sức phát triển công cụ đi kèm để nâng cấp Scaffold thành các trang Admin hoàn thiện hơn về mặt giao diện lẫn chức năng để bất cứ người dùng nào (có quyền) cũng đều có thể truy cập được như người quản trị hệ thống, những người quản lý...

Hiện nay các framework lớn như Ruby-On-Rails, MonoRail... đều cung cấp Scaffold giúp đẩy mạnh xây dựng các trang Admin. Đặc biệt với Scaffold trong SubSonic, bạn không cần phải viết một dòng mã nào.

Ngoài các chức năng cơ bản CRUD, SubSonic còn trang bị cho Scaffold các control tiện dụng như: GridView, Paging, Calendar, DropDown...

Scaffold trong SubSonic rất đa đạng và được phân loại như sau:

• QuickTable: Hiển thị toàn bộ dữ liệu của một bảng trên GridView, không có đầy đủ CRUD. Đây là control đơn giản nhất của SubSonic.

• Các User Control thành phần bao gồm: DropDown, CalendarControl, ManyManyList, RadioButtons.

• Scaffold: Có đầy đủ CRUD và chứa tất cả các control thành phần của SubSonic.
<subsonic:Scaffold id=Scaffold1 ProviderName="CommerceDataProvider" TableName="Orders" runat="server">
</subsonic:Scaffold >

Bạn có thể kéo thả Scaffold vào giao diện tùy theo mục đích sử dụng, thí dụ nếu muốn tương tác với dữ liệu của bảng Orders trên GridView thì có thể viết trên ASP.NET như sau:Control này sẽ vẽ một bảng chứa toàn bộ dữ liệu của Orders và thiết lập trang soạn thảo (Editor) cho các chức năng Create, Update, Delete.
Chú ý: Đối với các bảng nối (Associate Table), SubSonic bổ sung control có tên là "Many-To-Many" vào Scaffold, nhờ đó người dùng có thể nhập dữ liệu vào bảng nối một cách dễ dàng. Thí dụ bảng OrderDetails trong CSDL của Northwind sẽ là bảng nối của hai bảng Order và Product.
SubStage

SubSonic có thể tự động thiết kế toàn bộ các trang Admin, có nghĩa là bạn không phải mất một giây nào để làm điều đó. SubSonic cung cấp một chương trình quản lý với giao diện trực quan có tên là SubStage với hai chức năng chính: quản lý thông tin cấu hình và sinh mã cho DAL (bao gồm cả mã cho Scaffold). Tất cả những gì bạn phải làm chỉ là chọn Database, Provider và Table mà bạn sẽ tương tác.
Có thể tải về video hướng dẫn sử dụng SubStage tại địa chỉ: http://subsonicproject.com/2-1-pakala/using-substage/

Trước khi bước sang phần tiếp theo, hãy cùng tổng kết lại những gì SubSonic có thể tạo ra:

• DAL:
- "Active Record Wrapper" cho Table, View và SP
- Strongly Typed Collection classes
- Object Data Source Controllers

• Source code (SubSonic config, Scaffold...)
Thư viện hàm tiện ích

Bên cạnh Scaffold và User Control, SubSonic còn cung cấp một thư viện khá lớn bao gồm các hàm xử lý ngày-tháng nâng cao, các hàm xử lý file nâng cao, các hàm xử lý string/number/validation, các hàm thao tác web (thí dụ như mở/đọc trang web, thực hiện DNS Lookup...)
Ứng dụng của SubSonic
SubSonic ngày càng được sử dụng rộng rãi và được xem là framework tốt nhất để xây dựng DAL. Có thể kể đến một số phần mềm nguồn mở cũng như nguồn đóng như:

• DashCommerce (www.dashcommerce.com)

• Commerce for Umbraco: http://www.codeplex.com/commerce4umbraco

• Club Starter Kit: http://www.codeplex.com/ClubStarterKit

• DotNetKick: http://www.dotnetkicks.com

• FranchiseBlast: http://www.lavablast.com/en/lb_franchiseblast.aspx

Các nhà phát triển DotNetNuke (hay DNN, framework nổi tiếng cho Portal trên nền .NET) cũng đang tích hợp SubSonic vào một số module và có thể trong tương lai gần sẽ tích hợp SubSonic vào toàn bộ DNN.
Đới với những người mới bắt đầu tìm hiểu SubSonic, có thể tham khảo một trong hai gói ứng dụng đơn giản nhất:

• "SubSonic Starter Site", tải về tại địa chỉ: http://www.codeplex.com/subsonic/Release/ProjectReleases.aspx?ReleaseId=5177

• "Blog Engine": http://dotnetslackers.com/articles/aspnet/UsingSubSonicToCreateASimpleBogEngine.aspx

Xin giới thiệu thêm một video trình bày về "SubSonic Starter Site" tại địa chỉ: http://www.wekeroad.com/starterintro.htm

Mọi thắc mắc và cần tài liệu tham khảo xin gửi thư về địa chỉ tác giả.
Phạm Đình Trường
GrapeCity Vietnam
Email: phdtruong@yahoo.com

Theo PCWorld VN

Friday 1 April 2011

Design Pattern - Thiết kế theo mô hình mẫu

Trong phát triển phần mềm hiện đại, kiến trúc tổng thể của dự án đóng một vai trò quan trọng, đặc biệt với bộ khung (framework) và mẫu thiết kế (design pattern). Bài viết này sẽ giúp các bạn hiểu được một cách tổng quan về pattern cũng như cách thức thiết kế một số pattern tiêu biểu.

Trong phát triển phần mềm hiện đại, kiến trúc tổng thể của dự án đóng một vai trò quan trọng, đặc biệt với bộ khung (framework) và mẫu thiết kế (design pattern). Bài viết này sẽ giúp các bạn hiểu được một cách tổng quan về pattern cũng như cách thức thiết kế một số pattern tiêu biểu.

PATTERN là gì?

Pattern mô tả một giải pháp chung đối với một vấn đề nào đó trong thiết kế thường được “lặp lại” trong nhiều dự án. Nói một cách khác, một pattern có thể được xem như một “khuôn mẫu” có sẵn áp dụng được cho nhiều tình huống khác nhau để giải quyết một vấn đề cụ thể. Trong bất kỳ hệ thống phần mềm hướng đối tượng nào chúng ta cũng có thể bắt gặp các vấn đề lặp lại.

Đặc điểm chung:

• Pattern được hiểu theo nghĩa tái sử dụng ý tưởng hơn là mã lệnh. Pattern cho phép các nhà thiết kế có thể cùng ngồi lại với nhau và cùng giải quyết một vấn đề nào đó mà không phải mất nhiều thời gian tranh cãi. Trong rất nhiều trường hợp, dự án phần mềm thất bại là do các nhà phát triển không có được sự hiểu biết chung trong các vấn đề về kiến trúc phần mềm. Ngoài ra, pattern cũng cung cấp những thuật ngữ và khái niệm chung trong thiết kế. Nói một cách đơn giản, khi đề cập đến một pattern nào đấy, bất kỳ ai biết pattern đó đều có thể nhanh chóng hình dung ra “bức tranh” của giải pháp. Và cuối cùng, nếu áp dụng pattern hiệu quả thì việc bảo trì phần mềm cũng được tiến hành thuận lợi hơn, nắm bắt kiến trúc hệ thống nhanh hơn.

• Pattern hỗ trợ tái sử dụng kiến trúc và mô hình thiết kế phần mềm theo quy mô lớn. Cần phân biệt design pattern với framework. Framework hỗ trợ tái sử dụng mô hình thiết kế và mã nguồn ở mức chi tiết hơn. Trong khi đó, design pattern được vận dụng ở mức tổng quát hơn, giúp các nhà phát triển hình dung và ghi nhận các cấu trúc tĩnh và động cũng như quan hệ tương tác giữa các giải pháp trong quá trình thiết kế ứng dụng đối với một chuyên khu riêng biệt.

• Pattern đa tương thích. Pattern không phụ thuộc vào ngôn ngữ lập trình, công nghệ hoặc các nền tảng lớn như J2EE của Sun hay Microsoft .NET Framework.

Tiềm năng ứng dụng của pattern là rất lớn. Các thiết kế dựa trên pattern được sử dụng khá nhiều ở các phần mềm mã nguồn mở, trong nền tảng J2EE hoặc .NET... Trong các dạng ứng dụng này, có thể dễ dàng nhận ra một số tên lớp chứa các tiền tố hoặc hậu tố như Factory, Proxy, Adapter...

PHÂN LOẠI PATTERN

Pattern được phân loại ra làm 3 nhóm chính sau đây:

• Nhóm cấu thành (Creational Pattern): Gồm Factory, Abstract Factory, Singleton, Prototype, Builder... Liên quan đến quá trình khởi tạo đối tượng cụ thể từ một định nghĩa trừu tượng (abstract class, interface).

• Nhóm cấu trúc tĩnh (Structural Pattern): Gồm Proxy, Adapter, Wrapper, Bridge, Facade, Flyweight, Visitor... Liên quan đến vấn đề làm thế nào để các lớp và đối tượng kết hợp với nhau tạo thành các cấu trúc lớn hơn.

• Nhóm tương tác động (Behavioral Pattern): Gồm Observer, State, Command, Iterator... Mô tả cách thức để các lớp hoặc đối tượng có thể giao tiếp với nhau.

Dưới đây chúng ta sẽ tìm hiểu chi tiết một số pattern tiêu biểu nhất: Factory, Abstract Factory, Singleton, Proxy, Adapter và Wrapper. Chúng ta quy ước với nhau rằng “giao diện lớp” được hiểu như interface hoặc abstract class vì đây đơn thuần là các định nghĩa lớp.

FACTORY PATTERN

Định nghĩa

Factory Pattern định nghĩa một lớp (interface, abstract, class) đóng vai trò như một “nhà xưởng” có nhiệm vụ khởi tạo đối tượng “cụ thể” khi ứng dụng chạy. Tại thời điểm thiết kế đối tượng này được định nghĩa trừu tượng.

Đặc điểm

Kiểm soát được các hoạt động trong suốt chu kỳ sống của đối tượng, như khởi tạo đối tượng, huỷ đối tượng... Đảm bảo cho các đối tượng được thực thi an toàn. Nắm được thông tin về những đối tượng nào được tạo ra và được khởi tạo ra sao. Nói cách khác, các đối tượng được quản lý tốt hơn và an toàn hơn với Factory Pattern.

Đối tượng Factory thường được đặt tên theo những chuẩn khác nhau nhưng vẫn có thể dễ dàng nhận ra thiết kế Factory Pattern ẩn chứa trong đó. Ví dụ: BankFactory,...

Tính chất đóng gói (encapsulation) thể hiện rõ trong Factory Pattern; các thông tin liên quan đến truy cập đối tượng được che giấu trong Factory. Thiết kế Factory luôn có một thủ tục khởi tạo đối tượng, ví dụ createObject().

Factory Pattern tuân thủ nguyên tắc thiết kế DIP (Dependency Inversion Principle): không nên phụ thuộc vào những thứ quá cụ thể.

Phân loại

Factory Pattern được thiết kế theo một trong hai cách sau đây:

Based-class Factory Pattern: Mẫu này sử dụng tính chất thừa kế để phân loại các đối tượng được tạo ra.

Based-object Factory Pattern: Sử dụng mối quan hệ kết hợp để tham chiếu tới một đối tượng sẽ được tạo ra. Đối tượng được tạo ra sẽ trở thành một phần hay thuộc tính của lớp Factory. Chúng ta thường hay gặp loại này trong Abstract Factory Pattern được trình bày ở phần tiếp theo.

ABSTRACT FACTORY PATTERN

Định nghĩa

Abstract Factory cung cấp một giao diện lớp có chức năng tạo ra một tập hợp các đối tượng liên quan hoặc phụ thuộc lẫn nhau mà không chỉ ra đó là những lớp cụ thể nào tại thời điểm thiết kế.

Về bản chất, Abstract Factory Pattern chỉ khác Factory Pattern ở chỗ bản thân đối tượng Factory không được chỉ ra cụ thể tại thời điểm thiết kế, tức nó là một giao diện hoặc lớp trừu tượng (interface, abstract). Nếu như Factory Patttern phân loại đối tượng dựa trên tham số đầu vào thì đối với Abstract Factory Pattern, thủ tục createObject() còn phụ thuộc thêm vào các yếu tố phụ khác như môi trường hệ điều hành chẳng hạn. Ứng với mỗi yếu tố phụ thứ hai ta có một lớp Factory cụ thể.

Thiết kế động với Abstract Factory
Một trong những vấn đề gặp phải là khung giao diện Abstract Factory thường hay bị sửa đổi, thí dụ như bổ sung thủ tục chẳng hạn, khi đó các lớp cụ thể thực thi giao diện này sẽ phải được dịch và triển khai lại. Để giảm nhẹ vấn đề này người ta thường thiết kế giao diện Abstract Factory một cách linh động.

SINGLETON PATTERN
(Static Factory Pattern)

Định nghĩa

Singleton Pattern đảm bảo một lớp chỉ có một thực thể (instance) duy nhất được tạo ra và đồng thời cung cấp một truy cập toàn cục đến đối tượng được tạo ra.
Chúng ta xét trường hợp có nhiều đối tượng có cùng chung một số tính chất nào đó được tạo ra ứng với mỗi một yêu cầu từ các đối tượng khách (client), lúc này độ phức tạp sẽ tăng lên và ứng dụng sẽ chiếm dụng nhiều vùng nhớ hơn. Singleton Pattern là một giải pháp đặc biệt của Factory Pattern ở chỗ đối tượng sinh ra là điểm truy cập toàn cục “duy nhất” đối với mọi chương trình gọi đến, hay nói một cách khác tất cả các đối tượng khách gọi đến đều chia sẻ đối tượng được tạo ra.

Ứng dụng rõ rệt nhất của Singleton Pattern có thể thấy trong dịch vụ web khi triệu gọi các đối tượng từ xa, ở đó đối tượng nằm trên server hoặc sẽ phục vụ chung cho tất cả các ứng dụng khách (singleton) hoặc sẽ chỉ đáp ứng một ứng dụng khách riêng lẻ nào đó rồi tự bị phá huỷ sau đó (single call).

Về các mẫu thiết kế tiêu biểu trong nhóm cấu thành: Factory, Abstract Factory và Singleton, các bạn có thể tham khảo thêm tài liệu về phương pháp xây dựng cụ thể cũng như mã nguồn chương trình viết bằng C#.NET tại địa chỉ:

http://www.codeproject.com/gen/design/CSharpClassFactory.asp

PROXY PATTERN

Định nghĩa

Proxy Pattern là mẫu thiết kế mà ở đó tất cả các truy cập trực tiếp một đối tượng nào đó sẽ được chuyển hướng vào một đối tượng trung gian (Proxy Class).
Nếu như Factory Pattern giúp quản lý đối tượng tốt hơn thì Proxy Pattern lại có nhiệm vụ bảo vệ việc truy cập một đối tượng thông qua Proxy, hay còn gọi là truy cập gián tiếp. Proxy được ủy quyền về phía ứng dụng khách cho phép tương tác với đối tượng đích theo những cách khác nhau; như gửi yêu cầu một dịch vụ nào đó, theo dõi trạng thái và vòng đời đối tượng, xây dựng lớp vỏ bảo vệ đối tượng... Thí dụ chúng ta phát hiện ra một đối tượng trong một thư viện DLL có thể bị khai thác truy cập vào một số trường quan trọng, khi đó chúng ta không thể mở mã nguồn thư viện đã được dịch để vá lỗ hổng, giải pháp lúc này là xây dựng một proxy ngăn chặn truy cập các trường đó và cuối cùng biên dịch lại thành một DLL mới.

Phân loại

Độ phức tạp của giải pháp sử dụng Proxy Pattern phụ thuộc vào tình huống bài toán đưa ra, chúng ta sẽ lần lượt tìm hiểu nguyên tắc làm việc của các proxy dưới đây:
Remote Proxy: Client truy cập qua remote proxy để tham chiếu tới một đối tượng được bảo vệ nằm bên ngoài ứng dụng (trên cùng máy hoặc máy khác) như dịch vụ Windows, dịch vụ web, ứng dụng ở xa... Mô hình này "che giấu" đối tượng được triệu gọi đang nằm ở rất xa đâu đó và client có vẻ như truy cập vào đối tượng nằm trên cùng một chuyên khu làm việc (domain).

Virtual Proxy: Virtual Proxy tạo ra một đối tượng trung gian mỗi khi có yêu cầu tại thời điểm thực thi ứng dụng, nhờ đó làm tăng hiệu suất của ứng dụng.

Monitor Proxy: Monitor Proxy sẽ thiết lập các ràng buộc bảo mật trên đối tượng cần bảo vệ, ngăn không cho client truy cập một số trường quan trọng của đối tượng.

Protection Proxy: Đối với proxy này thì phạm vi truy cập của các client khác nhau sẽ khác nhau. Protection Proxy sẽ kiểm tra các quyền truy cập của client khi có một dịch vụ được yêu cầu.

Cache Proxy: Cung cấp không gian lưu trữ tạm thời cho các kết quả trả về từ đối tượng nào đó, kết quả này sẽ được tái sử dụng cho các client chia sẻ chung một yêu cầu gửi đến và do đó làm tăng đáng kể hiệu suất chương trình.

Firewall Proxy: Bảo vệ đối tượng từ chối các yêu cầu xuất xứ từ các client không tín nhiệm.

Smart Reference Proxy: Là nơi kiểm soát các hoạt động bổ sung mỗi khi đối tượng được tham chiếu, ví dụ như kiểm soát vòng đời của đối tượng, lưu lại số lần tham chiếu vào đối tượng...

Synchronization Proxy: Đảm bảo nhiều client có thể truy cập vào cùng một đối tượng mà không gây ra xung đột. Thực tế có rất nhiều tình huống khiến chúng ta phải nghĩ đến thiết kế này. Một synchronization proxy được thiết lập có thể kiểm soát được nhiều yêu cầu cập nhật dữ liệu một cách đồng thời, tại thời điểm bắt đầu cập nhật chỉ có một client với mức ưu tiên cao nhất giành được khoá để đánh dấu rằng các client khác cần phải chờ đến lượt.

Synchronization proxy hoạt động rất hiệu quả và phổ biến trong thiết kế các bài toán đa tuyến. Một hiện tượng hay xảy ra với thiết kế này là khi một client nào đó chiếm dụng khoá khá lâu (và thậm chí là mãi mãi) khiến cho số lượng các client trong danh sách hàng đợi cứ tăng lên, và do đó hoạt động của hệ thống bị ngừng trệ, có thể dẫn đến hiện tượng “tắt nghẽn” là hiện tượng khoá được giữ vô thời hạn bởi một đối tượng nào đó. Trong trường hợp này người ta cải tiến thành mẫu thiết kế phức tạp hơn, đó là Copy-On-Write Proxy.

Copy-On-Write Proxy: Cope-On-Write Proxy đảm bảo rằng sẽ không có client nào phải chờ vô thời hạn. Thiết kế này rất phức tạp do đó chỉ nên ứng dụng Copy-On-Write Proxy thay thế Synchronization Proxy khi hệ thống được dự đoán sẽ thường xuyên bị ngừng trệ hoặc có hiện tượng “tắt nghẽn” xảy ra.

Đặc điểm chung

Proxy Pattern có những đặc điểm chung sau đây:

• Cung cấp mức truy cập gián tiếp vào một đối tượng.

• Tham chiếu vào đối tượng đích và chuyển tiếp các yêu cầu đến đối tượng đó.

• Cả proxy và đối tượng đích đều kế thừa hoặc thực thi chung một lớp giao diện. Mã máy dịch cho lớp giao diện thường “nhẹ” hơn các lớp cụ thể và do đó có thể giảm được thời gian tải dữ liệu giữa server và client.

ADAPTER PATTERN

Định nghĩa

Adapter Pattern biến đổi giao diện của một lớp thành một giao diện khác mà các đối tượng client có thể hiểu được. Lớp với giao diện được tạo ra đó gọi là Adapter. Nguyên tắc cơ bản của Adapter Pattern nằm ở chỗ làm thế nào để các lớp với các giao diện không tương thích có thể làm việc được với nhau.

Nguyên lý xây dựng Adapter Pattern khá đơn giản: chúng ta xây dựng một lớp với một giao diện mong muốn sao cho lớp đó giao tiếp được với một lớp cho trước ứng với một giao diện khác.

Adapter Pattern không quản lý tập trung các đối tượng gần giống nhau như Factory Pattern, mà kết nối với nhiều lớp không có liên quan gì với nhau. Ví dụ lớp A sau khi thực thi giao diện của nó và vẫn muốn bổ sung các phương thức từ một lớp B nào đó, chúng ta có thể kết nối A với B thông qua hình thức kế thừa hoặc liên kết đối tượng như một thành phần. Adapter Pattern có sự giống nhau một chút với Proxy Pattern ở chỗ nó tận dụng tối đa tính chất “uỷ quyền” (delegation); lớp Adapter sẽ kết nối với một đối tượng nào đó gọi là Adaptee và Adapter sẽ được uỷ quyền truy cập vào Adaptee, lớp Adapter đóng vai trò như một kênh trung gian để client truy cập vào một số các thành phần quan trọng của lớp Adaptee.

Đặc điểm

• Adapter Pattern hướng tập trung vào giải quyết sự tương thích giữa hai giao diện đang tồn tại, giảm công sức viết lại mã lệnh xuống mức tối thiểu có thể được.

• Tái sử dụng giao diện cũ và Adapter Pattern chỉ thực sự cần thiết khi mọi thứ đã được thiết kế từ trước.

Phạm vi ứng dụng

Adapter Pattern được ứng dụng trong các trường hợp:

• Cần tích hợp một vài module vào chương trình.

• Không thể sát nhập trực tiếp module vào chương trình (ví dụ như module thư viện đã được dịch ra .DLL, .CLASS...).

• Module đang tồn tại không có giao diện mong muốn như:

- Cần nhiều hơn phương thức cho module đó.

- Một số phương thức có thể được nạp chồng.

WRAPPER PATTERN

Wrapper Pattern là một trường hợp đặc biệt của Adapter Pattern. Nếu một Adapter chỉ đơn thuần là “nhúng” (wrap) các lớp với các giao diện không tương thích với nhau để chúng có thể hoạt động cùng nhau thì có thể được gọi bằng tên riêng Wrapper Pattern. Khi đó lớp Adapter còn được gọi là lớp Wrapper. Đây là quan hệ “có một”, tức là một giao diện không tương thích có thể được nhúng vào thành một phần của một giao diện khác.

Đặc điểm

Đối tượng Wrapper mô phỏng tất cả các hành vi (hàm, thủ tục) của giao diện được nhúng bởi các hành vi với tên y hệt. Thí dụ nếu lớp được nhúng A có thủ tục SpecificRequest() thì lớp Wrapper cũng phải có thủ tục SpecificRequest() tham chiếu đến thủ tục cùng tên của A. (Ngoài ra đối tượng Wraper có thể được bổ sung các phương thức khác nếu cần thiết). Đặc điểm này được đưa ra dựa trên nguyên tắc thiết kế “Law of Demeter” nói rằng không nên tham chiếu một đối tượng sâu hơn một lớp.

Các phương thức trong Adaptee được “nhúng” trong Wrapper bằng cách truyền lời gọi cùng với các tham số tới phương thức tương ứng trong Adaptee, và trả về kết quả giống như vậy. Các thành viên (thuộc tính, trường, sự kiện) được nhúng trong Wrapper có tính chất giống hệt như trong các lớp được nhúng (tên, kiểu dữ liệu, phạm vi truy cập...).

Từ các đặc điểm ở trên, có thể thấy rằng Wrapper Pattern cho phép một module chương trình tương tác được trong một môi trường khác biệt với môi trường phát triển của module đó (ví dụ C++ và Java).

Khác biệt giữa Wrapper Pattern và Adapter Pattern

Sự khác biệt giữa Wrapper và Adapter nằm ở mục đích sử dụng: Adapter Pattern định hướng cho một đối tượng đang tồn tại có thể làm việc được với các đối tượng khác và biến đổi logic theo một cách thức nào đó, trong khi Wrapper Pattern chỉ đơn thuần cung cấp một giao diện kết hợp các đối tượng được xây dựng từ cùng một ngôn ngữ hoặc khác ngôn ngữ, trên cùng một hệ điều hành hoặc trên những hệ điều hành khác nhau.

Proxy Adapter Pattern

Nếu một lớp Adapter đóng thêm vai trò như một proxy bảo vệ cho Adaptee thì ta có mô hình Proxy Adapter Pattern, trong trường hợp này chúng ta có thể đặt tên lớp với nghĩa kết hợp, ví dụ BankProxyAdapter.

Lời kết

Bài viết này đưa ra một số mẫu thiết kế tiêu biểu giúp các bạn thấy được tầm quan trọng của design pattern trong việc nâng cao chất lượng phần mềm ở các yếu tố: hiệu suất ứng dụng, độ ổn định, tái sử dụng, tính bảo mật... Các bạn có thể tìm hiểu thêm về các mẫu thiết kế cao cấp khác ở rất nhiều tài liệu thiết kế phần mềm.

Phạm Đình Trường
phdtruong@yahoo.com
Software Engineer, GrapeCity Inc

Theo PCWorld VN,

Thursday 31 March 2011

Unit Test với phát triển phần mềm hiện đại

Unit Test (UT) là một kỹ thuật quan trọng góp phần nâng cao chất lượng phần mềm (PM), nhưng có nhiều quan điểm trái ngược nhau về việc đưa UT vào quy trình phát triển PM. Bài viết này giới thiệu một cái nhìn tổng quan về UT và mô hình phát triển phần mềm hiện đại TDD (Test-Driven Development).

UNIT TEST
UT là kỹ thuật kiểm nghiệm các hoạt động của mọi chi tiết mã (code) với một quy trình tách biệt với quy trình phát triển PM, giúp phát hiện sai sót kịp thời. UT còn có thể giúp phát hiện các vấn đề tiềm ẩn và các lỗi thời gian thực ngay cả trước khi chuyên viên kiểm định chất lượng (QA - Quality Assurance) tìm ra, thậm chí có thể sửa lỗi ngay từ ý tưởng thiết kế.

UT là các đoạn mã có cấu trúc giống như các đối tượng được xây dựng để kiểm tra từng bộ phận trong hệ thống. Mỗi UT sẽ gửi đi một thông điệp và kiểm tra câu trả lời nhận được đúng hay không, bao gồm:

• Các kết quả trả về mong muốn

• Các lỗi ngoại lệ mong muốn

Các đoạn mã UT hoạt động liên tục hoặc định kỳ để thăm dò và phát hiện các lỗi kỹ thuật trong suốt quá trình phát triển, do đó UT còn được gọi là kỹ thuật kiểm nghiệm tự động.

UT có các đặc điểm sau:

• Đóng vai trò như những người sử dụng đầu tiên của hệ thống.

• Chỉ có giá trị khi chúng có thể phát hiện các vấn đề tiềm ẩn hoặc lỗi kỹ thuật.

Vòng đời của UT

Một thí dụ trong NUnit. Nút xanh thể hiện các trạng thái "pass", nút đỏ ứng với trạng thái "fail" và nút vàng ứng với trạng thái "ignore".
UT có 3 trạng thái cơ bản:

• Fail (trạng thái lỗi)

• Ignore (tạm ngừng thực hiện)

• Pass (trạng thái làm việc)

Toàn bộ UT được vận hành trong một hệ thống tách biệt. Có rất nhiều PM hỗ trợ thực thi UT với giao diện trực quan. Thông thường, trạng thái của UT được biểu hiện bằng các màu khác nhau: màu xanh (pass), màu vàng (ignore) và màu đỏ (fail).

UT chỉ thực sự đem lại hiệu quả khi:

• Được vận hành lặp lại nhiều lần

• Tự động hoàn toàn

• Độc lập với các UT khác.

Thiết kế UT

Mỗi UT đều được tiết kế theo trình tự sau:

• Thiết lập các điều kiện cần thiết: khởi tạo các đối tượng, xác định tài nguyên cần thiết, xây dựng các dữ liệu giả...

• Triệu gọi các phương thức cần kiểm tra.

• Kiểm tra sự hoạt động đúng đắn của các phương thức.

• Dọn dẹp tài nguyên sau khi kết thúc kiểm tra.

Ứng dụng của UT
• Kiểm tra mọi đơn vị nhỏ nhất là các thuộc tính, sự kiện, thủ tục và hàm.

• Kiểm tra các trạng thái và ràng buộc của đối tượng ở các mức sâu hơn mà thông thường chúng ta không thể truy cập được.

• Kiểm tra các quy trình (process) và mở rộng hơn là các khung làm việc(workflow - tập hợp của nhiều quy trình).

Lợi ích của UT

Thời gian đầu, người ta thường do dự khi phải viết UT thay vì tập trung vào viết mã cho các chức năng nghiệp vụ. Công việc viết UT có thể ngốn nhiều thời gian, yuy nhiên UT đem lại lợi ích to lớn như:

• Tạo ra môi trường lý tưởng để kiểm tra bất kỳ đoạn mã nào, có khả năng thăm dò và phát hiện lỗi chính xác, duy trì sự ổn định của toàn bộ PM và giúp tiết kiệm thời gian so với công việc gỡ rối truyền thống.

• Phát hiện các thuật toán thực thi không hiệu quả, các thủ tục chạy vượt quá giới hạn thời gian.
• Phát hiện các vấn đề về thiết kế, xử lý hệ thống, thậm chí các mô hình thiết kế.

• Phát hiện các lỗi nghiêm trọng có thể xảy ra trong những tình huống rất hẹp.

• Tạo hàng rào an toàn cho các khối mã: Bất kỳ sự thay đổi nào cũng có thể tác động đến hàng rào này và thông báo những nguy hiểm tiềm tàng.
UT tạo thành hàng rào an toàn cho mã ứng dụng
• UT là môi trường lý tưởng để tiếp cận các thư viện API bên ngoài một cách tốt nhất. Sẽ rất nguy hiểm nếu chúng ta ứng dụng ngay các thư viện này mà không kiểm tra kỹ lưỡng công dụng của các thủ tục trong thư viện. Dành ra thời gian viết UT kiểm tra từng thủ tục là phương pháp tốt nhất để khẳng định sự hiểu đúng đắn về cách sử dụng thư viện đó. Ngoài ra, UT cũng được sử dụng để phát hiện sự khác biệt giữa phiên bản mới và phiên bản cũ của cùng một thư viện.

Trong môi trường làm việc cạnh tranh, UT còn có tác dụng rất lớn đến năng suất làm việc:

• Giải phóng chuyên viên QA khỏi các công việc kiểm tra phức tạp.

• Tăng sự tự tin khi hoàn thành một công việc. Chúng ta thường có cảm giác không chắc chắn về các đoạn mã của mình như liệu các lỗi có quay lại không, hoạt động của module hiện hành có bị tác động không, hoặc liệu công việc hiệu chỉnh mã có gây hư hỏng đâu đó...

• Là công cụ đánh giá năng lực của bạn. Số lượng các tình huống kiểm tra (test case) chuyển trạng thái "pass" sẽ thể hiện tốc độ làm việc, năng suất của bạn.

Chiến lược viết mã hiệu quả với UT

• Phân tích các tình huống có thể xảy ra đối với mã. Đừng bỏ qua các tình huống tồi tệ nhất có thể xảy ra, thí dụ dữ liệu nhập làm một kết nối cơ sở dữ liệu thất bại, ứng dụng bị treo vì một phép toán chia cho không, các thủ tục đưa ra lỗi ngoại lệ sai có thể phá hỏng ứng dụng một cách bí ẩn...

• Mọi UT phải bắt đầu với trạng thái "fail" và chuyển trạng thái "pass" sau một số thay đổi hợp lý đối với mã chính.

• Mỗi khi viết một đoạn mã quan trọng, hãy viết các UT tương ứng cho đến khi bạn không thể nghĩ thêm tình huống nào nữa.

• Nhập một số lượng đủ lớn các giá trị đầu vào để phát hiện điểm yếu của mã theo nguyên tắc:
- Nếu nhập giá trị đầu vào hợp lệ thì kết quả trả về cũng phải hợp lệ

- Nếu nhập giá trị đầu vào không hợp lệ thì kết quả trả về phải không hợp lệ

• Sớm nhận biết các đoạn mã không ổn định và có nguy cơ gây lỗi cao, viết UT tương ứng để khống chế.

• Ứng với mỗi đối tượng nghiệp vụ (business object) hoặc đối tượng truy cập dữ liệu (data access object), nên tạo ra một lớp kiểm tra riêng vì những lỗi nghiêm trọng có thể phát sinh từ các đối tượng này.
• Để ngăn chặn các lỗi có thể phát sinh trở lại thực thi tự động tất cả UT mỗi khi có một sự thay đổi quan trọng, hãy làm công việc này mỗi ngày. Các UT lỗi cho chúng ta biết thay đổi nào là nguyên nhân gây lỗi.

• Để tăng hiệu quả và giảm rủi ro khi viết các UT, cần sử dụng nhiều phương thức kiểm tra khác nhau. Hãy viết càng đơn giản càng tốt.

• Cuối cùng, viết UT cũng đòi hỏi sự nỗ lực, kinh nghiệm và sự sáng tạo như viết PM.

Trước khi kết thúc phần này, chúng tôi có một lời khuyên là viết UT cũng tương tự như viết mã một chương trình, điều bạn cần làm là không ngừng thực hành. Hãy nhớ UT chỉ thực sự mang lại lợi ích nếu chúng ta đặt vấn đề chất lượng phần mềm lên hàng đầu hơn là chỉ nhằm kết thúc công việc đúng thời hạn. Khi đã thành thạo với công việc viết UT, bạn có thể đọc thêm về các kỹ thuật xây dựng UT phức tạp hơn, trong số đó có mô hình đối tượng ảo sẽ được trình bày trong phần tiếp theo.
UT tương tác với cả đối tượng ảo và đối tượng thực
XÂY DỰNG UT VỚI MÔ HÌNH ĐỐI TƯỢNG ẢO (MOCK OBJECT)

Trong UT, mỗi một đối tượng hay một phương thức riêng lẻ được kiểm tra tại một thời điểm và chúng ta chỉ quan tâm đến các trách nhiệm của chúng có được thực hiện đúng hay không. Tuy nhiên trong các dự án PM phức tạp thì UT không còn là quy trình riêng lẻ, nhiều đối tượng (đơn vị chương trình) không làm việc độc lập mà tương tác với các đối tượng khác như kết nối mạng, cơ sở dữ liệu hay dịch vụ web. Như vậy công việc kiểm nghiệm có thể bị trì hoãn gây tác động xấu đến quy trình phát triển chung. Để giải quyết các vấn đề này người ta đưa ra mô hình "Mock Object" hay đối tượng ảo (hoặc đối tượng giả).

Định nghĩa
Mock object (MO) là một đối tượng ảo mô phỏng các tính chất và hành vi giống hệt như đối tượng thực được truyền vào bên trong khối mã đang vận hành nhằm kiểm tra tính đúng đắn của các hoạt động bên trong.

Đặc điểm

• Đơn giản hơn đối tượng thực nhưng vẫn giữ được sự tương tác với các đối tượng khác.

• Không lặp lại nội dung đối tượng thực.

• Cho phép thiết lập các trạng thái riêng trợ giúp kiểm tra.

Lợi ích

• Đảm bảo công việc kiểm nghiệm không bị gián đoạn bởi các yếu tố bên ngoài, giúp các chuyên viên tập trung vào một chức năng nghiệp vụ cụ thể, từ đó tạo ra UT vận hành nhanh hơn.

• Giúp tiếp cận hướng đối tượng tốt hơn. Nhờ MO chúng ta có thể phát hiện interface cần tách ở một số lớp.

• Dễ dàng cho việc kiểm nghiệm. Thay vì gọi các đối tượng thực vận hành nặng nề, chúng ta có thể gọi các MO đơn giản hơn để kiểm tra nhanh liên kết giữa các thủ tục, công việc kiểm nghiệm có thể tiến hành nhanh hơn.

Phạm vi sử dụng
MO được sử dụng trong các trường hợp sau:

• Cần lập trạng thái giả của một đối tượng thực trước khi các UT có liên quan được đưa vào vận hành (thí dụ kết nối cơ sở dữ liệu, giả định trạng thái lỗi server...)

• Cần lập trạng thái cần thiết cho một số tính chất nào đó của đối tượng đã bị khoá quyền truy cập (các biến, thủ tục, hàm, thuộc tính riêng được khai báo private). Không phải lúc nào các tính chất của một đối tượng cũng có thể được mở rộng phạm vi truy cập ra bên ngoài vì điều này có thể trực tiếp phá vỡ liên kết giữa các phương thức theo một trình tự sắp đặt trước, từ đó dẫn đến kết quả có thể bị xử lý sai. Tuy nhiên, MO có thể thiết lập các trạng thái giả mà vẫn đảm bảo các yêu cầu ràng buộc, các nguyên tắc đúng đắn và các quan hệ của đối tượng thực.

• Cần kiểm tra một số thủ tục hoặc các biến của thành viên bị hạn chế truy cập. Bằng cách kế thừa MO từ đối tượng thực chúng ta có thể kiểm tra các thành viên đã được bảo vệ (khai báo protected).

• Cần loại bỏ các hiệu ứng phụ của một đối tượng nào đó không liên quan đến UT.

• Cần kiểm nghiệm mã vận hành có tương tác với hệ thống bên ngoài.

Các dạng đối tượng được mô phỏng

MO mô phỏng các loại đối tượng sau đây:

• Các đối tượng thực mới chỉ được mô tả trên bản thiết kế nhưng chưa tồn tại dưới dạng mã, hoặc các module chưa sẵn sàng cung cấp các dữ liệu cần thiết để vận hành UT.

• Các đối tượng thực có các thủ tục chưa xác định rõ ràng về mặt nội dung (mới chỉ mô tả trong interface) nhưng được đòi hỏi sử dụng gấp trong các UT.

• Các đối tượng thực rất khó cài đặt (thí dụ đối tượng xử lý các trạng thái của server)

• Các đối tượng thực xử lý một tình huống khó xảy ra. Thí dụ lỗi kết nối mạng, lỗi ổ cứng...

• Các đối tượng có các tính chất và hành vi phức tạp, các trạng thái luôn thay đổi và các quan hệ chặt chẽ với nhiều đối tượng khác

• Các đối tượng vận hành chậm chạp. Công việc kiểm tra hiện hành không liên quan đến thao tác xử lý đối tượng này.

• Đối tượng thực liên quan đến giao diện tương tác người dùng. Không người dùng nào có thể ngồi kiểm nghiệm các chức năng hộ bạn hết ngày này qua ngày khác. Tuy nhiên bạn có thể dùng MO để mô phỏng thao tác của người dùng, nhờ đó công việc có thể được diễn biến lặp lại và hoàn toàn tự động.

Thiết kế MO

Thông thường, nếu số lượng MO không nhiều, chúng ta có thể tự thiết kế. Nếu không muốn mất nhiều thời gian tự thiết kế một số lượng lớn MO, bạn có thể tải về các công cụ có sẵn thông dụng hiện nay như EasyMock, jMock, Nmock... Các phần mềm này cung cấp nhiều API cho phép xây dựng MO và các kho dữ liệu giả dễ dàng hơn, cũng như kiểm tra tự động các số liệu trong UT. Nói chung, việc thiết kế MO gồm 3 bước chính sau đây:
1. Đưa ra interface để mô tả đối tượng. Tất cả các tính chất và thủ tục quan trọng cần kiểm tra phải được mô tả trong interface.

2. Viết nội dung cho đối tượng thực dựa trên interface như thông thường.

3. Trích interface từ đối tượng thực và triển khai MO dựa trên interface đó.

Lưu ý MO phải được đưa vào quy trình kiểm nghiệm tách biệt. Cách này có thể sinh ra nhiều interface không thực sự cần thiết, có thể làm cho thiết kế ứng dụng trở nên phức tạp. Một cách làm khác là kế thừa một đối tượng đang tồn tại và cố gắng mô phỏng các hành vi càng đơn giản càng tốt, như trả về một dữ liệu giả chẳng hạn. Đặc biệt tránh tạo ra những liên kết mắt xích giữa các MO vì chúng có thể làm cho thiết kế UT trở nên phức tạp.

TEST-DRIVEN DEVELOPMENT
Trong những năm gần đây, khái niệm TDD được đưa ra dựa trên mô hình phát triển PM khá nổi tiếng XP (Extreme Programming). TDD là một chiến lược phát triển sử dụng kỹ thuật UT theo nguyên tắc tạo ra các công đoạn kiểm nghiệm trước khi xây dựng mã.

Ý tưởng chính của TDD: Trước khi bạn bắt tay viết mã, hãy nghĩ về những gì phải làm trước. Không giống như lập trình truyền thống, trong TDD chúng ta viết mã kiểm tra trước khi viết mã chính, chỉ được viết sau khi đạt đủ số lượng UT cần thiết cho các tình huống có thể xảy ra.

Có thể hiểu TDD là một quy trình vòng tròn bắt đầu bởi các UT với trạng thái đầu tiên là "fail", tiếp theo cần viết mã để các UT chuyển trạng thái "pass", và cuối cùng hiệu chỉnh mã cho đơn giản hơn. Quy trình này được tái diễn liên tục đối với mọi đơn vị chương trình cho đến khi kết thúc hoàn toàn dự án.

Đặc điểm
• Là quy trình phát triển tăng dần theo kịch bản và gắn chặt với các công đoạn kiểm nghiệm trước khi đưa ứng dụng vào vận hành thực sự.

• Là phương pháp phát triển PM ở đó áp dụng kỹ thuật UT tiến hành kiểm tra tất cả các interface, tạo ra các MO cần thiết mô phỏng sự vận hành của ứng dụng ở một nơi riêng biệt.

• Tạo ra bộ khung vận hành tự động cho tất cả các thao tác kiểm nghiệm bộ phận trong hệ thống mỗi khi xây dựng một phiên bản mới.

Lợi ích

• TDD là một kỹ thuật giúp định hình ý tưởng thiết kế hơn là kiểm nghiệm mã chương trình. Thực hiện theo TDD sẽ làm sáng tỏ thêm các yêu cầu bài toán, giải tỏa sự bế tắc trong khi đi tìm giải pháp, phát hiện sớm các vấn đề về thiết kế và tránh được những công việc phải làm lại.

• TDD là một phần bổ trợ không thể thiếu trong các công việc lập trình theo nhóm nhỏ, thường là hai người cùng phát triển một module. Trong mô hình này, luân phiên một người có nhiệm vụ nghĩ về tình huống kiểm tra tiếp theo, viết UT cho tình huống và các MO cần thiết. Người còn lại tập trung viết mã để các UT chuyển sang trạng thái "pass"; giúp giảm thiểu lỗi so với khi làm việc độc lập.

• TDD định hướng cho nhóm thiết kế vận dụng tốt các phương pháp hướng đối tượng (các đối tượng cần kiểm tra phải thực thi một interface là một thí dụ), đặc biệt có thể thu được thiết kế tốt theo hai nguyên tắc:

- Loosely-Coupled: Bất kỳ sự thay đổi nào cũng đều không ảnh hưởng đến các đối tượng khác.

- Highly-Cohesive: Có tính chất khép kín theo nghĩa chỉ thực hiện những chức năng gần với nhau về mặt nghiệp vụ và thiết kế, đồng thời loại ra những chức năng ít có liên quan đến các chức năng chính.

• Lợi ích quan trọng cuối cùng của TDD là xây dựng các đoạn mã chất lượng và an toàn, tập trung hơn, giảm phân mảnh mã và giảm rủi ro xảy ra ngoài dự kiến.

Trong TDD, càng nhiều UT được tạo ra thì càng có nhiều khả năng khống chế nhanh chóng các lỗi nghiêm trọng xảy ra. Các UT càng "mịn" theo nghĩa không thể chia nhỏ hoặc không thể bổ sung được nữa thì khả năng đáp ứng yêu cầu kiểm nghiệm càng cao. Khi đã thiết kế đủ các UT có khả năng phát hiện chính xác bất kỳ một lỗi kỹ thuật nào, chúng ta có thể yên tâm chuyển giao module cho chuyên viên QA kiểm định chức năng (functional testing). Tuy nhiên trong suốt giai đoạn phát triển sau đó cần kiểm tra định kỳ các trạng thái của UT để đảm bảo việc cập nhật không phá vỡ tính đúng đắn của các đoạn mã cũ.

Quy trình thực hiện
Trình tự thực hiện trong TDD như sau:

1. Đối với một module, nghĩ về các công việc sẽ làm và cách kiểm tra công việc đó như thế nào.

2. Tạo test suite ứng với module đó.

3. Bắt tay thiết kế sơ bộ tất cả các UT có thể nghĩ ra. Bước này thực chất là thu thập các tình huống có thể phát hiện lỗi vào một danh sách công việc cần kiểm nghiệm.

4. Viết mã để đảm bảo các UT được biên dịch.

5. Thực thi các UT, vì mã chính của module chưa tồn tại nên trạng thái là "fail".

6. Viết mã cho module để thay đổi trạng thái UT, có thể bổ sung UT nếu cần thiết.

7. Chạy lại toàn bộ test suite và quan sát các UT lỗi, lặp lại bước 6-7 cho đến khi tất cả UT đều đạt trạng thái "pass".

8. Hiệu chỉnh mã để loại bỏ các phần lặp lại, các khối mã và các phân nhánh, liên kết thiếu hợp lý hoặc các khối mã không còn hoạt động... đồng thời viết chú giải các phần quan trọng. Hãy thực hiện công việc này thường xuyên vì chúng ta sẽ không có thời gian quay lại cho công việc hiệu chỉnh.

Bước cuối cùng có ý nghĩa rất lớn trong việc giảm sự phụ thuộc vào các module khác và gia tăng sự độc lập về mặt nghiệp vụ của module hiện hành. Cần lưu ý kiểm tra lại trạng thái tất cả các UT sau mỗi lần hiệu chỉnh vì rất có thể công việc này sẽ gây ra lỗi ở đâu đó.

Chiến lược phát triển với TDD

Mỗi công ty PM đều có cách điều hành quản lý phát triển PM khác nhau, nhưng tất cả đều có chung một mục đích là giảm số lỗi xuống mức nhỏ nhất có thể và khống chế lỗi phát sinh trở lại. Tuy nhiên nhìn chung một quy trình phát triển PM lý tưởng không thể thiếu các bước quan trọng sau đây:

• Thiết kế một dự án thử nghiệm riêng, độc lập, tách biệt với khu vực phát triển. Không gắn dự án thử nghiệm đó vào phiên bản sản phẩm được giao cho khách hàng, vì điều này có thể làm tăng kích thước sản phẩm.

• Xây dựng một cơ sở dữ liệu các test suite cho mọi module phục vụ việc kiểm nghiệm cả hai khía cạnh phát triển và chức năng.

• Chia nhỏ dự án ra nhiều quy trình nhỏ hơn dựa trên ngữ cảnh, giúp việc viết UT được dễ dàng hơn. Để kiểm tra hiệu quả của toàn bộ ứng dụng, tốt nhất là kiểm tra hiệu quả của mọi đơn vị mã nhỏ nhất.

• Có thể thiết lập các cơ sở dữ liệu riêng cho dự án thử nghiệm lưu trữ tất cả các giá trị đầu vào và các kết quả trả về mong muốn... XML sẽ là cách tiếp cận tốt nhất cho những cơ sở dữ liệu loại này.

• Tích hợp công việc kiểm nghiệm thành một phần trong quy trình tự động hoá quản lý mã nguồn như tích hợp toàn bộ công việc, biên dịch vào cuối ngày làm việc... Mỗi một công việc như vậy được gọi là một "build". Về quy trình này có thể tham khảo ứng dụng nguồn mở Ant trong Java (hoặc NAnt cho .NET), hay các công cụ thương mại như CruiseControl hoặc Anthill.

• Cuối cùng thay vì kiểm nghiệm bằng tay, hãy để máy tính thực hiện tự động và gửi báo cáo cho bạn. Các thông báo email tự động hàng ngày về tình trạng của các UT sẽ luôn đảm bảo cho dự án thông suốt. Tất cả các công việc này có thể được tiến hành trên một máy tính riêng có khả năng kiểm soát các thay đổi mã nguồn.

LỜI KẾT

UT là một phương pháp hỗ trợ phát triển PM đang được áp dụng và vẫn đang được hoàn thiện. Kết hợp UT với chiến lược phát triển TDD sẽ giúp bạn xây dựng được các PM chất lượng và ổn định.
CÁC THUẬT NGỮ TRONG UNIT TESTING
•Assertion: Là một phát biểu mô tả các công việc kiểm tra cần tiến hành, thí dụ: AreEqual(), IsTrue(), IsNotNull()... Mỗi một UT gồm nhiều assertion kiểm tra dữ liệu đầu ra, tính chính xác của các lỗi ngoại lệ ra và các vấn đề phức tạp khác như:
- Sự tồn tại của một đối tượng
- Điều kiện biên: Các giá trị có vượt ra ngoài giới hạn hay không
- Thứ tự thực hiện của các luồng dữ liệu
...
• Test Point: Là một đơn vị kiểm tra nhỏ nhất, chỉ chứa đơn giản một assertion nhằm khẳng định tính đúng đắn của một chi tiết mã nào đó. Mọi thành viên dự án đều có thể viết một test point.
• Test Case: Là một tập hợp các test point nhằm kiểm tra một đặc điểm chức năng cụ thể, thí dụ toàn bộ giai đoạn người dùng nhập dữ liệu cho đến khi thông tin được nhập vào cơ sở dữ liệu. Trong nhiều trường hợp kiểm tra đặc biệt và khẩn cấp có thể không cần đến test case.
• Test Suite: Là một tập hợp các test case định nghĩa cho từng module hoặc hệ thống con.
• Regression Testing (hoặc Automated Testing): Là phương pháp kiểm nghiệm tự động sử dụng một phần mềm đặc biệt. Cùng một loại dữ liệu kiểm tra giống nhau nhưng được tiến hành nhiều lần lặp lại tự động nhằm ngăn chặn các lỗi cũ phát sinh trở lại. Kết hợp Regression Testing với Unit Testing sẽ đảm bảo các đoạn mã mới vẫn đáp ứng yêu cầu thay đổi và các đoạn mã cũ sẽ không bị ảnh hưởng bởi các hoạt động bảo trì.
• Production Code: Phần mã chính của ứng dụng được chuyển giao cho khách hàng.
• Unit Testing Code: Phần mã phụ để kiểm tra mã ứng dụng chính, không được chuyển giao cho khách hàng.

Theo PCWorld VN

How to embed source code in BlogSpot


Step1: Go to your Dashboard/Design/Edit HTML.
Step2: Add the following codesnipet right before </body>
<link href='http://lockimage.googlepages.com/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/>
<script language='javascript' src='http://lockimage.googlepages.com/shCore.js'/>
<script language='javascript' src='http://lockimage.googlepages.com/shBrushCpp.js'/>
<script language='javascript'>
dp.SyntaxHighlighter.BloggerMode();
dp.SyntaxHighlighter.HighlightAll('code');
</script>


Now it is ready to add source code to your post. It must be placed in <pre> tag. For example:
<pre name="code" class="Cpp">
...insert code here...
</pre>

Here is the result: