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)
và
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"