<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1170860146330122341</id><updated>2011-08-27T17:56:53.414+07:00</updated><category term='articles'/><category term='WF'/><category term='Prof Asp.Net Server Control'/><category term='agile'/><category term='javascript'/><category term='3D'/><category term='Essential WF'/><category term='books'/><category term='dotnetnuke'/><category term='Java Persistence with Hibernate'/><category term='Hibernate'/><category term='Martin C.Robert'/><category term='Agile Principles: Patterns and Practice in C#'/><category term='Prof DNN (Wrox)'/><category term='Design Patterns (Head First)'/><title type='text'>:-Dzinh on tech</title><subtitle type='html'>books (articles) reviewed only</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>53</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-2777831557057635726</id><published>2010-08-05T09:58:00.000+07:00</published><updated>2010-08-05T09:59:38.367+07:00</updated><title type='text'>...</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fLnGNScTh18/TFoo-zt-e2I/AAAAAAAAEMo/eXtbMxlDKlE/s1600/2010-08-05_095823.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 248px;" src="http://2.bp.blogspot.com/_fLnGNScTh18/TFoo-zt-e2I/AAAAAAAAEMo/eXtbMxlDKlE/s400/2010-08-05_095823.png" alt="" id="BLOGGER_PHOTO_ID_5501754954286529378" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-2777831557057635726?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/2777831557057635726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=2777831557057635726' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2777831557057635726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2777831557057635726'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2010/08/blog-post.html' title='...'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_fLnGNScTh18/TFoo-zt-e2I/AAAAAAAAEMo/eXtbMxlDKlE/s72-c/2010-08-05_095823.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5081390935410089592</id><published>2009-03-26T01:21:00.006+07:00</published><updated>2009-03-26T13:50:49.567+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Agile product design</title><content type='html'>Bữa nay buồn buồn đi lang thang, vô tình mình đọc được các bài viết / presentation về phân tích theo hướng Agile,  sử dụng User Story phải nói là ... quá hay :-D (đang buồn ngủ mà đọc xong là ... tỉnh ngủ luôn)&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;User story mapping&lt;/span&gt; (download &lt;a href="http://www.agileproductdesign.com/downloads/patton_user_story_mapping.ppt"&gt;here&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;User story to user interface&lt;/span&gt; (download &lt;a href="http://www.agileproductdesign.com/downloads/patton_user_story_to_user_interface.ppt"&gt;here&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Embraced uncertainly&lt;/span&gt; (download &lt;a href="http://www.agileproductdesign.com/downloads/patton_embrace_uncertainty_optimized.ppt"&gt;here&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;User experience distilled &lt;/span&gt;(download &lt;a href="http://www.agileproductdesign.com/downloads/patton_user_experience_distilled.ppt"&gt;here&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;..............&lt;/li&gt;&lt;/ul&gt;Cám ơn &lt;a href="http://www.agileproductdesign.com/jeff_patton.html"&gt;Jeff Patton&lt;/a&gt; đã chia sẽ kiến thức một cách cô đọng và dễ hiểu, một cây đại thụ của Agile.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5081390935410089592?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5081390935410089592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5081390935410089592' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5081390935410089592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5081390935410089592'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/03/agile-design.html' title='Agile product design'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-7288394468238468428</id><published>2009-03-26T01:15:00.003+07:00</published><updated>2009-03-26T01:45:48.544+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>NHibernate FAQ</title><content type='html'>Block này viết về NHibernate khá hay, không chỉ đơn thuần là NHibernate mà còn là các thức phát triển phần mềm nữa (test driven, domain driven, seperate of concern, etc.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blogs.hibernatingrhinos.com/nhibernate/Default.aspx"&gt;http://blogs.hibernatingrhinos.com/nhibernate/Default.aspx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ngoài ra nó dùng domain driven, đúng hướng mapping hơn so với template của Codesmith ở bài trước.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-7288394468238468428?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/7288394468238468428/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=7288394468238468428' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7288394468238468428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7288394468238468428'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/03/nhibernate-faq.html' title='NHibernate FAQ'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-580540551520514195</id><published>2009-03-15T14:04:00.003+07:00</published><updated>2009-03-26T01:46:00.297+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>NHibernate template</title><content type='html'>Download codesmith 5.0 professional (trial version) at &lt;a href="www.codesmithtools.com"&gt;codesmithtools&lt;/a&gt;. The template is included there.&lt;br /&gt;&lt;br /&gt;Or download it here: http://codesmith.googlecode.com/svn/trunk/Templates/Frameworks/NHibernate/&lt;br /&gt;&lt;br /&gt;Watch the tutorial video &lt;a href="http://community.codesmithtools.com/cfs-filesystemfile.ashx/__key/CommunityServer.Components.PostAttachments/00.00.03.32.61/nhibernate.swf"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Install TestDriven &lt;a href="http://www.testdriven.net/downloads/TestDriven.NET-2.19.2409_Personal.zip"&gt;here&lt;/a&gt;. (one plug in for UnitTest in VS 2008)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-580540551520514195?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/580540551520514195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=580540551520514195' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/580540551520514195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/580540551520514195'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/03/nhibernate-template.html' title='NHibernate template'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6108424449222678671</id><published>2009-03-13T00:42:00.003+07:00</published><updated>2009-03-26T01:46:11.591+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='3D'/><title type='text'>3D nhập môn</title><content type='html'>Quy trình hiển thị 3D bao gồm các bước:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Modeling Transformation: biến đổi từ hệ tọa độ đối tượng sang hệ tọa độ thế giới thực.&lt;/li&gt;&lt;li&gt;Trivial rejection: loại bỏ các đối tượng không nhìn thấy được.&lt;/li&gt;&lt;li&gt;Illumination: chiếu sáng đối tượng.&lt;/li&gt;&lt;li&gt;Viewing transformation: chuyển ừ world space sang eye space.&lt;/li&gt;&lt;li&gt;Clipping: loại bỏ phần nằm ngoài viewing frustum (góc nhìn hình chóp cụt).&lt;/li&gt;&lt;li&gt;Projection: chiếu từ eye space xuống screen space.&lt;/li&gt;&lt;li&gt;Rasterization: chuyển từ đối tượng sang dạng pixel.&lt;/li&gt;&lt;li&gt;Display: hiển thị đối tượng.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6108424449222678671?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6108424449222678671/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6108424449222678671' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6108424449222678671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6108424449222678671'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/03/3d-nhap-mon.html' title='3D nhập môn'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-7742732821084072981</id><published>2009-03-13T00:31:00.005+07:00</published><updated>2009-03-13T00:41:26.467+07:00</updated><title type='text'>Code đẹp và code dễ đọc</title><content type='html'>&lt;span style="font-style: italic;"&gt;(dịch từ&lt;/span&gt; &lt;a href="http://binstock.blogspot.com/2007/12/beautiful-code-vs-readable-code.html"&gt;&lt;span style="font-style: italic;"&gt;Beautiful code vs Readable Code&lt;/span&gt;&lt;/a&gt;, &lt;span style="font-style: italic;"&gt;có sự hỗ trợ của google translator :-D)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Trong nhiều năm - thực sự  nhiều thập kỷ qua - Tôi là một fan hâm mộ lớn của code đẹp. Tôi đọc gần như tất cả mọi thứ của Brian Kernighan, Jon Bentley, và P. J. Plauger. Niềm đam mê này là một cố gắng tạo lại sự hăm hở (rush) khi tôi đọc được đầu tiên dòng code:&lt;br /&gt;&lt;br /&gt;*x++ = *y++&lt;br /&gt;&lt;br /&gt;Trong ngôn ngữ lập trình. Tôi chưa bao giờ nhìn thấy bất cứ đoạn code nào cô đọng đẹp đến vậy. Nó đã được phát minh! Nhưng nhiều năm qua, tôi đọc rất nhiều các thuật toán thông minh, rất nhiều tối ưu hóa ấn tượng, rất nhiều thủ thuật nhỏ. Và tôi đã nhận ra càng ngày càng ít hơn những gì thu được (charge) trong những khám phá đó. Lý do là, khá thẳng thắn, là chúng gần như luôn luôn rơi vào một trong hai loại: một số cách thể hiện rất tao nhã trong một ngôn ngữ mới (Ruby được chuyển từ Java là một minh chứng) hoặc một kỹ thuật mà tôi không bao giờ có khả năng để sử dụng. Nói cách khác, tôi đang theo đuổi những món đồ chơi rẻ tiền. Theo thời gian, khiếu thẩm mỹ của tôi chuyển thành việc code một cách sáng sủa vì sự dễ chịu của nó.&lt;br /&gt;&lt;br /&gt;Bây giờ, nếu tôi có thể chọn ra một đoạn code phức tạp, đọc nó lướt qua, và hiểu một cách chính xác những gì nó làm, thì tôi cảm thấy hăm hở trở lại. Tôi thường có cảm giác này khi đọc các đoạn code từ những lập trình viên giỏi, hoặc không hàng lâm. Nói một cách trung thực, khi miệt mài trong khoảnh khắc như vậy, tôi thường xuyên nhận thức rằng đoạn code của tôi không giống như họ. Thậm chí đoạn code hay nhất của tôi hoàn toàn không giống với họ một chút nào. Và tôi đã tự hỏi tôi có thể làm gì để cải thiện tính rõ ràng (clarity) trong đoạn code của tôi.&lt;br /&gt;&lt;br /&gt;Cuốn sách mới của Kent Beck, Implementation Pattern là một cuốn sách tóm tắt ngắn về code rõ ràng.  Tôi đã đọc nhiều về nó và tôi đã nhận ra rằng một số thói quen xấu của tôi đã làm phá hoại đi tính đọc  được của đoạn code.  Beck cơ bản chỉ nhìn vào các đoạn code có vấn đề và tư vấn những lời khuyên khôn ngoan.&lt;br /&gt;&lt;br /&gt;Điều này có nghĩa là vài lời khuyên là thích hợp nhất cho người mới bắt đầu, trong khi chúng chỉ đủ là các lời gợi ý tế nhị để giữ sự chú ý của một người kỳ cựu, người quan tâm đến sự rõ ràng.&lt;br /&gt;Ví dụ, một trong những thói quen xấu là tôi thường code mà không không cần suy nghĩ nhiều về sự pha trộn của các mức độ trừu tượng hóa trong cùng một method. Vì vậy, ví dụ (sử dụng ví dụ của Beck):&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;void process() {&lt;br /&gt;   input();&lt;br /&gt;   count++;&lt;br /&gt;   output();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Dòng code thứ hai rõ ràng ở một mức độ trừu tượng khác với hai cái kia, và làm cho đoạn code khó đọc một cách nhanh chóng. Beck đề nghị giải pháp sau đây, mà tôi đồng ý là rõ ràng hơn.&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;void process() {&lt;br /&gt;   input();&lt;br /&gt;   tally();&lt;br /&gt;   output();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Có nhiều thói quen xấu khác của tôi mà cuốn sách này đã soi sáng. Và trong nửa tá thay đổi sẽ mang đến cho phong cách của tôi, tôi nghĩ rằng tôi đã thu được nhiều lợi ích hơn so với trong tất cả các bài luận tôi đã đọc về chủ đề beautiful code.&lt;br /&gt;&lt;br /&gt;Tuy nhiên trước khi kết thúc, tôi nên đưa ra hai lời cảnh báo: Tài liệu cho người mới bắt đầu đến trung cấp đang tràn lan, vì vậy bạn sẽ cần phải lươm lặt trên phần lớn đống chữ nghĩa đó để rút trích ra các thông tin có giá trị. (Tuy nhiên, khía cạnh này tạo món quà cho các lập trình viên junior ở vị trí của bạn.) Điểm thứ hai là cuốn sách này thiếu chỉnh sửa tốt. Một cuốn sách về clarity code thì nên trong sáng; cuốn này thì không.  (Xem xét việc sử dụng các từ ‘mẫu từ,' nó rất dể gây hiểu lầm cao. Và không phải tất các mẫu từ đều như vậy.) Nhưng những vấn đề này có thể chấp nhận được.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-7742732821084072981?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/7742732821084072981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=7742732821084072981' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7742732821084072981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7742732821084072981'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/03/code-ep-va-code-de-oc.html' title='Code đẹp và code dễ đọc'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-395224164693872491</id><published>2009-03-12T12:42:00.001+07:00</published><updated>2009-03-12T12:43:32.952+07:00</updated><title type='text'>Test syntax highlighter</title><content type='html'>&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;while (relax() == true)&lt;br /&gt;{&lt;br /&gt;  destroy();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-395224164693872491?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/395224164693872491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=395224164693872491' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/395224164693872491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/395224164693872491'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/03/test-syntax-highlighter.html' title='Test syntax highlighter'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-7892260890183180260</id><published>2009-01-06T17:35:00.003+07:00</published><updated>2009-01-06T17:37:10.434+07:00</updated><title type='text'>Extjs</title><content type='html'>Cứ nghĩ khi dùng ExtJs để viết GUI thì quanh đi quẩn laị cũng có nhiêu đó, nhưng thật bất ngờ khi thấy có những site viết GUI trên đó khá cool&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fLnGNScTh18/SWM0DMllPnI/AAAAAAAABjQ/m2tvBsDpo4Q/s1600-h/versochat-console.jpg"&gt;&lt;img style="cursor: pointer; width: 400px; height: 315px;" src="http://3.bp.blogspot.com/_fLnGNScTh18/SWM0DMllPnI/AAAAAAAABjQ/m2tvBsDpo4Q/s400/versochat-console.jpg" alt="" id="BLOGGER_PHOTO_ID_5288127616986201714" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fLnGNScTh18/SWM0KJF_-DI/AAAAAAAABjY/Oo8TpjoTnu8/s1600-h/MyHome.gif"&gt;&lt;img style="cursor: pointer; width: 400px; height: 231px;" src="http://1.bp.blogspot.com/_fLnGNScTh18/SWM0KJF_-DI/AAAAAAAABjY/Oo8TpjoTnu8/s400/MyHome.gif" alt="" id="BLOGGER_PHOTO_ID_5288127736307513394" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fLnGNScTh18/SWM0Q8kv6UI/AAAAAAAABjg/FtzJ_E0DBFs/s1600-h/ManageResources.gif"&gt;&lt;img style="cursor: pointer; width: 400px; height: 256px;" src="http://4.bp.blogspot.com/_fLnGNScTh18/SWM0Q8kv6UI/AAAAAAAABjg/FtzJ_E0DBFs/s400/ManageResources.gif" alt="" id="BLOGGER_PHOTO_ID_5288127853205907778" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-7892260890183180260?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/7892260890183180260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=7892260890183180260' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7892260890183180260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7892260890183180260'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2009/01/extjs.html' title='Extjs'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fLnGNScTh18/SWM0DMllPnI/AAAAAAAABjQ/m2tvBsDpo4Q/s72-c/versochat-console.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-1428286891324614799</id><published>2008-12-21T22:56:00.003+07:00</published><updated>2008-12-21T23:17:26.782+07:00</updated><title type='text'>Con đường của Semantic Web</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Semantic web là gì?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Theo định nghĩa của wikipedia, Semantic Web là một dự án dự định tạo ra một thiết bị (phương tiện) toàn cầu dùng cho việc trao đổi thông tin bằng cách đặt các văn bản cùng với ý nghĩa được xử lý bởi máy tính (ngữ nghĩa) trong World Wide Web. Ý tưởng chính là tạo ra một meta data dùng để miêu tả data, cho phép các máy tính xử lý ý nghĩa của chúng. Một khi các máy tính được trang bị với các khả năng hiểu được ngữ nghĩa, nó sẽ có khả năng giải quyết các vấn đề tối ưu hóa ngữ nghĩa phức tạp. Ví dụ, như John Markoff miêu tả trong bài viết của mình, một máy tính sẽ có thể trả ra kết quả tức thời nếu bạn nói với nó tìm một chỗ nghỉ với giá tiền khoảng 3K. Ngoài khả năng trên, thông tin trên web cần được giải thích với các miêu tả và các quan hệ. Ví dụ cơ bản của semantices bao gồm việc phân loại đối tượng và thuộc tính của nó. Ví dụ các cuốn sách nằm trong một danh mục sách thì có các thuộc tính như là tác giả, số trang, ngày xuất bản. Ví dụ về mối quan hệ đến từ các mạng xã hội khác nhau. Trong một mạng lưới quan hệ có thể là "một người bạn của", hoặc "một thành viên của gia đình.." hoặc là "làm việc với".&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;RDF, OWL và cách tiếp cận mang tính toán học để chú thích&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Có hàng tỉ trang web không có cấu trúc HTML không chứa meta data và annotations (lời chú giải). Câu hỏi cơ bản là làm cách nào chúng ta có thể đi từ web không có cấu trúc hiện nay đến một thứ giàu thông tin ngữ nghĩa hơn. Tổ chức W3C đã xuất bản các đặc tả cho RDF (Resource Description Framework) và OWL (Web Ontology Languages) cố gắng cho phép việc thu thập và mô tả thông tin theo tập hợp, cùng với bản chất học (ontology) và các mối quan hệ với các mảng thông tin khác, một cách chính xác và toán học.&lt;br /&gt;&lt;br /&gt;RDF là một ngôn ngữ dựa trên XML cho phép miệu tả mối quan hệ thông qua các vị từ (predicates). Wikipedia giải thích: Subject dùng để chỉ về resoruce, và vị từ chỉ về các nét chính (traits) hay các khía cạnh của resource và diễn tả mối quan hệ giữa subject và object. Ví dụ, một cách để diễn tả "The sky has the color blue" trong RDF là câu gồm có 3 phần: một chủ ngữ chỉ về "the sky", một vị từ chỉ về "has the color" và một object chỉ về "blue".&lt;br /&gt;&lt;br /&gt;OWL là một ngôn ngữ dựa trên XML khác dùng để miêu tả và giải thích các bản chất vấn đề (ontologies - bản thể học). Một cách tóm gọn, OWL tận dụng các mô tả ngữ nghĩa chẳng hạn như con chó nó một con vật, hoặc con chó có 4 chân. Có 3 dạng OWL: OWL Lite, OWL DL, và OWL full - mỗi loại có những khía cạnh khác nhau và có sự trả giá lẫn nhau giữa việc diễn tả và khả năng tính toán để hiểu được nó. Framework RDF/OWL này dễ hiểu, nhưng khó cho người nò không có nền tảng toán hoc và khoa học máy tính. Cho rằng có một cách tiếp cận từ dưới lên, rõ ràng rằng để thành công, cần phải tồn tại một cơ chế lấy các nội dung html đã tồn tại và chuyển nó về RDF hoặc OWL meta data. Tuy nhiên, việc này là một vấn đề giống như quả trứng với con gà cái nào có trước (chicken-egg problem). Chúng ta có thể mường tượng ra các tool thực hiện khoảng 80% công việc tự động và sau đó tương tác với con người để thực hiện 20% công việc còn lại.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Microformats&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Nhận ra tính phức tạp của RDF và OWL, một nhóm người đang cố gắng thực hiện một cách tiếp cận khác gọi là Microformats. Mục tiêu của microformats là nhúng các sematics cơ bản vào trong ngay các trang HTML. Cách này diễn đạt không bằng RDF và OWL, nhưng nó rất cô động và sử dụng các xHTML để thêm vào ngữ nghĩa cho trang. Ví dụ có một microformat dùng để miêu ta các thông tin về contact, gọi là hCard. Sử dụng hCard để chú thích vào trong HTML để các browser nào "biết được" microformat hoặc một search engine có thể suy luận / rút trích ra thông tin về người, chẳng hạn như first và last name, tên công ty và phone number. Có một trong những microformat sau này tên là hCalendar cho phép các tác giả của trang miêu tả các events. Nhiều các event site thông dụng như Facebook, và Yahoo! local sử dụng event này để chú thích các event trong trang HTML của họ.&lt;br /&gt;&lt;br /&gt;Bỏ đi nét đẹp của việc diễn tả sang một bên (aesthetics), tiếp cận theo microformat rõ ràng đơn giản hơn là RDF và OWL. Và ngay cả khi nó ít mạnh hơn, nó cũng trở nên rất thông dụng. Nhiều tác giả của trang web đã bắt đầu nhúng microformat và trong html page của họ. Chúng ta cũng đang tìm kiếm các ví dụ ban đầu của search engine dựa trên microformat, giống như trang này từ Technorati. Việc đơn giản sử dụng microformat và làm công việc tìm kiếm này đang bỏ đi sự tối nghĩa, nhập nhằng (ambiguity). Nói cách khác, nó tương tự như search theo chiều dọc, trong đó biết chiều nào mà bạn đang tìm kiếm. Với microformat bên trong trang, data không còn tối nghĩa nữa, và kết quả tìm kiếm sẽ được chính xác hơn.&lt;br /&gt;&lt;br /&gt;Tuy nhiên, vẫn còn vài vấn đề với microformats. Đầu tiên nó giống như ví dụ từ dưới lên ở trên - người ta phải làm việc để ghi chú lại web site. Tin mới nhất là vì format này đơn giản hơn - ngày càng có nhiều trang được làm xong thông qua reverse engineering và automation. Vấn đề thứ hai là tập hợp các microformat hiện tại không bao phủ hết các nhiều thứ chúng ta gặp online. Ví dụ, chúng ta không quan tâm đến format nào giúp chúng ta diễn tả sách hay movie. Nên có nhiều format cần phải được tạo ra hơn nữa trước khi chúng thật sự "cover" được thế giới web.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Semantic Web là web cá nhân (personalize web)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Vấn đề ghi chú dữ ileu65 rất phức tạp và còn rất xa để nó có thể được giải quyết một cách hoàn toàn. Nhưng chúng ta hãy để nó sang một bên và nghĩ về những thứ mà chúng ta có thể làm một khi tất cả các data đều được ghi chú. Hứa chắc rằng, khi đó chúng ta sẽ làm ít hơn những gì chúng ta đang làm bây giờ -đó là sàn lọc thông qua một đống các thông tin có liên quan. Cho trước một lượng thông tin nào đó đang gia tăng với cấp số mũ và sức chịu đựng của chúng ta thì càng ngày càng ít lại. Đó là một lời tuyên bố thật hấp dẫn. Nếu máy tính có thể trả về các kết quả tìm kiếm ngay lập tức, chúng ta có thể tiết kiếm hàng đống thời gian.&lt;br /&gt;&lt;br /&gt;Nhưng có một ngữ nghĩa và biết được tất cả các mối quan hệ giữa các dữ liệu vẫn không đủ để làm việc đó. Lấy một ví dụ đơn giản của một travel agency. Khi bạn xuất hiện (hay tìm kiếm) lần đầu, agent không biết gì để offer cho bạn, ngay cả khi cô ấy biêt semantics của các chuyến du lịch đi nữa, hoặc mối quan hệ giữa các thứ và giá của mọi thứ. Để có hiệu quả, có ấy cần phải biết bạn đã ở đâu rồi, và bạn muốn đến đâu. Do đó cô ta hỏi bạn vài câu hỏi. Tất cả các dịch vụ mà chúng ta nhận được sẽ làm việc theo cách này và kết quả sẽ tốt hơn và hiệu quả hơn theo thời gian, vì người làm dịch vụ sẽ có thời gian để học được bạn thích gì.&lt;br /&gt;&lt;br /&gt;Do đó thành phần quan trọng thứ hai của Semantic Web, là cái mà bạn sẽ nâng cao hiệu suất, là một tập cả preference về thông tin cá nhân. Một khi máy tính biết preferences của bạn và có một representation semantic online, nó sau đó có thể chạy một giải thuật để mang lại cho bạn một kết quả chính xác, và mang tính cá nhân hơn. Để làm chó nó khác đi, tham khảo cá nhân của bạn là một filter cần thiết để được áp dụng vào kết quảtìm kiếm của bạn "Tìm một chỗ nghĩ dưới 3K". Khi điều này xảy ra, sau đó ta có thể tuyên bố Semantic Web đã thành công.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Kết luận&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Vậy web 3.0 có phải là semantic web? có thể. Và chúng ta chưa ở đó?. Không phải hoàn toàn. Nó sẽ tốn thời gian để chú thích thông tin của thế giới và sau đó lấy thông tin của cá nhân một các đúng đắn, để bắt đầu cho các dạng ứng dụng mà chúng ta đã đề cập. Chúng ta nhất định sẽ đi gần đến đích và sẽ rất thích thú để thấy mọi thứ sẽ được mở ra như thế nào sau vài năm tiếp theo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-1428286891324614799?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/1428286891324614799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=1428286891324614799' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1428286891324614799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1428286891324614799'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/12/con-ng-ca-semantic-web.html' title='Con đường của Semantic Web'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6187865745265244805</id><published>2008-12-19T11:53:00.001+07:00</published><updated>2008-12-19T11:56:51.557+07:00</updated><title type='text'>Tiếp Nhận Trách Nhiệm (Accepted Responsibility)</title><content type='html'>&lt;p style="margin-bottom: 0cm; text-align: justify;"&gt; Trách nhiệm không thể được gán cho ai đó; nó chỉ có thể được tiếp nhận bởi người nào đó. Nếu ai đó cố gắng trao trách nhiệm cho bạn, thì bạn là người duy nhất quyết định có nhận trách nhiệm đó hay không. &lt;/p&gt;&lt;div&gt; &lt;/div&gt;&lt;div style="text-align: justify;"&gt; &lt;/div&gt;&lt;p style="margin-bottom: 0cm; text-align: justify;"&gt;Các practice phản ánh principle tiếp nhận trách nhiệm theo nhiều cách. Lấy ví dụ, bất kỳ ai nhận làm một việc, họ sẽ ước lượng nó. Tương tự, ai chịu trách nhiệm cài đặt câu chuyện sẽ là người chịu trách nhiệm sau cùng về thiết kế, cài đặt và kiểm thử câu chuyện đó. &lt;/p&gt;&lt;div style="text-align: justify;"&gt; &lt;/div&gt;&lt;div style="text-align: justify;"&gt; &lt;/div&gt;&lt;p style="margin-bottom: 0cm; text-align: justify;"&gt;&lt;span style="font-weight: bold;"&gt;Trách nhiệm đi liền với quyền hạn&lt;/span&gt;. Sự bất cân đối giữa trách nhiệm và quyền hạn bóp méo truyền thông của nhóm. Khi một chuyên gia qui trình nói cho tôi cách làm việc, nhưng không chia sẻ gì về công việc đó hoặc hậu quả hay kết quả của công việc đó, thì quyền lực và trách nhiệm là không cân xứng. Với tư cách là những trí thức, không một ai trong chúng ta có thể nhìn thấy hay sử dùng các phản hồi để có thể cải tiến. Sự mất cân xứng còn làm tổn thất về cảm xúc cuộc sống.&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm; text-align: justify;"&gt;&lt;span style="font-style: italic;"&gt;(thanks Vi for your sharing - reference &lt;/span&gt;&lt;a style="font-style: italic;" href="http://docs.google.com/View?docid=ddp9sqjm_214vpwzt7hg"&gt;here&lt;/a&gt;&lt;span style="font-style: italic;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6187865745265244805?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6187865745265244805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6187865745265244805' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6187865745265244805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6187865745265244805'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/12/tip-nhn-trch-nhim-accepted.html' title='Tiếp Nhận Trách Nhiệm (Accepted Responsibility)'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5338841358024658347</id><published>2008-11-27T11:51:00.001+07:00</published><updated>2008-11-27T11:54:33.072+07:00</updated><title type='text'>Refactoring</title><content type='html'>&lt;span style="font-style: italic;"&gt;(copy từ bài viết mình để trong &lt;a href="http://www.pyramid-consulting.com.vn/tools/channel8/forums/ShowThread.aspx?PostID=2851#2851"&gt;C8&lt;/a&gt; :-D)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Rất vui khi có người nào đó quan tâm đến chủ đề &lt;b&gt;Refactor&lt;/b&gt;. Nếu đã từng là một lập trình viên, một team leader, ... bạn không thể nào tránh được việc này. Ai càng suy nghĩ nhiều về nó, chứng tỏ người đó càng quan tâm nhiều đến chất lượng code, và về mặt logic họ sẽ develop ra được nhiều good code hơn những người khác. &lt;i&gt;(thật ra chủ đề về code quality rộng hơn refactoring)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Refactoring &lt;/b&gt;phản ánh tự nhiên của cuộc sống - tôi không phải là siêu nhân mà bắt buộc phải làm tốt tất cả ngay từ đầu. Chỉ đến khi nào cảm thấy cần phải cải tiến nó, tôi cải tiến. Đôi khi tôi ép mình hơn nữa bằng những suy nghĩ như - liệu nó có còn tốt hơn được nữa - và nếu được, tôi lại tiếp tục cải tiến. Ngoài ra, việc tiếp cần một cách tự nhiên này, giúp cho ta thoát khỏi một lỗi lầm hay thường gặp: &lt;i&gt;Premature optimize is the root of all evil&lt;br /&gt;&lt;br /&gt;&lt;/i&gt;&lt;b&gt;Design patterns&lt;/b&gt; là kết quả của một quá trình refactoring (tôi nghĩ vậy :-)). Có điều tác giả không muốn cho chúng ta lại phải giải quyết lại những gì mà họ đã giải quyết, nên xây dựng sẵn, đặt tên cho nó, và làm mẫu để cho chúng ta nhận diện. Cách thức nhận diện design pattern để áp dụng người ta gọi là &lt;i&gt;heuristic&lt;/i&gt;. Có điều nếu các bạn muốn thật sự hiểu rõ design pattern, các bạn phải hiểu được quá trình refactoring của nó để ra được hình mẫu đó, hơn là dừng lại ở việc nhận dạng pattern và áp dụng nó. Cũng giống như đánh võ vậy - lúc ra trận thì ai biến hóa được nhiều nhất, linh hoạt nhất thì sẽ thắng, và lúc đó chưa chắc tất cả các chiêu thuộc về một bài quyền đâu, hehe :-D&lt;br /&gt;&lt;br /&gt;Có một câu hỏi đặt ra là - có khi nào bạn không thể làm tốt hơn được trong thời điểm đó? Điều mà tác giả bài viết trước gọi nôm na là &lt;b&gt;Ngụy Biện&lt;/b&gt;. Thật ra trong thực tế điều này xảy ra rất nhiều, vd như:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Khách hàng kêu giao code liền, copy paste hay làm cái gì để produce liền cho sếp coi là nhanh nhất :-D&lt;/li&gt;&lt;li&gt;QC báo bug muốn fix liền, mình đang làm cái khác nên nhảy vô edit vài chỗ cho nó chạy liền, hơi đâu mà refactor chi cho mệt.&lt;/li&gt;&lt;li&gt;Cái code cũ này thằng nào viết chuối quá, mà biết gì đâu mà sửa, thôi ngồi change vài cái thôi.&lt;/li&gt;&lt;li&gt;....&lt;/li&gt;&lt;/ul&gt;Nếu người nào cố tình phớt lờ những vấn đề trên, và mong muốn phải refactor mọi lúc mọi nơi, hoặc làm tốt ngay từ đầu thì người đó mới là.... :-) &lt;b&gt;&lt;br /&gt;&lt;br /&gt;Martin Fowler&lt;/b&gt; có một khái niệm gọi là &lt;a href="http://martinfowler.com/bliki/TechnicalDebt.html"&gt;Technical Debt&lt;/a&gt;, tức là cho bản thân mình (hoặc người khác) nợ về một đoạn code nào đó chưa thật sự là tốt nhất trong thời điểm đó. Khi nào có thể tôi sẽ quay lại làm tốt nó. Người lập trình viên sẽ tạo ra một danh sách nợ đó và quản lý nó, cố gắng trả nợ càng sớm càng tốt vì có thể nó sẽ sinh ra lãi :-D (để càng lâu thì bug càng dễ phát sinh hoặc càng khó improve sau này vì có thể có nhiều class khác phụ thuộc vào nó). Như vậy vấn đề không phải ở chỗ anh viết code tốt hay xấu ở thời điểm đó. Vấn đề là anh phải ý thức được rằng, anh đang mắc nợ, hoặc anh có muốn bị mắc nợ hay không. Và nếu muốn chê bai hay chỉ trích một đoạn code nào đó, cứ xem thử anh developer đang viết có biết được mình đang ... chuối chỗ nào không? Nếu code đã chuối, mà mình cũng ko biết mình chuối, thì công nhận là... chuối thiệt :-D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5338841358024658347?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5338841358024658347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5338841358024658347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5338841358024658347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5338841358024658347'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/11/refactoring.html' title='Refactoring'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-8562332157082887000</id><published>2008-08-10T13:21:00.007+07:00</published><updated>2008-08-10T13:51:02.194+07:00</updated><title type='text'>MVP (2)</title><content type='html'>Theo quan điểm của mình thì MVP chỉ là một hình thức khác của MVC. Presenter cũng là một dạng của Controller, chỉ nhấn mạnh ở một số điểm:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Mối quan hệ giữa view và controller: Presenter không bắt buộc phải handle các sự kiện của view mà chính view sẽ làm việc đó. View chỉ thông báo các sự kiện mà nó muốn cho Presenter mà thôi. Và việc Presenter chỉ quan tâm đến View như là một interface làm tăng tính độc lập của cả hai và tính test được nữa.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Mối quan hệ giữa view và model: trong một số trường hợp cần thiết, mô hình cho phép View tương tác trực tiếp với model, nhưng thật sư ở đây mình thấy nếu thông qua Presenter thì chính thống hơn và chả khác gì cả.&lt;/li&gt;&lt;/ul&gt;Martin Fowler chia MVP ra làm hai dạng: &lt;span style="font-weight: bold;"&gt;Passive View&lt;/span&gt; và &lt;span style="font-weight: bold;"&gt;SuperVising Conroller&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_fLnGNScTh18/SJ6OC4739AI/AAAAAAAAAic/zNDWCA0Vjsc/s1600-h/PassiveViewAndSupervisingController.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_fLnGNScTh18/SJ6OC4739AI/AAAAAAAAAic/zNDWCA0Vjsc/s400/PassiveViewAndSupervisingController.png" alt="" id="BLOGGER_PHOTO_ID_5232775997345494018" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Để hiểu rõ hơn ta hãy xem MVP Quickstart của &lt;a href="http://www.codeplex.com/websf/Release/ProjectReleases.aspx?ReleaseId=8812"&gt;WebClient Software Factory&lt;/a&gt; như sau:&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-8562332157082887000?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/8562332157082887000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=8562332157082887000' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/8562332157082887000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/8562332157082887000'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/08/mvp-2.html' title='MVP (2)'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_fLnGNScTh18/SJ6OC4739AI/AAAAAAAAAic/zNDWCA0Vjsc/s72-c/PassiveViewAndSupervisingController.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5348971397516465570</id><published>2008-08-10T02:03:00.009+07:00</published><updated>2008-08-10T02:22:27.346+07:00</updated><title type='text'>Setting PropertyBehavior with RhinoMock</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fLnGNScTh18/SJ3tm9XRZtI/AAAAAAAAAiU/6qeJcbHjkaI/s1600-h/2008-08-10_021749.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_fLnGNScTh18/SJ3tm9XRZtI/AAAAAAAAAiU/6qeJcbHjkaI/s400/2008-08-10_021749.png" alt="" id="BLOGGER_PHOTO_ID_5232599595637368530" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fLnGNScTh18/SJ3tj1czchI/AAAAAAAAAiM/Gp5noqzrcBQ/s1600-h/2008-08-10_021820.png"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_fLnGNScTh18/SJ3tj1czchI/AAAAAAAAAiM/Gp5noqzrcBQ/s400/2008-08-10_021820.png" alt="" id="BLOGGER_PHOTO_ID_5232599541973479954" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ví dụ trên cho thấy ta không cần tạo một stub object để test, implement từ interface IAnimal. Thay vào đó, RMock sẽ tự động tạo cho ta. PropertyBehavior mục tiêu dùng để generate ra property Legs cho chúng ta sử dụng (dựa trên get/set của interface)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5348971397516465570?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5348971397516465570/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5348971397516465570' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5348971397516465570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5348971397516465570'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/08/setting-propertybehavior-with-rhinomock.html' title='Setting PropertyBehavior with RhinoMock'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_fLnGNScTh18/SJ3tm9XRZtI/AAAAAAAAAiU/6qeJcbHjkaI/s72-c/2008-08-10_021749.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-2194508440333449073</id><published>2008-08-10T00:31:00.009+07:00</published><updated>2008-08-10T01:34:13.262+07:00</updated><title type='text'>Sự khác biệt giữa Mocks và Stubs</title><content type='html'>Khi được giới thiệu ban đầu, nhiều người dễ dàng lầm lẫn các mock object với các testing sử dụng sử dụng stubs. Xem hai dạng testing tôi sử dụng trong ví dụ bên dưới, cái đầu tiên sử dụng warehouse object thật sự, và cái thứ hai sử dụng một mock warehouse - và nó không phải là một warehouse object thật sự. Sử dụng mocks là một cách không sử dụng object thật sự khi test, nhưng cũng có những dạng khác sử dụng object giả giống như vậy.&lt;br /&gt;&lt;br /&gt;Có một số từ rất dễ làm cho chúng ta rối, nó là: sub, mock, fake, dummy. Trong bài viết này tôi tham khảo trong cuốn sách của Gerard Meszaros. Meszaros dùng thuật ngữ Test Double như là một thuật ngữ chung, áp chỉ đến bất dạng object giả nào dùng để thay thế cho một object thiệt cho mục đích test. Tên của nó lấy từ khái niệm (notion) của Stunt Double trong phim. Meszaros sử dụng bốn dạng double riêng biệt:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;   &lt;span style="font-weight: bold;"&gt;Dummy &lt;/span&gt;objects là những object được dùng đến để làm cho pass một cái gì đó, nhưng không bao giờ được sử dụng, ví dụ như ta dùng nó để fill các parameter list.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Fake &lt;/span&gt;objects thật sự được sử dụng trong implementation, nhưng do nó được làm một cách vắn tất nên thường không thích hợp trong production (&lt;a href="http://www.martinfowler.com/bliki/InMemoryTestDatabase.html"&gt;InMemoryTest &lt;/a&gt;database là một ví dụ).&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Stubs &lt;/span&gt;cung cấp các câu trả lời được đóng sẵn (canned) đến các lời gọi hàm tạo ra trong suốt quá trình test, thông thường nó không trả ra kết quả chút nào ngoài những gì được lập trình để test. Stub cũng được dùng để lưu lại thông tin về những lời gọi, chẳng hạn như một email gateway stub có thể nhớ những message mà nó đã gởi, hoặc đã gởi bao nhiêu cái.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Mocks&lt;/span&gt;: là những objects được lập trình trước với  mong muốn hình thành một đặc tả của những lời gọi hàm mà chúng mong muốn được nhận.&lt;/li&gt;&lt;/ul&gt;Trong những dạng double này, chỉ có mock khăng khăng dựa trên việc xác nhận behavior. Các double khác có thể, và thường là dùng xác nhân thông qua trạng thái (state). Mock thông thường hành xử như các double khác, chỉ khác trong giai đoạn setup và verification.&lt;br /&gt;&lt;br /&gt;Xem ví dụ sau:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fLnGNScTh18/SJ3f9Xg9vcI/AAAAAAAAAhE/nYMik5Vbk_Y/s1600-h/2008-08-10_012001.png"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_fLnGNScTh18/SJ3f9Xg9vcI/AAAAAAAAAhE/nYMik5Vbk_Y/s400/2008-08-10_012001.png" alt="" id="BLOGGER_PHOTO_ID_5232584587451678146" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;message&gt;&lt;message&gt;&lt;br /&gt;Chúng ta có thể sử dụng state verification trên stub như sau:&lt;br /&gt;&lt;/message&gt;&lt;/message&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_fLnGNScTh18/SJ3hmLTcDUI/AAAAAAAAAhM/ROAPXjsUhC8/s1600-h/2008-08-10_012337.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_fLnGNScTh18/SJ3hmLTcDUI/AAAAAAAAAhM/ROAPXjsUhC8/s400/2008-08-10_012337.png" alt="" id="BLOGGER_PHOTO_ID_5232586388059983170" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;message&gt;&lt;message&gt;&lt;br /&gt;Sử dụng mock object để test thì hoàn toàn khác&lt;br /&gt;&lt;/message&gt;&lt;/message&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fLnGNScTh18/SJ3iKsL3E1I/AAAAAAAAAhc/W6qJW2BLXwU/s1600-h/2008-08-10_012931.png"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_fLnGNScTh18/SJ3iKsL3E1I/AAAAAAAAAhc/W6qJW2BLXwU/s400/2008-08-10_012931.png" alt="" id="BLOGGER_PHOTO_ID_5232587015361860434" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;message&gt;&lt;message&gt;Trong cả hai trường hợp, tôi đã dùng test double để test thay vì mail service thật sự. Sự khác biệt ở đây là stub sử dụng state verification trong khi mock sử dụng behavior verification. Để sử dụng state verification trong stub, tôi cần phải tạo thêm các method thêm vào để hỗ trợ việc test.&lt;br /&gt;&lt;br /&gt;(tham khảo từ &lt;a href="http://www.martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs"&gt;martin folwer&lt;/a&gt;)&lt;/message&gt;&lt;/message&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-2194508440333449073?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/2194508440333449073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=2194508440333449073' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2194508440333449073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2194508440333449073'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/08/mocks-and-stubs.html' title='Sự khác biệt giữa Mocks và Stubs'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_fLnGNScTh18/SJ3f9Xg9vcI/AAAAAAAAAhE/nYMik5Vbk_Y/s72-c/2008-08-10_012001.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5344099366403106984</id><published>2008-08-07T00:26:00.006+07:00</published><updated>2008-08-07T00:46:52.981+07:00</updated><title type='text'>MVP</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_fLnGNScTh18/SJnfPcGND7I/AAAAAAAAAg0/F3wKOV3Mpig/s1600-h/mvp.png"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_fLnGNScTh18/SJnfPcGND7I/AAAAAAAAAg0/F3wKOV3Mpig/s400/mvp.png" alt="" id="BLOGGER_PHOTO_ID_5231457898500788146" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Model&lt;/span&gt;&lt;br /&gt;This is the data upon which the user interface will operate. It is typically a domain object and the intention is that such objects should have no knowledge of the user interface. Here the M in VP differs from the M in MVC. As mentioned above, the latter is actually an Application Model, which holds onto aspects of the domain data but also implements the user interface to manipulate it. In MVP, the model is purely a domain object and there is no expectation of (or link to) the user interface at all.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The View&lt;/span&gt;&lt;br /&gt;The behaviour of a view in MVP is much the same as in MVC. It is the view's responsibility to displaythe contents of a model. The model is expected to trigger appropriate change notifications whenever its data is modified and these allow the view to "hang off" the model following the standard Observer pattern. In the same way as MVC does, this allows multiple views to be connected to a single model. One significant difference in MVP is the removal of the controller. Instead, the view is expected to handle the raw user interface events generated by the operating system (in Windows these come in as WM_xxxx messages) and this way of working fits more naturally into the style of most modern operating systems. In some cases, such as a TextView, the user input is handled directly by the view and used to make changes to the model data. However, in most cases the user input events are actually routed via the presenter and it is this which becomes responsible for how the model gets changed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Presenter&lt;/span&gt;&lt;br /&gt;While it is the view’s responsibility to display model data it is the presenter that governs how he model can be manipulated and changed by the user interface. This is where the heart of an application's behaviour resides. In many ways, a MVP presenter is equivalent to the application model in MVC; most of the code dealing with how a user interface works is built into a presenter class. The main difference is that a presenter is directly linked to its associated view so that the two can closely collaborate in their roles of supplying the user interface for a particular model.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Why not MVC?&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_fLnGNScTh18/SJnh36-OMQI/AAAAAAAAAg8/uvZa3rswkL4/s1600-h/mvc.png"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_fLnGNScTh18/SJnh36-OMQI/AAAAAAAAAg8/uvZa3rswkL4/s400/mvc.png" alt="" id="BLOGGER_PHOTO_ID_5231460793006829826" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In MVC, most of the application functionality must be built into a model class known as an Application Model  (see figure 1). It is the responsibility of the application model to be the mediator between the true domain objects and the views and their controllers.  The views, of course, are responsible for displaying the domain data  while the controllers handle the raw user gestures that will eventually perform actions on this data. So the application model typically has methods to perform menu command actions, push buttons actions and general validation on the data that it manages. Nearly all of the application logic will reside in the application model classes. However, because the application model’s role is that of a go-between, it is at times necessary for it to gain access to the user interface directly but, because of the Observer relationship between it and the view/controller, this sort of access is discouraged.&lt;br /&gt;&lt;br /&gt;For example, let's say one wants to explicitly change the colour of one or more views dependent on some conditions in the application model. The correct way to do this in MVC would be to trigger some sort of event, passing the colour along with it. Behaviour would then have to be coded in the view to "hang off" this event and to apply the colour change whenever the event was triggered. This is a rather circuitous route to achieving this simple functionality  and typically it would be avoided by taking a shortcut and using #componentAt: to look up a particular named view from the application model and to apply the colour change to the view directly. However, any direct access of a view like this breaks the MVC dictum that the model should know nothing about the views to which it is connected. If nothing else, this sort of activity surely breaks the possibility of allowing multiple views onto a model, which must be the reason behind using the Observer pattern in MVC in the first place.&lt;br /&gt;&lt;br /&gt;Another irritating feature of MVC, at least with respect to Dolphin, was that the idea of a controller did not fit neatly into the Windows environment. Microsoft Windows, like most modern graphical operating systems, provides a set of native widgets from which user interfaces can be constructed. These "windows" already include most of the controller functionality embodied as part of the underlying  operating system control. We found that, in order to create a sensible controller hierarchy, it was necessary to "break out" this inherent functionality and route it to various Controller subclasses. Even having done so, the ability to plug and play with these controller classes was severely limited by what the Windows OS would actually allow.&lt;br /&gt;Eventually, we decided that this approach was not appropriate so we stripped these controller classes away leaving us with a mainly vestigial Controller hierarchy.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Benefits of MVP &lt;/span&gt;&lt;br /&gt;So the effect of this "twist" is that the presenter, where the data manipulation part of a user interface is handled, is also allowed direct access to the view, where the data display is implemented. This can be very handy at times and is one of the most obvious benefits over MVC where the application model only has an indirect link to its associated view. The Dolphin implementation of MVP also manages to dispense with the idea of a controller, which seems to make the framework "fit" better with the underlying Windows operating system.&lt;br /&gt;&lt;br /&gt;Compared with our original widget framework, MVP offers a much greater separation between the visual presentation of an interface and the code required to implement the interface functionality. The latter resides in one or more presenter classes that are coded as normal using a standard class browser. The window layouts for most applications are created using a tool known as the View Composer which is used to create an instance of the view required . These view instances are held in an internal binary form by a Resource Manager. Normally, one or more view instances can be associated with any presenter class and a presenter can specify which particular view is required when it is launched. Hence it is easy for an MVP application to have one or more "skins" that can be selected as required.&lt;br /&gt;&lt;br /&gt;For example, the Dolphin development environment has three versions of the standard Class Hierarchy Browser which are all driven by the same ClassBrowserShell presenter class. We have the standard browser view, a simplified version for beginners (offering fewer options) and an alternative version where the class hierarchy is represented as a diagram rather than a standard tree view. This level of flexibility would not be possible with a framework based solely on widgets.&lt;br /&gt;&lt;br /&gt;(reference from &lt;a href="http://www.object-arts.com/papers/TwistingTheTriad.PDF"&gt;here&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5344099366403106984?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5344099366403106984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5344099366403106984' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5344099366403106984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5344099366403106984'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/08/mvp.html' title='MVP'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_fLnGNScTh18/SJnfPcGND7I/AAAAAAAAAg0/F3wKOV3Mpig/s72-c/mvp.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-4493717660335657168</id><published>2008-08-05T00:02:00.002+07:00</published><updated>2008-08-05T00:05:06.385+07:00</updated><title type='text'>TPS</title><content type='html'>...&lt;br /&gt;&lt;br /&gt;Taiichi Ohno, nhà lãnh đạo tinh thần của TPS, cho rằng hao phí lớn nhất là hao phí về sản xuất thừa. Nếu bạn làm ra một thứ mà bạn không thể bán được, thì công sức bạn bỏ ra sẽ mất đi. Nếu bạn làm ra một thứ dùng nội bộ trong dây chuyền mà không sử dụng chúng ngay lập tức, thì giá trị thông tin của chúng sẽ bốc hơi. Và bạn cũng tốn chi phí lưu trữ chúng: chuyển chúng vào nhà kho; theo dõi kho; lau chùi rỉ sét khi đem ra dùng lại; và sẽ gặp rủi ro nếu bạn không bao giờ dùng đến chúng nữa; trong trường hợp đó, bạn lại tốn chi phí để đem hủy bỏ chúng.&lt;br /&gt;&lt;br /&gt;Có vô vàn hao phí về sản xuất thừa trong phát triển phần mềm: bộ tài liệu yêu cầu đồ sộ nhanh chóng bị lỗi thời; các kiến trúc phức tạp không bao giờ được dùng; các đoạn mã hàng tháng trời không được tích hợp, kiểm thử và chạy trên môi trường thực; và các tài liệu không ai muốn đọc cho tới khi chúng không còn thích hợp hay mất tác dụng. Bởi vì tất cả những hoạt động này đều quan trọng với phát triển phần mềm, chúng ta cần sử dụng đầu ra của chúng ngay lập tức; để nhận được các phản hồi cần thiết nhằm loại trừ hao phí.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Lấy ví dụ về việc thu thập yêu cầu. Nó sẽ không được cải tiến bởi một tá các qui trình thu thập yêu cầu phức tạp. Nó sẽ được cải tiến bằng cách rút ngắn đoạn đường giữa “việc tạo ra các yêu cầu chi tiết” và “việc triển khai các phiên bản phần mềm”. Sử dụng ngay lập tức yêu cầu chi tiết ngầm ý rằng thu thập yêu cầu không phải là một giai đoạn mà kết quả là một thứ tài liệu tĩnh; thu thập yêu cầu là hoạt động diễn ra suốt quá trình phát triển, nó sản xuất ra các chi tiết vừa ngay khi chúng được cần đến.&lt;br /&gt;&lt;br /&gt;Có rất nhiều mặt của TPS tương đồng với phát triển phần mềm; những ý tưởng hữu ích chẳng hạn như: huấn luyện chéo giữa các công nhân, tổ chức nhà máy thành nhiều ô4, và lập các hợp đồng chia sẻ thành quả5 giữa khách hàng và nhà cung cấp. Nếu bạn có hứng thú, tôi khuyên bạn nên đọc quyển Toyota Production System của Ohno.&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(copy to blog của Vĩ, Chapter 19. Toyota Production System)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-4493717660335657168?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/4493717660335657168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=4493717660335657168' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4493717660335657168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4493717660335657168'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/08/blog-post.html' title='TPS'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-718331966783249571</id><published>2008-06-25T18:24:00.005+07:00</published><updated>2008-06-25T18:34:32.298+07:00</updated><title type='text'>TDD on Three Index Card</title><content type='html'>Tim Ottinge trên Object Mentor có viết một bài về TDD khá thú vị, tên là &lt;span style="font-style: italic;"&gt;TDD on &lt;/span&gt;&lt;span style="font-style: italic;"&gt;Three Index Card&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Card 1 - Uncle Bob’s Three Laws (Object Mentor)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_fLnGNScTh18/SGIsI3-ya1I/AAAAAAAAAfc/7ONqMIMBaGg/s1600-h/image48.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_fLnGNScTh18/SGIsI3-ya1I/AAAAAAAAAfc/7ONqMIMBaGg/s400/image48.png" alt="" id="BLOGGER_PHOTO_ID_5215779849426332498" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Card 2 - FIRST Principles (Brett and Tim at Object Mentor)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/SGIsb6d0tII/AAAAAAAAAfk/gVknylmfaPo/s1600-h/image49.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/SGIsb6d0tII/AAAAAAAAAfk/gVknylmfaPo/s400/image49.png" alt="" id="BLOGGER_PHOTO_ID_5215780176510891138" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Card 3 - Flow (using the famous three-node circle diagram) – origin unknown.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/SGIsobkmv3I/AAAAAAAAAfs/RN6VswEpvvw/s1600-h/image50.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/SGIsobkmv3I/AAAAAAAAAfs/RN6VswEpvvw/s400/image50.png" alt="" id="BLOGGER_PHOTO_ID_5215780391556136818" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(reference &lt;a href="http://blog.briandicroce.com/2008/03/14/three-index-cards-to-easily-remember-the-essence-of-test-driven-development/"&gt;http://blog.briandicroce.com/2008/03/14/three-index-cards-to-easily-remember-the-essence-of-test-driven-development/&lt;/a&gt;)&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-718331966783249571?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/718331966783249571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=718331966783249571' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/718331966783249571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/718331966783249571'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/06/tdd-on-three-index-card.html' title='TDD on Three Index Card'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_fLnGNScTh18/SGIsI3-ya1I/AAAAAAAAAfc/7ONqMIMBaGg/s72-c/image48.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-3875171320865868649</id><published>2008-04-29T11:45:00.003+07:00</published><updated>2008-04-29T16:58:34.058+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile Principles: Patterns and Practice in C#'/><title type='text'>Agile design</title><content type='html'>&lt;span style="font-style: italic;"&gt;After reviewing the software development life cycle as I understood it, I concluded that the only software documentation that actually seems to satisfy the criteria of an engineering design is the source code listings.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Jack Reeves&lt;br /&gt;&lt;br /&gt;Như vậy phát biểu của tác giả là gì? &lt;span style="font-weight: bold;"&gt;Code chính là design&lt;/span&gt; (không phải UML diagram)&lt;br /&gt;&lt;br /&gt;Agile design là một quá trình, không phải là một sự kiện (event). Nó là việc áp dụng các nguyên tắc, pattern và practices liên tục để cải tiến cấu trúc và tính khả đọc (readability) của phần mềm. Nó là một sự cống hiến để giữ cho thiết kế của hệ thống càng sạch, đơn giãn và dễ hiểu (expressive) càng nhiều càng tốt trong tất cả các thời điểm.&lt;br /&gt;&lt;br /&gt;Trong chương trước các bạn đã thấy, agile developer không áp dụng những nguyên tắc và pattern này cho design ngay từ ban đầu. Họ chỉ áp dụng nó từ iteration này đến iteration khác để bảo vệ các nguyên tắc ở trên ở thời điểm đó.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Design smells&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Rigidity: cứng nhắc&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Fragility: dễ vỡ, mỏng manh&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Immobility: bất động, không đổi&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Viscosity: nhớt, dính&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Needless complexity: phức tạp không cần thiết&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Needless repetition: lặp lại không cần thiết&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Opacity: tối nghĩa&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-3875171320865868649?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/3875171320865868649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=3875171320865868649' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/3875171320865868649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/3875171320865868649'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/agile-design.html' title='Agile design'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6432434299903197990</id><published>2008-04-29T10:09:00.005+07:00</published><updated>2008-04-29T13:38:57.658+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile Principles: Patterns and Practice in C#'/><title type='text'>Why software Rots</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Tại sao phần mềm trở nên mục nát? (rot)&lt;/span&gt;&lt;br /&gt;Trong một môi trường không agile (nonagile), thiết kế dần giảm giá trị theo thời gian khi requirements thay đổi không đúng với thiết kế ban đầu. Thường thường, các thay đổi này bị buộc làm một cách nhanh chóng và có thể được thực hiện bởi các developer không rành về lịch sử của các thiết kế ban đầu ấy.&lt;br /&gt;&lt;br /&gt;Tuy nhiên, chúng ta không thể đổ lỗi cho sự thay đổi requirement là nguyên nhân dẫn đến việc giảm giá trị của thiết kế ban đầu. Chúng ta, là những lập trình viên phần mềm, biết rõ rằng những requirement đó thay đổi. Thật sự, hầu hết chúng ta đều nhận ra rằng chúng là các thay phần bất ổn định nhất trong dự án. Nếu thiết kế của chúng ta là gánh nặng cho các cơn mưa ”change request” ấy thì thiết kết và practices của chúng ta đã thất bại (xem phần phân biệt practices và principles trong eXtreme programming của Vĩ :-D). Chúng ta phải có một cách nào đó làm cho thiết kế trở nên linh động, đáp ứng đựoc những thay đổi đó và sử dụng các practices để bảo vệ chúng khỏi sự mục nát.&lt;br /&gt;&lt;br /&gt;Một agile team phát triển dựa trên sự thay đổi. Team đầu tư không quá nhiều công sức ban đầu và không đặt hết gánh nặng cho thiết kế ban đầu. Hơn nữa, team sẽ giữ cho thiết kế của hệ thống càng trong sạch (clean) và đơn giản càng nhiều càng tốt, và “back up” với rất nhiều unit tests và acceptance tests. Việc này giúp cho thiết kết trở nên linh động và dễ thay đổi. Team tận dụng sự linh động đó để tiếp tục cải tiến design. Do đó, kết thúc mỗi interation thì thiết kế của hệt thống sẽ thích hợp với yêu cầu của iteration ở thời điểm đó.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Chương trình copy – Một ví dụ điển hình&lt;/span&gt;&lt;br /&gt;Giả sử sếp đến gặp bạn vào buổi sáng sớm thứ hai, và yêu cầu viết một chương trình copy các ký tự từ keyboard đến máy in. Làm một vài bài động não trong đầu, bạn kết luận nó chỉ tốn ít hơn mười dòng code. Thiết kế và code chỉ tốn ít hơn 1 giờ. Công thêm các cuộc hợp về tính năng của chương trình, kiểm tra chất lượng, group process hằng ngày, và các thứ linh tinh khác nữa. Nó có thể làm cho bạn tốt khoảng một tuần để hoàn tất. Tuy nhiên, cuối cùng bạn luôn nhân các estimate này lên ba lần.&lt;br /&gt;&lt;br /&gt;“Ba tuần”, bạn trả lời với sếp như vậy&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Thiết kế ban đầu&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/SBaSZLpTqFI/AAAAAAAAAHo/JPmUpqn6JQM/s1600-h/2008-04-28_100847.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/SBaSZLpTqFI/AAAAAAAAAHo/JPmUpqn6JQM/s400/2008-04-28_100847.png" alt="" id="BLOGGER_PHOTO_ID_5194500181538875474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Có ba module (hoặc gọi là chương trình con) trong ứng dụng. Copy module gọi hai module kia. Copy sẽ lấy các ký tự từ ReadKeyboard module và route chúng đến WritePrinter module.&lt;br /&gt;Bạn nhìn vào design và cảm thấy chúng đủ tốt. Bạn mĩm cười và rời khỏi văn phòng.&lt;br /&gt;&lt;br /&gt;Vào ngày thứ ba, bạn đến sớm hơn một chút để có thể hoàn tất chương trình Copy. Thật không may là bạn phải hỗ trợ debug một vấn đề cho đến 3 giờ chiều. Cuối cùng bạn quyết định ngồi code luôn chương trình Copy. Bạn nhận ra rằng mình đã trễ cho buổi họp quality theo kế hoạch, nói về tầm quan trọng của “zero defects”.&lt;br /&gt;&lt;br /&gt;Chương trình Copy được viết như sau:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/SBaS4LpTqGI/AAAAAAAAAHw/2lRfFxo21gk/s1600-h/2008-04-28_100949.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/SBaS4LpTqGI/AAAAAAAAAHw/2lRfFxo21gk/s400/2008-04-28_100949.png" alt="" id="BLOGGER_PHOTO_ID_5194500714114820194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Vào ngày thứ tư, bạn đến sớm lần này và mọi thứ có vẽ không có gì sai nữa. Bạn lấy source code và biên dịch lần đầu không error. Đây là một tín hiệu tốt vì sếp bạn gọi vào họp đột xuất vì có nhu cầu giữ tone của máy in laser.&lt;br /&gt;&lt;br /&gt;Vào ngày thứ năm, sau khi tốn 4 giờ trên gọi điện cho gã kỹ thuật ở Rocky Mount, miền nam Carolina, bàn luận về chương trình remote debugging và các lệnh logging, là một trong những component tối nghĩa trong hệ thống, bạn quyết định test chương trình copy. Nó chạy trong lần đầu tiên. Thật là tốt. Tên sinh viên làm chung vừa mới xóa source code trong thư mục trên server, và bạn phải tìm backup sau cùng và restore nó.&lt;br /&gt;&lt;br /&gt;Thứ sáu là ngày hoàn toàn không dự tính trước. Bạn phải tốn cả ngày để load nó vào trong source control system.&lt;br /&gt;&lt;br /&gt;Dĩ nhiên, chương trình của bạn đã thành công vang dọi và được deploy khắp công ty. Tên tuổi của bạn, một developer xuất sắc một lần nữa được khẳng định, và bạn đang đắm mình trong ánh hào quang vừa đạt được. Nếu bạn may mắn, bạn có thể chỉ code 30 dòng code như vậy trong năm nay!&lt;br /&gt;&lt;br /&gt;&lt;span id="fe_n0"&gt;&lt;b id="cabj3"&gt;Requirements - nó đang thay đổi&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Vài tháng sau đó, sếp của bạn đi đến và nói rằng chương trình Copy nên đọc từ một thiết bị nữa là paper tape reader. Bạn nghiến răng và trừng mắt. Bạn tự hỏi tại sao người ta luôn luôn thay đổi requirements. Chương trình của bạn không được thiết kế cho một paper tape reader! Bạn cảnh báo cho sếp của bạn rằng các thay đổi như vậy đang phá hủy nét đẹp (elegance) của design. Tuy nhiên, sếp của bạn là người rất cứng rắn, cho rằng khách hàng thật sự cần làm việc đó.&lt;br /&gt;&lt;br /&gt;Bạn thở dài và lên kế hoạch cho sự thay đổi. Bạn định thêm vào một argument Boolean đến hàm Copy. Nếu True, bạn đọc từ paper tape, nếu False, bạn đọc từ Keyboard. Không may là có quá nhiều chương trình khác đang sử dụng chương trình Copy bây giờ nên bạn không thể thay đổi interface được. Thay đổi interface có thể làm cho tốn từ tuần này đến tuần khác để compiling và retesting. Tester có thể sẽ hành hình bạn, không chỉ là 7 người trong nhóm configuration.&lt;br /&gt;&lt;br /&gt;Không, thay đổi interface không được. Nhưng sau đó làm cách nào bạn có thể để chương trình Copy biết nó phải đọc từ đâu? Dĩ nhiên bạn sẽ dùng một biến toàn cục! Bạn dùng operator ?: như đoạn code sau:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_fLnGNScTh18/SBaTLbpTqHI/AAAAAAAAAH4/lQopgxmKTBU/s1600-h/2008-04-28_105646.png"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_fLnGNScTh18/SBaTLbpTqHI/AAAAAAAAAH4/lQopgxmKTBU/s400/2008-04-28_105646.png" alt="" id="BLOGGER_PHOTO_ID_5194501044827302002" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Chương trình gọi chương trình Copy sẽ truyền ptFlag = true trước khi gọi từ nếu muốn đọc từ paper tape reader. Sau khi hàm Copy trả về, caller phải reset ptFlag; nếu không lần gọi tiếp theo có thể đọc sai từ paper tape reader hơn là keyboard. Nhớ rằng programmer phải có nhiệm vụ reset biến này, bạn phải thêm vào comment thích hợp cho họ.    Một lần nữa, bạn release chương trình và được đánh giá rất cao. Hơn cả thành công trước kia nữa, và đám lập trình viên hớn hở đó đang đợi sử dụng nó. Cuộc sống thật tốt đẹp.    &lt;span id="oe050"&gt;&lt;b id="mqpd0"&gt;&lt;br /&gt;&lt;br /&gt;Thêm một chút nữa (give 'em an inch)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Vài tuần sau, sếp bạn nói rằng khách hàng đôi khi muốn chương trình Copy xuất ra "paper tape punch". Lại là khách hàng!. Họ luôn phá hủy thiết kế của bạn. &lt;span id="a8d70"&gt;&lt;i id="mqpd1"&gt;Viết phần mềm sẽ dễ hơn nhiều nếu không có khách hàng&lt;/i&gt;&lt;/span&gt;. Bạn nói với sếp bạn rằng những thay đổi liên tục như vậy có ảnh hưởng xấu đến vẽ đẹp của thiết kế, và cảnh báo rằng nếu thay đổi liên tục ở nhịp độ kinh khủng như vậy, software sẽ không thể nào bảo trì được trước cuối năm. Sếp bạn gật đầu một cách có hiểu biết, nhưng dù sao vẫn bắt bạn thay đổi.&lt;br /&gt;&lt;br /&gt;Thiết kế này được thay đổi giống như thay đổi của lần trước. Thêm một biến global và một operator ?: nữa.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_fLnGNScTh18/SBaWdrpTqJI/AAAAAAAAAII/z8jabjIRyW4/s1600-h/2008-04-29_102738.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_fLnGNScTh18/SBaWdrpTqJI/AAAAAAAAAII/z8jabjIRyW4/s400/2008-04-29_102738.png" alt="" id="BLOGGER_PHOTO_ID_5194504656894797970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Bạn đặc biệt tự hào vì mình vẫn nhớ để thay đổi comment. Tuy nhiên bạn vẫn lo lắng về cấu trúc của chương trình bắt đầu trở nên lung lay. Bất kỳ một thay đổi nào nữa của thiết bị input chắc chắn sẽ làm cho bạn thay đổi toàn bộ của trúc của vòng lặp while. Có lẽ đây là thời điểm thích hợp cho bạn phủi bụi resume, chuẩn bị để đi phỏng vấn.    &lt;span id="gegu0"&gt;&lt;b id="ey740"&gt;&lt;br /&gt;&lt;br /&gt;Mong đợi sự thay đổi&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Tôi để lại vấn đề trên cho bạn quyết định về mức độ châm biếm được cường điệu hóa ở trên. Điểm mấu chốt của câu truyện cho thấy làm cách nào design của chương trình dần dần giảm chất lượng khi có sự hiện hữu của thay đổi. Thiết kế ban đầu của chương trình Copy khá đơn giản và nhẹ nhàng (elegant). Nhưng chỉ sau 2 lần thay đổi, nó đã bắt đầu biểu lộ các dấu hiệu của sự cứng nhắc (&lt;span id="gn680"&gt;&lt;b id="ey741"&gt;rigidity&lt;/b&gt;&lt;/span&gt;), mỏng manh (&lt;span id="gn681"&gt;&lt;b id="ey742"&gt;fragility&lt;/b&gt;&lt;/span&gt;), bất động (&lt;span id="gn682"&gt;&lt;b id="ey743"&gt;immobility&lt;/b&gt;&lt;/span&gt;), phức tạp (&lt;span id="gn683"&gt;&lt;b id="ey744"&gt;complexity&lt;/b&gt;&lt;/span&gt;), thừa thãi (&lt;span id="gn684"&gt;&lt;b id="ey745"&gt;redundancy&lt;/b&gt;&lt;/span&gt;) và tối nghĩa (&lt;span id="gn685"&gt;&lt;b id="ey746"&gt;opacity&lt;/b&gt;&lt;/span&gt;). Xu hướng này có khả năng tiếp tục, và chương trình trở thành một đống hỗn độn (mess).&lt;br /&gt;&lt;br /&gt;Chúng ta có thể ngồi lại và trách cứ những thay đổi này. Chúng ta có thể than phiền rằng các chương trình đã được thiết kế tốt cho specification ban đầu và các thay đổi tiếp theo của spec làm cho design bị giảm chất lượng. Tuy nhiên, chúng ta đã bỏ qua một trong những đặc trưng nỗi trội của phát triển phần mềm: &lt;span id="xglw0"&gt;&lt;i id="ey747"&gt;Requirements luôn thay đổi&lt;/i&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Hãy nhớ rằng, một trong những thứ thay đổi trong software projects là requirements. Đây là một sự thật mà chúng ta, các developer phải chấp nhận. &lt;span id="tmcp0"&gt;&lt;i id="ey748"&gt;Chúng ta đang sống trong một thế giới thay đổi requirement, và nhiệm vụ của chúng ta là đảm bảo phần mềm của mình có thể tồn tại trước những thay đổi đó&lt;/i&gt;&lt;/span&gt;. Nếu thiết kế phần mềm của chúng ta giảm khi requirement thay đổi, chúng ta &lt;span id="r_xe0"&gt;&lt;i id="ey749"&gt;không agile&lt;/i&gt;&lt;/span&gt;.  &lt;span id="pue02"&gt;&lt;b id="ey7410"&gt;&lt;br /&gt;&lt;br /&gt;Agile design của chương trình Copy&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Một agile development team có thể bắt đầu theo cùng một cách, như đoạn code ở trên ban đầu. Khi sếp yêu cầu làm cho chương trình đọc từ paper tape reader, developer phải đáp ứng bằng cách thay đổi design để nó có thể &lt;span id="ecbm0"&gt;&lt;i id="ey7411"&gt;co giãn&lt;/i&gt;&lt;/span&gt; (resilient) với những dạng thay đổi đó.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/SBaYaLpTqKI/AAAAAAAAAIQ/JVm01GRz2YE/s1600-h/interface.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/SBaYaLpTqKI/AAAAAAAAAIQ/JVm01GRz2YE/s400/interface.png" alt="" id="BLOGGER_PHOTO_ID_5194506795788511394" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Thay vì cố gắng chắp vá thiết kế ban đầu để cho requirement mới chạy, team chộp cơ hội này để cải tiến design sao cho nó có thể đáp ứng được những dạng thay đổi này trong tương lai. Từ giờ trở đi, khi nào sếp bạn hỏi về một dạng thiết bị input mới, team sẽ có khả năng đáp ứng theo cách mà nó làm giảm chất lượng của chương trình Copy.&lt;br /&gt;&lt;br /&gt;Design lại ở trên cho thấy team đã áp dụng nguyên tắc &lt;span style="font-style: italic;"&gt;Open/Closed (OCP)&lt;/span&gt;, được miêu tả ở chương 9 trong sách. Nguyên tắc này hướng dẫn chúng ta thiết kế module để nó có thể được mở rộng mà không cần phải sửa code cũ đang chạy. Mỗi một thiết bị input mới mà sếp yêu cầu hỗ trợ sẽ được cung cấp mà không cần phải sửa lại chương trình Copy.&lt;br /&gt;&lt;br /&gt;Cần phải chú ý rằng, trong thiết kế ban đầu của module, team không có gắng đoán trước được chương trình sẽ thay đổi như thế nào. Thay vào đó, team đã viết module theo cách đơn giản nhất có thể. Chỉ khi nào requirement thay đổi thì team sẽ thay đổi design của module theo cách linh động theo dạng của thay đổi đó.&lt;br /&gt;&lt;br /&gt;Chúng ta có thể tranh cãi rằng team chỉ làm mới có một nữa công việc. Trong khi developer đang bảo vệ chương trình khỏi các thiết bị input khác nhau, họ cũng có thể làm tương tự với các thiết bị output. Tuy nhiên team thật sự không có ý kiến về việc các thiết bị output có bị thay đổi hay không. Và cách thêm vào design cho output không phục vụ cho mục tiêu hiện tại nên nó không phải là lý do thật sự để chúng ta thêm vào nó bây giờ.&lt;br /&gt;&lt;br /&gt;&lt;span id="nh-l2"&gt;&lt;b id="ey7412"&gt;Tuân theo các agile practices&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Agile developers trong ví dụ trên đã xây dựng một abstract class để ngăn chặn chúng khỏi sự thay đổi của các thiết bị input. Làm cách nào họ biết làm như vậy. Câu trả lời nằm trong một trong các nguyên lý cơ bản của thiêt kế hướng đối tượng.&lt;br /&gt;&lt;br /&gt;Thiết kế ban đầu của chương trình Copy linh động vì hướng phụ thuộc các dependencies của nó. Figure design ban đầu ta thấy Copy module phụ thuộc trực tiếp vào KeyboardReader và PrinterWriter. Copy module là một module ở cấp cao trong ứng dụng này. Nó thiết lập các policy của ứng dụng. Nó biết làm cách nào để copy các ký tự. Không may là nó cũng phụ thuộc vào chi tiết của component ở mức thấp là keyboard và printer. Vì vậy, khi low-level thay đổi, high-level policy cũng bị ảnh hưởng.&lt;br /&gt;&lt;br /&gt;Một khi sự không linh động xuất hiện, agile developer biết rằng sự phụ thuộc từ Copy module từ thiết bị input cần phải được đảo lại, sử dụng &lt;span style="font-style: italic;"&gt;Dependency Inversion (DIP)&lt;/span&gt; trong chương 11, để cho Copy module không còn phụ thuộc vào thiết bị input nữa. Sau đó họ có thể dùng STRATEGY pattern, để tạo ra inversion mong muốn.&lt;br /&gt;&lt;br /&gt;Túm lại, agile developer biết cách làm gì bởi vì họ tuân theo các bước sau:&lt;br /&gt;&lt;ol id="xmov9"&gt;&lt;li id="xmov10"&gt;Phát hiện ra các vấn đề bằng cách tuân theo các agile practices&lt;/li&gt;&lt;li id="xmov10"&gt;Phân tích (diagnosed) vấn đề bằng cách áp dụng các design principles&lt;/li&gt;&lt;li id="xmov10"&gt;Giải quyết vấn đề bằng cách áp dụng các design pattern thích hợp&lt;/li&gt;&lt;/ol&gt;&lt;span id="u3vg8"&gt;&lt;b id="ey7413"&gt;Giữ cho thiết kế tốt nhất có thể&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Agile developer phải luôn giữ design ban đầu thích hợp (approriate) và sạch sẽ tốt nhất có thể. Đây không phải là một lời cam kết ngập ngừng và bừa bãi. Agile developer không "clean up" design mỗi tuần. Hơn thế nữa, họ giữ cho software càng sạch, đơn giản và có nghĩa (expressive) như nó tốt nhất có thể trong mỗi ngày, mỗi giờ và mỗi phút. Họ không bao giờ nó "Chúng ta sẽ về nhà và fix nó sau". Họ không bao giờ để cho sự mục nát có cơ hội bắt đầu.&lt;br /&gt;&lt;br /&gt;Quan điểm của agile developer hướng về design của software cùng với quan điểm mà các bác sĩ phẫu thuật hướng về quá trình vô trùng (sterile procedure). Quá trình vô trùng để cho cuộc giải phẩu xảy ra. Không có việc vô trùng, rủi rõ của việc nhiễm trùng sẽ lên cao. Agile developer có cảm giác tương tự về thiết kế của họ. Rủi ro của việc để cho một sự mục nát nào đó dù nhỏ nhất sẽ làm cho sự gánh chịu nhiều hơn về sau.&lt;br /&gt;&lt;br /&gt;Thiết kế phải sạch (clean). Và vì source code là diễn tả quan trọng nhất của design, nó phải sạch. Các nhà chuyên nghiệp chỉ ra rằng, chúng ta, là các software developer, không thể chịu đựng code của mình bị mục nát (rot).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6432434299903197990?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6432434299903197990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6432434299903197990' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6432434299903197990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6432434299903197990'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/why-software-rots.html' title='Why software Rots'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_fLnGNScTh18/SBaSZLpTqFI/AAAAAAAAAHo/JPmUpqn6JQM/s72-c/2008-04-28_100847.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-3863692173462637996</id><published>2008-04-15T13:43:00.006+07:00</published><updated>2008-04-15T14:02:43.456+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Scope chain and [[scope]]</title><content type='html'>&lt;span style="font-weight: bold; font-style: italic;"&gt;&lt;/span&gt;&lt;span style="font-style: italic;"&gt;(tiếp theo bài trước)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Scope chain của một execution context cho một lời gọi hàm được xây dựng bằng cách thêm object Activation vào trước chain của property [[scope]] của function object.&lt;br /&gt;&lt;br /&gt;Trong ECMAScript, function là các object. Chúng được tạo ra trong suốt quá trình khởi tạo biến trong khai báo hàm hoặc trong evaluation của function expression hoặc gọi Function constructor.&lt;br /&gt;&lt;br /&gt;Function object được tạo ra bởi constructor luôn có một [[scope]] property trỏ đến scope chain chỉ có một global object. Function object đựoc tạo ra bởi khai báo hàm hoặc function expression có một scope chain của execution context trong đó chúng được tạo ra gắn với [[scope]] property bên trong của chúng.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;function exampleFunction(formalParameter) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  // function body code&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ví dụ này cho thấy function object tương ứng sẽ đựoc tạo ra trong suốt quá trình khởi tạo biến cho global execution context. Global execution context sẽ có một property tên là "exampleFunction" và trỏ đến một object gọi là function object. Function object này có một property bên trong là [[scope]] trong đó trỏ đến một scope chain chỉ có một global object.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;var exampleFunctionRef = function() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    // ....&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;trong trường hợp này, một property của global object được tạo ra nhưng function object chưa được tạo, và một tham khảo đến nó được gán đến property này của global object cho đến khi quá trình assignment expression được thực thi. Nhưng quá trình tạo function object vẫn diễn ra bên trong global execution context nên [[scope]] property của function object này chỉ trỏ đến global object.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;function exampleOuterFuction(formalParameter) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    function exampleInnerFunctionDec() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        //&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;exampleOuterFunction(5);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Function object của outer function được tạo ra trong quá trình khởi tạo biến của global object, và trong global context nên scope chain của nó chỉ chứa global object. Khi global code gọi hàm exampleOuterFunction, một execution context mới được tạo ra cùng với một Activation/Variable object. Scope cho context này bao gồm Activation object mới đó, theo sau là chuỗi các object của [[scope]] property của outer function.&lt;br /&gt;&lt;br /&gt;Để dễ hiểu hơn, ta xem một số minh họa sau:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.odetocode.com/aimages/200707/071007_0345_Closures1.png"&gt;&lt;img style="cursor: pointer; width: 400px;" src="http://www.odetocode.com/aimages/200707/071007_0345_Closures1.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(từ Scott Allen)&lt;/span&gt;&lt;br /&gt;Trong hình trên, vùng ngoặc vuông màu đỏ diễn tả cho scope, vòng tròn màu đỏ diễn tả cho Activation object, còn mũi tên xanh diễn tả scope chain.&lt;br /&gt;&lt;br /&gt;Hoặc một diagram khác&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mollypages.org/misc/js-scope.jpg"&gt;&lt;img style="cursor: pointer; width: 400px;" src="http://mollypages.org/misc/js-scope.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(theo http://www.jibbering.com/faq/faq_notes/closures.html)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-3863692173462637996?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/3863692173462637996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=3863692173462637996' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/3863692173462637996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/3863692173462637996'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/javascript-execution-context-2.html' title='Scope chain and [[scope]]'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-4604880532187022864</id><published>2008-04-15T12:59:00.001+07:00</published><updated>2008-04-15T13:01:09.628+07:00</updated><title type='text'>Javascript execution context</title><content type='html'>&lt;span style="font-weight: bold;"&gt;The Execution Context&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Execution context là một khái niệm trừu tượng, được đặc tả bởi ECMSScript specification (ECMA 262 3rd edition). Nó có thể được xem như là các objects cùng với những properties (nhưng không phải là public properties).&lt;br /&gt;&lt;br /&gt;Tất cả các javascript code được executed trong một execution context. Global code (code được execute inline, hoặc trong JS file, hoặc HTML page...) được execute trong global execution context và mỗi một lời gọi hàm sẽ có một conetxt object đi kèm với nó. Code được execute bằng hàm eval cũng có một execution context riêng, nhưng vì eval không thường được sử dụng bởi javascript programmer nên nó sẽ không được discuss ở đây. (các bạn có thể xem đặc tả của nó trong section 10.2 của ECMA 262)&lt;br /&gt;&lt;br /&gt;Khi một javascript function được gọi, nó bước vào một execution context. Nếu một function khác được gọi (hoặc gọi lại chính nó theo cách gọi đệ quy) thì một execution context mới sẽ được tạo ra và lời gọi hàm sẽ enter vào context đó cho đến khi nó trả ra kết quả. Việc này dẫn đến javascript code hình thành nên một stack các execution context trong quá trình gọi.&lt;br /&gt;&lt;br /&gt;Khi một execution context được tạo ra, một số việc sẽ xảy ra theo một thứ tự được định nghĩa trước. Đầu tiên, trong execution context của một function, một "&lt;span style="font-weight: bold;"&gt;Activation&lt;/span&gt;" object được tạo ra. Activation object này là một cơ chế đặc tả khác. Nó có thể được xem như là một object vì nó có các properties có thể truy xuất được. Nhưng nó không phải là một object bình thường vì nó không có prototype và không thể được reference trực tiếp bằng javascript code.&lt;br /&gt;&lt;br /&gt;Bước tiếp theo của việc tạo ra execution context cho một lời gọi hàm là việc tạo ra argument object, là một object giống như array với các index là các số interger tương ứng với thứ tự của các parameter được truyền vào. Nó cũng có length và callee property. Một property của Activation object được tạo ra với tên là "&lt;span style="font-weight: bold;"&gt;arguments&lt;/span&gt;" và trỏ đến object này.&lt;br /&gt;&lt;br /&gt;Bước tiếp theo execution context được gán vào một scope. Scope bao gồm một danh sách (hoặc một dây - chain) các objects. Mỗi function object có một property bên trong tên là &lt;span style="font-weight: bold;"&gt;[[scope]]&lt;/span&gt; và nó cũng chứa một danh sách các object. Scope gán cho execution context cho một function là một danh sách được tham khảo bởi [[scope]] property của function object đó với một chút thay đổi là Activation object sẽ được thêm vào đầu tiên của danh sách.&lt;br /&gt;&lt;br /&gt;Quá trình khởi tạo các variables diễn ra bằng cách sử dụng một object gọi là "&lt;span style="font-weight: bold;"&gt;Variable&lt;/span&gt;" object (tham khảo ECMA 262 phần khởi tạo biến 10.1.3). Thật sự, Activation object và Variable object là một (!!!). Các properties được đặt tên cho Variable object được tạo ra cho mỗi parameters của function, và nếu các arguments của lời gọi hàm tương ứng với các các parameters này, giá trị của chúng sẽ được gán cho các properties của Variable object (các giá trị default sẽ là undefined). Các inner function được định nghĩa bên trong sẽ tạo ra các function objects và được gán thành các properties của Variable object với tên tương ứng là tên của các inner function. Bước cuối cùng trong quá trình khởi tạo biến là tạo ra các properties với tên tương ứng với các local variables được khai báo bên trong function.&lt;br /&gt;&lt;br /&gt;Các properties được tạo trên Variable object tương ứng với các biến cục bộ được khai báo bên trong hàm ban đầu sẽ được gán giá trị là undefined, sau đó việc khởi tạo thật sự sẽ diễn ra trong quá trình evaluation của assignment expression bên trong thân hàm.&lt;br /&gt;&lt;br /&gt;Sự kiện Activation object - cùng với property arguments của nó - và Variable object - cùng với các properties có tên tương ứng các local variables - cùng chỉ đến một object cho phép arguments được đối xử như là biến local của function.&lt;br /&gt;&lt;br /&gt;Cuối cùng, nếu một giá trị được gán đang tham khảo đến một object mà sau đó các tác vụ truy xuất property có prefix là keyword this, thì nó sẽ dùng giá trị của object đó. Nếu không có từ khóa this hoặc giá trị được gán là null (tức là không nằm trong một object nào đó), nó sẽ refer đến global object.&lt;br /&gt;&lt;br /&gt;Global execution context có một vài khác biệt nhỏ vì nó không có arguments nên nó không cần phải có một Activation object tham khảo đến nó. Global execution context thật sự cần một scope và scope chain của nó chỉ chứa một object, đó là global object. Trong quá trình duyệt để khởi tạo biến của global object, nó sẽ được dùng như một Variable object, do đó các biến toàn cục (hoặc các hàm top-level function) sẽ được tạo thành các properties với cùng tên, giống như mô tả ở trên.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-4604880532187022864?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/4604880532187022864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=4604880532187022864' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4604880532187022864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4604880532187022864'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/javascript-execution-context.html' title='Javascript execution context'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-7459299936151876200</id><published>2008-04-11T17:20:00.002+07:00</published><updated>2008-04-11T17:26:12.374+07:00</updated><title type='text'>Why UML?</title><content type='html'>&lt;span style="font-style:italic;"&gt;(copied from blog của Vĩ)&lt;br /&gt;(from chapter2 - UML for java programmer)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Tại sao phải Mô Hình&lt;/span&gt;&lt;br /&gt;Tại sao các kỹ sư làm mô hình? Tại sao các kỹ sư hàng không tạo mô hình máy bay? Tại sao kỹ sư cầu đường làm mô hình cây cầu? Mục đích của những mô hình này là gì?&lt;br /&gt;&lt;br /&gt;Kỹ sư làm mô hình để xác định xem liệu thiết kế của họ có hoạt động chính xác không? Kỹ sư hàng không làm mô hình máy bay và đưa chúng vào các ống gió (wind tunnel) để xem chúng có bay được không. Kỹ sư cầu đường tạo mô hình cây cầu để xem chúng có đứng vững hay không. Kiến trúc sư xây dựng mô hình các tòa nhà để xem khách hàng có thích kiểu dáng như thế không. Mô hình được tạo để xác định xem một vật nào đó có hoạt động không.&lt;br /&gt;&lt;br /&gt;Điều này ẩn chứa ý rằng mô hình có thể kiểm thử được. Thật là vô nghĩa khi tạo mô hình mà bạn không có cách nào để thực hiện các phép kiểm thử trên nó. Nếu bạn không định lượng được mô hình, thì mô hình đó không có giá trị.&lt;br /&gt;&lt;br /&gt;Tại sao kỹ sư hàng không không chế tạo ra máy bay và lái thử nó? Tạo sao kỹ sư cầu đường không cho xây cây cầu rồi mới xem chúng có đứng vững không? Bởi vì máy bay và cây cầu đắt hơn rất nhiều so với mô hình. Chúng ta nghiên cứu các thiết kế bằng cách làm mô hình nếu như việc làm mô hình rẻ hơn nhiều lần so với việc chúng ta tạo ra vật thể thực.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Tại sao tạo mô hình phần mềm?&lt;/span&gt;&lt;br /&gt;Liệu có thể kiểm thử các lược đồ UML? Liệu tạo các lược đồ UML có rẻ hơn và dễ kiểm thử hơn cái phần mềm mà nó biểu diễn? Trong cả hai tình huống, câu trả lời không bao giờ rõ ràng như lĩnh vực hàng không và cầu đường. Không có phương pháp chắc chắn nào để kiểm thử các lược đồ UML. Chúng ta có thể nhìn chúng, định lượng chúng, áp dụng các nguyên tắc thiết kế và các mẫu thiết kế lên chúng, nhưng kết quả của việc đánh giá chúng vẫn rất chủ quan. Vẽ lược đồ UML thì dễ hơn là viết phần mềm, nhưng với hệ số không lớn. Thật vậy, thay đổi mã nguồn dễ hơn rất rất nhiều lần so với việc thay đổi một lược đồ. Như vậy dùng UML liệu có hợp lý không?&lt;br /&gt;&lt;br /&gt;Tôi đã không viết một quyển sách về UML nếu như dùng nó là không hợp lý. Tuy nhiên, những lập luận trên nhằm giải thích rằng UML rất dễ bị dùng sai mục đích. Chúng ta sử dụng UML khi chúng ta có một thứ gì đó mà chúng ta dứt khoát phải kiểm thử nó, và khi dùng UML để kiểm thử thì rẻ hơn là dùng mã nguồn để kiểm thử nó.&lt;br /&gt;&lt;br /&gt;Ví dụ, nói rằng tôi có một ý tưởng cho một thiết kế nào đó. Tôi muốn kiểm tra rằng liệu những thành viên trong nhóm của tôi có cho rằng đấy là một ý tưởng đúng đắn. không. Vì vậy tôi vẽ lược đồ UML lên bảng và hỏi thành viên trong nhóm để nhận phản hồi.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;br /&gt;Tại sao chúng ta tạo các thiết kế đầy đủ trước khi lập trình?&lt;/span&gt;&lt;br /&gt;Kiến trúc sư, kỹ sư hàng không, kỹ sư cầu đường luôn vẽ các bản vẽ thiết kế (blueprint)? Tại sao? Bởi vì một người có thể vẽ bản thiết kế cho một ngôi nhà mà để xây nó cần đến năm người hoặc hơn. Khoảng một chục kỹ sư hàng không có thể vẽ bản thiết kế cho chiếc máy bay mà để chế tạo nó phải cần hơn cả ngàn người. Có thể vẽ bản thiết kế mà không cần phải đào móng, không cần phải trộn bê tông hay lắp cửa sổ.... Nói ngắn gọn, lập một kế hoạch xây dựng trước sẽ rẻ hơn nhiều so với việc xây dựng mà không có kế hoạch nào. Và nó sẽ không tốn nhiều chi phí khi bỏ đi những bản vẽ tồi, nhưng sẽ tốn rất nhiều nếu giựt sập một tòa nhà.&lt;br /&gt;&lt;br /&gt;Một lần nữa, điều này lại không rõ ràng khi áp dụng cho phần mềm. Vẽ các lược đồ UML không thực sự rẻ hơn là viết mã nguồn. Thật vậy rất nhiều nhóm đã tiêu tốn nhiều thời gian trên lược đồ hơn là ngồi làm việc thực sự trên mã nguồn. Và việc ném bỏ các lược đồ không thực sự rẻ hơn là ném bỏ những đoạn mã. Vì vậy việc tạo một bộ đầy đủ các lược đồ UML trước khi viết code không thực sự là chọn lựa hiệu quả về chi phí.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-7459299936151876200?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/7459299936151876200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=7459299936151876200' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7459299936151876200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/7459299936151876200'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/why-uml.html' title='Why UML?'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6062347113936621305</id><published>2008-04-06T00:48:00.013+07:00</published><updated>2008-04-06T01:51:36.857+07:00</updated><title type='text'>Aspect#</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Một ví dụ&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Giả sử bạn đang làm một dự án nào đó. Bạn đang tạo ra một CMS mang tính cách mạng và sắp hoàn tất với một mô hình object model đẹp và đơn giản. Các component quan trọng implement interface IContentProvider và IView. IContentProvider chịu trách nhiệm thu thập thông tin từ các nguồn như database, xml, rss, excels. IView chịu trách nhiệm hiển thị thông tin theo một cách nào đó cho một kênh thông tin riêng biệt nào đó.&lt;br /&gt;&lt;br /&gt;Mọi thứ đang tốt đẹp và hầu hết đã làm xong với cả ngàn ContentProviders và Views, cover tất cả các communication channels. Bất ngờ, the sale guy - luôn luôn là họ :-) - đến, với quan điểm của anh ta, và với một requirement không quan trọng. Anh ta cần security checking cho các provider và view, và anh ta đã bán nó cho một khách hàng quan trọng, vd như BBC. Tương lai của công ty bạn phụ thuộc vào việc nó có hoàn tất vào cuối ngày hôm nay hay không.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Các giải pháp có sẵn&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Bạn cần phải expose các object ra như là một securable resource cho security framework. Để làm nhẹ đi gánh nặng phải thay đổi mỗi ContentProvider và View, bạn nghĩ đến một số giải pháp có thể sau đây:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tạo ra một property trên IContentProvider và IView, để show thông tin của IResource&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Tạo ra một 'IResourceable' và làm cho IContentProvider và IView mở rộng từ nó. Sau đó sửa chữa base class của chúng, mặc dù bạn không chắc một base class như vậy có tồn tại hay không.&lt;/li&gt;&lt;li&gt;Bạn đang ở tầng 9, vì vậy nhảy ra khỏi cửa sổ có thể giải quyết vấn đề trong chốc lát...&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Vấn đề với những giải pháp trên là nó sẽ làm rối mô hình object đẹp và sạch sẽ của bạn. Trong khái niệm security của bạn, không có cái gì phải làm với content providers và view. Do đó, dễ hiểu nhất là bạn thích dùng một ISecurityResource interface và implement nó.&lt;br /&gt;&lt;br /&gt;Coi nào, bạn chỉ có một vài giờ. Hãy bắt đầu modify những component này. Không cần phải gấp lắm, hãy dùng AOP để giải quyết nó.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Giải pháp kết hợp (mixin solution)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Đầu tiên, bạn sẽ làm cho tất cả các ContentProvider trong một namespace nào đó phải bao gồm một interface implement ISecurityResource, ví dụ:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class SecurityResourceImpl : ISecurityResource&lt;br /&gt; {&lt;br /&gt;       public SecurityResourceImpl()&lt;br /&gt;       {&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       public String ResourceName&lt;br /&gt;       {&lt;br /&gt;             get { return "Content"; }&lt;br /&gt;       }&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Chúng ta sử dụng một build-in language (giống Ruby) để cấu hình aspect đó. Chúng ta giữ cấu hình này trong một config file, hoặc trong code (not recommended). Ví dụ&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import YourCompany.CMS.ContentProviders in YourCompanyAssembly&lt;br /&gt; aspect SecurityAspect for RSSContentProvider&lt;br /&gt;     include Mixins.SecurityResourceImpl in MyMixinsAssembly&lt;br /&gt; end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ví dụ trên làm cho RSSContentProvider sẽ có một ISecurityResource interface được implement bởi SecurityResourceImpl&lt;br /&gt;&lt;br /&gt;Thay vì chỉ định một class, ta có thể chỉ định tất cả các class trong một namespace cho trước.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import YourCompany.CMS.ContentProviders in YourCompanyAssembly&lt;br /&gt; aspect SecurityAspect for [ YourCompany.CMS.ContentProviders ]&lt;br /&gt;   include Mixins.SecurityResourceImpl in MyMixinsAssembly&lt;br /&gt; end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Aspect# engine sẽ làm việc này:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;StreamReader reader = new StreamReader( configfile );&lt;br /&gt;AspectEngineBuilder builder = new AspectLanguageEngineBuilder(reader);&lt;br /&gt;&lt;br /&gt;AspectEngine engine = builder.Build();&lt;br /&gt;RSSContentProvider provider = engine.Wrap( new RSSContentProvider() );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Chuyện gì xảy ra nếu security resource cần phải truy xuất một vài thứ từ content provider hoặc từ view? Ta phải implement thêm interface IProxyAware&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class SecurityResourceImpl : ISecurityResource, IProxyAware&lt;br /&gt; {&lt;br /&gt;        private String _name;&lt;br /&gt; &lt;br /&gt;        public SecurityResourceImpl()  &lt;br /&gt;        {  &lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        public void SetProxy(object proxy)  &lt;br /&gt;        {  &lt;br /&gt;                if (proxy is IContentProvider)  &lt;br /&gt;                {  &lt;br /&gt;                        Name = (proxy as IContentProvider).Name;  &lt;br /&gt;                }  &lt;br /&gt;                else if (proxy is IView)  &lt;br /&gt;                {  &lt;br /&gt;                        Name = (proxy as IView).Name;  &lt;br /&gt;                }  &lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        public String Name  &lt;br /&gt;        {  &lt;br /&gt;                get { return _name; }  &lt;br /&gt;                set { _name = value; }  &lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        public String ResourceName  &lt;br /&gt;        {  &lt;br /&gt;                get { return Name; }  &lt;br /&gt;        }  &lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Intercepting Invocation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Làm cách nào để khi gọi hàm RetrieveContent trên interface IContentProvider, nó gọi ISecurityResource.Demand() để thực hiện security check.&lt;br /&gt;&lt;br /&gt;Đầu tiên ta cần một pointcut để chọn các method và property. Bên trong một pointcut, ta có thể chọn các advice và thực hiện một vài action trên joinpoints. Có một số thuật ngữ mới như sau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Pointcut: chọn các method và|hoặc property bên trong một kiểu&lt;/li&gt;&lt;li&gt;Joinpoints: method hoặc property match pointcut&lt;/li&gt;&lt;li&gt;Advice: đoạn code sẽ được thực hiện trước|sau khi joinpoint&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Ví dụ trên được cấu hình như sau:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import YourCompany.CMS.ContentProviders in YourCompanyAssembly&lt;br /&gt;import YourCompany.CMS.Aop.Interceptors&lt;br /&gt; &lt;br /&gt; aspect SecurityAspect for RSSContentProvider&lt;br /&gt;      include Mixins.SecurityResourceImpl in MyMixinsAssembly&lt;br /&gt; &lt;br /&gt;      pointcut method(* RetrieveContent(*))&lt;br /&gt;            advice(SecurityCheckInterceptor)&lt;br /&gt;      end&lt;br /&gt; end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;pointcut ở trên cho thấy "nó không quan tâm đến kết quả trả về, cũng như parameter của hàm RetrieveContent". SecurityCheckInterceptor được implement như sau:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class SecurityCheckInterceptor : IMethodInterceptor&lt;br /&gt; {&lt;br /&gt;    public object Invoke(IMethodInvocation invocation)&lt;br /&gt;    {&lt;br /&gt;        ISecurityResource target = invocation.GetThis() as ISecurityResource;&lt;br /&gt;        target.Demand(); // Can throw a SecurityException&lt;br /&gt; &lt;br /&gt;        return invocation.Proceed(); // All right, get on with it&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;(from http://www.castleproject.org/AspectSharp/documentation/trunk/tutorial/basic.html)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6062347113936621305?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6062347113936621305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6062347113936621305' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6062347113936621305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6062347113936621305'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/aspect.html' title='Aspect#'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5163038568657325652</id><published>2008-04-02T23:50:00.004+07:00</published><updated>2008-04-03T00:00:52.817+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>yield</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_fLnGNScTh18/R_O5-hLmJEI/AAAAAAAAAG4/MspVV33C-wg/s1600-h/2008-04-02_235151.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_fLnGNScTh18/R_O5-hLmJEI/AAAAAAAAAG4/MspVV33C-wg/s400/2008-04-02_235151.png" alt="" id="BLOGGER_PHOTO_ID_5184692079743607874" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/7797046@N08/2382482161/" title="2008-04-02_235240 by thevinh.ng, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3253/2382482161_031a3d2ab0_o.png" width="447" height="719" alt="2008-04-02_235240" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5163038568657325652?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5163038568657325652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5163038568657325652' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5163038568657325652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5163038568657325652'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/04/yield.html' title='yield'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_fLnGNScTh18/R_O5-hLmJEI/AAAAAAAAAG4/MspVV33C-wg/s72-c/2008-04-02_235151.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6739782307135432783</id><published>2008-03-08T00:26:00.002+07:00</published><updated>2008-03-08T00:34:47.819+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><category scheme='http://www.blogger.com/atom/ns#' term='WF'/><title type='text'>WF questions</title><content type='html'>10 câu hỏi về workflow được tổng hợp và trả lời khá thú vị của Scott Allen&lt;br /&gt;&lt;br /&gt;1/ Thuận lợi và khó khăn khi chỉ sử dụng XAML để build workflow&lt;br /&gt;2/ So sánh ExternalDataExchange service với sử dụng trực tiếp WorkflowQueueingService&lt;br /&gt;3/ Khi nào dependency property hữu ích trong lập trình wf&lt;br /&gt;4/ Behavior mặc định của scheduling service là gì&lt;br /&gt;5/ Tại sao ta phải sử dụng database transaction cùng với một workflow instance&lt;br /&gt;6/ Tại sao ta phải sử dụng tracking service&lt;br /&gt;7/ Nêu một scenario khi cần phải cancel một executing activity&lt;br /&gt;8/ Nêu một scenario khi ta cần phải sinh ra một executing context mới&lt;br /&gt;9/ Tại sao ta phải sử dụng một compensation handler&lt;br /&gt;10/ Mô tả các activity sau: Replicator, Parallel, Policy&lt;br /&gt;&lt;br /&gt;(reference &lt;a href="http://odetocode.com/Blogs/scott/archive/2007/10/31/11528.aspx"&gt;here&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6739782307135432783?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6739782307135432783/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6739782307135432783' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6739782307135432783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6739782307135432783'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/03/wf-questions.html' title='WF questions'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-4570751201119449472</id><published>2008-03-08T00:23:00.002+07:00</published><updated>2008-03-08T00:25:56.663+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>VS 2008 Training Kit</title><content type='html'>Nếu ai là dân dốt nát thì không thể bỏ qua cái này. Download here: &lt;a href="http://go.microsoft.com/?linkid=7602397"&gt;http://go.microsoft.com/?linkid=7602397&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-4570751201119449472?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/4570751201119449472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=4570751201119449472' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4570751201119449472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4570751201119449472'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/03/vs-2008-training-kit.html' title='VS 2008 Training Kit'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-1063059655149052948</id><published>2008-02-13T01:19:00.001+07:00</published><updated>2008-02-13T01:47:12.757+07:00</updated><title type='text'>Deconstructing WF (2)</title><content type='html'>&lt;span style="font-style: italic;"&gt;tiếp theo &lt;a href="http://dzinhontech.blogspot.com/2008/02/deconstructing-wf.html"&gt;bài trước&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Trở lại chương trình HelloWorld ở bài trước, thay vì tạo BookMark tên Read, ta refactoring lại để nó tạo một ProgramStatement thực hiện việc Read (đúng bản chất động từ hơn là danh từ)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_fLnGNScTh18/R7HkJEryLzI/AAAAAAAAACA/wD_56ie0pc0/s1600-h/2008-02-13_012301.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_fLnGNScTh18/R7HkJEryLzI/AAAAAAAAACA/wD_56ie0pc0/s400/2008-02-13_012301.png" alt="" id="BLOGGER_PHOTO_ID_5166161092098010930" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_fLnGNScTh18/R7HkQEryL0I/AAAAAAAAACI/FjZNgMGwPm8/s1600-h/2008-02-13_012316.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_fLnGNScTh18/R7HkQEryL0I/AAAAAAAAACI/FjZNgMGwPm8/s400/2008-02-13_012316.png" alt="" id="BLOGGER_PHOTO_ID_5166161212357095234" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ProgramStatement là một abstract class mà các activity phải implement&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_fLnGNScTh18/R7HlRUryL1I/AAAAAAAAACQ/t1nHJx9s6xA/s1600-h/2008-02-13_012749.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_fLnGNScTh18/R7HlRUryL1I/AAAAAAAAACQ/t1nHJx9s6xA/s400/2008-02-13_012749.png" alt="" id="BLOGGER_PHOTO_ID_5166162333343559506" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Quan sát hai class Read và OpenSesame đều implement ProgramStatement, ta thấy có một sự tương đối trong việc định nghĩa activity. Một activity có thể duy nhất hoặc bao gồm các activity khác. Từ đó xuất hiện khái niệm ProgramStatementBlock (sau này gọi là CompositeActivity)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/R7HmYkryL2I/AAAAAAAAACY/jDUQLcnI0eY/s1600-h/2008-02-13_013214.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/R7HmYkryL2I/AAAAAAAAACY/jDUQLcnI0eY/s400/2008-02-13_013214.png" alt="" id="BLOGGER_PHOTO_ID_5166163557409238882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ProgamStatementBlock có thể được thực hiện từ tuần tự (Sequence), hoặc đồng thời (Interleave) hoặc thực hiện theo một số điều kiện nào đó (Control flow: IfElse, While, ...) tùy theo chúng ta phát triển.&lt;br /&gt;&lt;br /&gt;Tương tự như vậy ta viết tiếp ProgramStatement cho PrintKey, PrintGreeting. Chú ý là các class này không cần phải add vào bookMarkManager vì nó không cần chờ đợi user input data, ví dụ:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_fLnGNScTh18/R7HpPEryL4I/AAAAAAAAACo/DHquJGRhol0/s1600-h/2008-02-13_014419.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_fLnGNScTh18/R7HpPEryL4I/AAAAAAAAACo/DHquJGRhol0/s400/2008-02-13_014419.png" alt="" id="BLOGGER_PHOTO_ID_5166166692735364994" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Môi trường chạy bây giờ như sau:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_fLnGNScTh18/R7Hn30ryL3I/AAAAAAAAACg/XzQbvOy0a_8/s1600-h/2008-02-13_013824.png"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_fLnGNScTh18/R7Hn30ryL3I/AAAAAAAAACg/XzQbvOy0a_8/s400/2008-02-13_013824.png" alt="" id="BLOGGER_PHOTO_ID_5166165193791778674" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-1063059655149052948?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/1063059655149052948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=1063059655149052948' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1063059655149052948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1063059655149052948'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/02/deconstructing-wf-2.html' title='Deconstructing WF (2)'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_fLnGNScTh18/R7HkJEryLzI/AAAAAAAAACA/wD_56ie0pc0/s72-c/2008-02-13_012301.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-546673687758419562</id><published>2008-02-06T14:45:00.000+07:00</published><updated>2008-02-13T01:12:58.930+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Essential WF'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='WF'/><title type='text'>Deconstructing WF</title><content type='html'>&lt;span style="font-style: italic;"&gt;from chapter 1 - Essential Windows Workflow Foundation &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Một cuốn sách viết khá hay của tác giả &lt;a href="http://www.dharmashukla.com/"&gt;Dharma Shukla&lt;/a&gt;, về bản chất bên trong của WF.&lt;br /&gt;&lt;br /&gt;Xuất phát từ một ví dụ cơ bản&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_fLnGNScTh18/R7HYTUryLuI/AAAAAAAAABY/RL7pIZmBlC4/s1600-h/2008-02-13_003227.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_fLnGNScTh18/R7HYTUryLuI/AAAAAAAAABY/RL7pIZmBlC4/s400/2008-02-13_003227.png" alt="" id="BLOGGER_PHOTO_ID_5166148074052136674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;tác giả dẫn dắt và mở rộng ra thành một workflow framework rất thú vị.&lt;br /&gt;&lt;br /&gt;Sample code ban đầu như sau:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_fLnGNScTh18/R7HY40ryLvI/AAAAAAAAABg/oBSDzvQgMno/s1600-h/2008-02-13_003508.png"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_fLnGNScTh18/R7HY40ryLvI/AAAAAAAAABg/oBSDzvQgMno/s400/2008-02-13_003508.png" alt="" id="BLOGGER_PHOTO_ID_5166148718297231090" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ứng dụng gồm có ba bước:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In ra màn hình một key random&lt;/li&gt;&lt;li&gt;Chờ đợi user input một key (được viết tối ưu bằng cách đưa nó ra một thread riêng)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Nếu user input key bằng với key của bước 1, sẽ in ra câu hello world, nếu không sẽ exit, kết thúc flow&lt;/li&gt;&lt;/ul&gt;Chúng ta nhận thấy rằng ở bước hai có thể kéo dài rất lâu, hàng tuần nên nó cần được lưu lại ở một data storage nào đó. Khi nhận được user input, nó sẽ activate lại và chạy tiếp bước thứ ba.&lt;br /&gt;&lt;br /&gt;Hàm ContinueAt ở trên có thể xem là một BookMark (delegate được đặt tên) cần được lưu lại, và quản lý bởi BookMarkManager&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_fLnGNScTh18/R7Hbf0ryLwI/AAAAAAAAABo/nvJhc53C2KI/s1600-h/2008-02-13_004607.png"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_fLnGNScTh18/R7Hbf0ryLwI/AAAAAAAAABo/nvJhc53C2KI/s400/2008-02-13_004607.png" alt="" id="BLOGGER_PHOTO_ID_5166151587335384834" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Đoạn code trên cho thấy việc chờ đợi userInput trong hàm Readline() không còn là nhiệm vụ của nó nữa. Bằng cách nào đó BookMarkManager hoặc môi trường ngoài phải có nhiệm vụ nhận user input và gọi lài hàm ContinueAt đã đăng ký ở trên.&lt;br /&gt;&lt;br /&gt;Từ đây cũng manh nha ý tưởng về một hoạt động cơ bản nhất của một chương trình (sau này gọi là Activity trong workflow)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_fLnGNScTh18/R7HfQUryLxI/AAAAAAAAABw/n_DcHwAs8PM/s1600-h/2008-02-13_010056.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_fLnGNScTh18/R7HfQUryLxI/AAAAAAAAABw/n_DcHwAs8PM/s400/2008-02-13_010056.png" alt="" id="BLOGGER_PHOTO_ID_5166155719093923602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Môi trường chạy sẽ chờ nhận input, biết được input đó thuộc chương trình nào và resume chương trình đó lên để chạy tiếp các bước sau&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/R7HffkryLyI/AAAAAAAAAB4/RO7VVLeMeYM/s1600-h/2008-02-13_010210.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/R7HffkryLyI/AAAAAAAAAB4/RO7VVLeMeYM/s400/2008-02-13_010210.png" alt="" id="BLOGGER_PHOTO_ID_5166155981086928674" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-546673687758419562?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/546673687758419562/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=546673687758419562' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/546673687758419562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/546673687758419562'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/02/deconstructing-wf.html' title='Deconstructing WF'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_fLnGNScTh18/R7HYTUryLuI/AAAAAAAAABY/RL7pIZmBlC4/s72-c/2008-02-13_003227.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-9061584693872189370</id><published>2008-01-13T16:28:00.001+07:00</published><updated>2008-01-13T16:50:21.062+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Develop custom controls with complex properties</title><content type='html'>&lt;span style="font-style: italic;"&gt;(from chapter 7 - Prof. Asp.Net Server controls)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Chương này tác giả đề cập đến các custom controls phức tạp, có thể gây khó khăn cho việc maintain trạng thái và apply style cho các control con.&lt;br /&gt;&lt;br /&gt;Bài viết về state management khá hay. Sau đây là một số trích dẫn:&lt;br /&gt;&lt;br /&gt;Lưu trữ trạng thái của các object là một vấn đề trong web application, vì một user session có thể chứa nhiều hơn một request. Do đặc điểm của HTTP, nên object sẽ bị disposed vào cuối mỗi request, cho dù session vẫn cần object đó.&lt;br /&gt;&lt;br /&gt;ASP.Net view state cho phép ta lưu trạng thái của object vào cuối mỗi request và load lên trở lại vào đầu của request sau. Request tiếp theo sẽ làm như sau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tạo ra object mới có cùng kiểu như là object đã bị displosed vào thời điểm cuối của request trước đó.&lt;/li&gt;&lt;li&gt;Load trạng thái của object cũ vào object mới.&lt;/li&gt;&lt;/ul&gt;Cơ chế hoạt động của asp.net như sau: Mỗi server control thừa kế 3 method là TrackViewState, SaveViewState và LoadViewState từ class Control base. Vào cuối mỗi request:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Page tự động gọi hàm SaveViewState method của các control trong Controls collection.&lt;/li&gt;&lt;li&gt;SaveViewState của mỗi control sẽ save trạng thái của control và của các control con của nó, sau đó trả về object.&lt;/li&gt;&lt;li&gt;Page thu thập các object được trả về từ SaveViewState method của các controls và tạo ra một tree các object gọi là object graph.&lt;/li&gt;&lt;li&gt;Page framework convert các object thành từng string và kết hợp với nhau thành một string duy nhất để diễn tả toàn bộ object graph.&lt;/li&gt;&lt;li&gt;Page framework lưu trữ string này trong một hidden field, gọi là __VIEWSTATE&lt;/li&gt;&lt;/ul&gt;Khi page được post back về server:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Page framework nhận string từ __VIEWSTATE và extract ra các string diễn tả mỗi object, sau đó tạo lại object từ string đó.&lt;/li&gt;&lt;li&gt;Page framework gọi hàm LoadViewState cho mỗi control trong Controls collection, và truyền các object tương ứng được tạo ở trên vào.&lt;/li&gt;&lt;li&gt;Hàm LoadViewState của mỗi control sẽ load content của control của lần request trước đó vào trong nó. Như vậy là nó đã có cùng trạng thái với request trước đó.&lt;/li&gt;&lt;li&gt;Page framework gọi hàm TrackViewState của mỗi control để chỉ rằng nó đang tracking trang thái. Từ thời điểm này trở đi, mọi thay đổi trạng thái của object sẽ được make dirty và nó sẽ được save vào ở cuối của request.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-9061584693872189370?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/9061584693872189370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=9061584693872189370' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/9061584693872189370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/9061584693872189370'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/01/develop-custom-controls-with-complex.html' title='Develop custom controls with complex properties'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-1040209586256811915</id><published>2008-01-09T09:12:00.000+07:00</published><updated>2008-01-10T18:27:46.138+07:00</updated><title type='text'>Colour Schemas</title><content type='html'>&lt;span style="font-style: italic;"&gt;from Design for Web developers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Basic terms&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;Hue: màu nguyên chất (&lt;/span&gt;undiluted color, true color)&lt;br /&gt;&lt;br /&gt;&lt;span&gt;Saturation: độ sáng của một màu (intensity or purity), diễn tả mức độ nguyên chất của màu đó. Nếu ta pha thêm một chút đen hoặc trắng sẽ làm thay đổi độ nguyên chất ấy.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Value: độ sáng và tối của một màu&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Tint: màu xuất hiện với sự hiện diện của màu trắng, làm cho màu sáng hơn (vd: pink là tint of red)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Shade: màu xuất hiện với sự pha trộn của màu đen, làm cho nó tối hơn&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;img src="http://www.artsparx.com/images/bluetint01.gif" /&gt; (add white to blue - tint)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.artsparx.com/images/blueshade01.gif" /&gt; (add black to blue -shade)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Monochromatic schema&lt;/span&gt;&lt;br /&gt;Là tất cả các màu được mở rộng từ một màu đơn bằng cách thay đổi yếu tố tints và shades của nó (hoặc là black and white)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://z.about.com/d/interiordec/1/8/V/G/o1-color-blues02.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Hình bên dưới cho thấy sự thay đổi màu sắc khi thay đổi các thành phần black (shades, bên trái) và white (tints, bên phải).&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2123/2179684999_d93265cfc3_o.png" alt="monochroColor" height="213" width="395" /&gt;&lt;br /&gt;&lt;br /&gt;Trong photoshop, ta có thể thay đổi schema bằng các giá trị S (saturation) và B (brightness) trong màu HSB.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.webreference.com/authoring/graphics/color/nondesigners/chap2/1/image034.jpg" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-1040209586256811915?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/1040209586256811915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=1040209586256811915' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1040209586256811915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1040209586256811915'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2008/01/colour-schemas.html' title='Colour Schemas'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5204779961444118561</id><published>2007-12-30T12:34:00.000+07:00</published><updated>2008-01-09T09:12:44.820+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dotnetnuke'/><title type='text'>Hello world module in DNN</title><content type='html'>Cuối cùng cũng có thời gian rảnh ngồi viết cái thử cái module HelloWorld trên DNN. &lt;br /&gt;&lt;br /&gt;Logic của module như sau:&lt;br /&gt;- Anonymous vào sẽ hiện ra câu message default: Hello World&lt;br /&gt;- Registered user sẽ thấy câu chào: Hello $userName + bên dưới có một button để customize câu welcome message.&lt;br /&gt;&lt;br /&gt;Do source code của DNN viết bằng VB.Net (đặc biệt là trong App_Code folder) nên muốn viết bằng C# ta phải tạo một project riêng (dạng class library) và được reference vào từ portal (nếu muốn import module trong đó).&lt;br /&gt;&lt;br /&gt;Cấu trúc của project &lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2327/2148452884_8db0dc8952_o.png" width="223" height="144" alt="DNN_HelloProj" /&gt;&lt;br /&gt;&lt;br /&gt;Message class tương ứng với data sẽ persistent xuống database (chú ý property trong object phải giống với field name trong table của database). Đối với mỗi message được customize, thông tin tối thiểu nhất cần phải có là: moduleId (dùng để phân biệt giữa các module), userId (kiểu int) và welcomeMessage.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2165/2147658339_a488a5145f_o.png" width="307" height="659" alt="DNN_HelloMessage" /&gt;&lt;br /&gt;&lt;br /&gt;HelloWorld service&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2280/2147642067_772e995ff1_o.png" width="793" height="713" alt="DNN_HelloService" /&gt;&lt;br /&gt;&lt;br /&gt;Bây giờ tạo GUI cho module. Open source code của DNN. Trong folder DesktopModules tạo một folder tên HelloWorld, sau đó add vào 2 user controls là ViewHelloWorld.ascx (dùng để welcome user) và EditHelloWorld.ascx (dùng để customize message).&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2221/2147816221_315c85c085_o.png" width="271" height="481" alt="DNN_add module" /&gt;&lt;br /&gt;&lt;br /&gt;Trang ViewHelloWorld sẽ kiểm ra user xem đã sign in hay chưa, nếu đã sign in rồi thì lấy custom message ra để show, nếu không thì dùng default message.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2216/2147821965_11e5f0a669_o.png" width="824" height="87" alt="dnn ViewHWUC" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2183/2147822065_ea807d003e_o.png" width="718" height="570" alt="dnn ViewHelloWorld" /&gt;&lt;br /&gt;&lt;br /&gt;"helloWorld2" là key của trang EditHelloWorld khi add control vào trong module&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2368/2147822183_13d4b59cc8_o.png" width="590" height="525" alt="dnn EditHelloWorld" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5204779961444118561?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5204779961444118561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5204779961444118561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5204779961444118561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5204779961444118561'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/12/helloworld-module-in-dnn.html' title='Hello world module in DNN'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6463665852985267659</id><published>2007-12-04T00:12:00.000+07:00</published><updated>2008-01-13T16:26:37.900+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Developing custom templated control</title><content type='html'>&lt;span style="font-style: italic;"&gt;(from chapter 6 - Prof ASP.NET Server Controls)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Chương này hướng dẫn cách tạo sử dụng template trong control và data binding trong template đó.&lt;br /&gt;&lt;br /&gt;Để expose một template, ta public một property có kiểu là ITemplate, và trong hàm CreateChildControls(), gọi hàm InstantiateIn trên property đó nếu nó tồn tại.&lt;br /&gt;&lt;br /&gt;Ta viết một ví dụ template control hello-world như sau: nó là một place holder cho phép người dùng đặt bất kỳ template nào bên trong.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2171/2188443279_2f829d1c64_o.png" alt="2008-01-13_155645" height="647" width="656" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2382/2188446819_1e68b96c33_o.png" alt="2008-01-13_155915" height="147" width="597" /&gt;&lt;br /&gt;&lt;br /&gt;Như đoạn code ở trên, ta thấy data binding vào một property thật ra là gọi giá trị của property trên container template. Khi gọi hàm &lt;span style="font-weight: bold;"&gt;InstantiateIn&lt;/span&gt;, ASP.NET framework thực hiện các bước sau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tự động tạo ra một component hoặc class thừa kế interface ITemplate vàimplement hàm InstantiateIn của interface đó.&lt;/li&gt;&lt;li&gt;Tự động compile class này và assign instance của nó đến property template tương ứng của control.&lt;/li&gt;&lt;/ul&gt;Ví dụ nếu có template như sau:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2057/2188474621_1cc20271c2_o.png" alt="2008-01-13_161911" height="63" width="746" /&gt;&lt;br /&gt;&lt;br /&gt;thì class được generate tự động:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2378/2188474551_c98c4a4a26_o.png" alt="2008-01-13_161858" height="223" width="590" /&gt;&lt;br /&gt;&lt;br /&gt;do đó, sau khi hàm InstantiateIn được gọi xong, thì container đã chứa control được generate bởi asp.net&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6463665852985267659?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6463665852985267659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6463665852985267659' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6463665852985267659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6463665852985267659'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/12/developing-custom-templated-control_04.html' title='Developing custom templated control'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5345345086529212057</id><published>2007-11-17T10:46:00.000+07:00</published><updated>2008-01-09T09:12:44.821+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dotnetnuke'/><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>YetAnotherForum in DNN</title><content type='html'>Lò mò 1 ngày cũng cái được cái YetAnotherForum thành một module của DNN. &lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2319/2038720555_e4cff78efd_o.png" width="787" height="616" alt="YAFDNN" /&gt;&lt;br /&gt;&lt;br /&gt;Đọc gần hết cuốn sách mà thấy DNN vẫn chưa có gì hấp dẫn, ghet VB :-(&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5345345086529212057?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5345345086529212057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5345345086529212057' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5345345086529212057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5345345086529212057'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/yetanotherforum-in-dnn.html' title='YetAnotherForum in DNN'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6683254402793959485</id><published>2007-11-13T00:34:00.000+07:00</published><updated>2007-11-17T23:42:59.610+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>Hibernate: Hello World</title><content type='html'>&lt;span style="font-style:italic;"&gt;(from chapter 2 - Java persistent with Hibernate)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Chương này hướng dẫn cách integrate Hibernate vào trong java application (nhưng ta sẽ test nó trên .net). Bắt đầu bằng ví dụ kinh điển: "Hello World".&lt;br /&gt;&lt;br /&gt;Có lẽ đây là ví dụ Hello World khó chịu nhất mà mình từng viết. Tuy đã từng viết 1 cái sample trên asp.net lâu rồi mà bây giờ ngồi viết vẫn gặp khó khăn.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2168/2040037905_34aeeaa397_o.png" alt="HibernateHelloWorldSol" height="261" width="286" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2171/2040831820_7f70f97852_o.png" alt="HibernateHelloWorld" height="508" width="647" /&gt;&lt;br /&gt;&lt;br /&gt;configuration file&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2338/2040038075_b9b4ddac2e_o.png" alt="HibernateHelloWorldConfig" height="344" width="760" /&gt;&lt;br /&gt;&lt;br /&gt;mapping file&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2365/2040831962_460dabc05f_o.png" alt="HibernateHelloWorldMapping" height="169" width="892" /&gt;&lt;br /&gt;&lt;br /&gt;Cái hay của lần viết này là hibernate sẽ dựa vào mapping file trên persistent object để export ra schema cần thiết của database (tạo table trong database luôn). Đây là một cách tiếp cận đúng chuẩn và lý tưởng nhất cho một developer.&lt;br /&gt;&lt;br /&gt;Ví dụ này tạo một message "hello world" và lưu nó xuống database. Persistent class Message bao gồm: Id, Text và một reference đến một message khác (conferToMessage)&lt;br /&gt;&lt;br /&gt;Ta thấy các attribute của Message được diển tả bằng các property trong class Message. Constructor no-argument là một yêu cầu bắt buộc đối với Hibernate vì nó sử dụng reflection với constructor này để khởi tạo object.&lt;br /&gt;&lt;br /&gt;Message không cần phải implement một interface nào của Hibernate nên ta có thể dùng nó như các entity class khác, ví dụ:&lt;pre&gt;&lt;br /&gt;Message message = new Message("Hello world");&lt;br /&gt;Console.Writeline(message.Text);&lt;/pre&gt;&lt;br /&gt;Đây là một tính năng quan trọng của Hibernate, giúp nó khác biệt với các persistent solutions khác: đó là persistent class có thể được dùng trong bất kỳ execution context nào, không cần một container đặc biệt nào khác.&lt;br /&gt;&lt;br /&gt;Thông thường, developer đầu tiên sẽ phân tích business domain dưới góc nhìn object-oriented và thường chịu ảnh hưởng nhiều của một mô hình cơ sở dữ liệu quan hệ có sẵn: là một legacy database hoặc một schema được design bởi một data modeler. Một số câu hỏi cần phải trả lời:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ta có thể start từ một clean design, từ legacy data hoặc từ legacy application code?&lt;/li&gt;&lt;li&gt;Có phần code nào có thể được generate một cách tự động từ phần đã có sẵn (ví dụ java source được generate từ một database schema). Hoặc database schema được generate từ java code và Hibernate mapping metadata?&lt;/li&gt;&lt;li&gt;Công cụ nào hỗ trợ cho việc trên?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Roadmap:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Chọn một mô hình phát triển (development process)&lt;/li&gt;&lt;li&gt;Tạo ra project infrastructure (cơ sở hạ tầng của project)&lt;/li&gt;&lt;li&gt;Viết application code và mappings&lt;/li&gt;&lt;li&gt;Configure và start Hibernate&lt;/li&gt;&lt;li&gt;Chạy ứng dụng.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Development process&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Topdown&lt;/li&gt;&lt;li&gt;Bottom up&lt;/li&gt;&lt;li&gt;Middle out&lt;/li&gt;&lt;li&gt;Meet in the middle&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2190/2040069865_f1a4d89acf_o.png" width="600" height="336" alt="HibernateTool" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6683254402793959485?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6683254402793959485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6683254402793959485' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6683254402793959485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6683254402793959485'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/hibernate-hello-world.html' title='Hibernate: Hello World'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-8241004515501324680</id><published>2007-11-13T00:33:00.000+07:00</published><updated>2008-02-06T15:36:26.044+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Java Persistence with Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>Understanding object/relational persistence</title><content type='html'>&lt;span style="font-style: italic;"&gt;from chapter 1 - Java Persistence with Hibernate (Manning)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Chương này đề cập đến các vấn đề persistence mismatch giữa object-oriented software development và relational database.&lt;br /&gt;&lt;br /&gt;Persistence được đề cập mang ý nghĩa:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Lưu trữ, phân loại và truy xuất dữ liệu có cấu trúc&lt;/li&gt;&lt;li&gt;Đồng bộ và toàn vẹn dữ liệu&lt;/li&gt;&lt;li&gt;Data sharing&lt;/li&gt;&lt;/ul&gt;Một số mismatch được liệt kê như sau:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1/ Quan hệ một - nhiều&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_fLnGNScTh18/R6lsT-vmnBI/AAAAAAAAABA/HB-jvSx0IxQ/s1600-h/2008-02-06_151326.png"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_fLnGNScTh18/R6lsT-vmnBI/AAAAAAAAABA/HB-jvSx0IxQ/s400/2008-02-06_151326.png" alt="" id="BLOGGER_PHOTO_ID_5163777538272959506" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Một User có nhiều BillingDetails.&lt;br /&gt;&lt;br /&gt;Trong object-oriented ta khai báo 2 class User và BillingDetails, trong đó User chứa một tập các billingDetails (list, set), còn BillingDetails reference đến User owner của nó.&lt;br /&gt;&lt;br /&gt;Trong relational database thì ta sẽ dùng khóa ngoại, trong đó BillingDetails sẽ chứa userID - là khóa ngoại chỉ đến bảng User.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2/ Subtype problem (granularity)&lt;/span&gt;&lt;br /&gt;Relational database không hỗ trợ thừa kế như hướng đối tượng. Ví dụ BillingDetails sẽ bao gồm hai loại là CreditCard và BankAccount&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_fLnGNScTh18/R6lt2uvmnCI/AAAAAAAAABI/uUPL_A6X1Vo/s1600-h/2008-02-06_151850.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_fLnGNScTh18/R6lt2uvmnCI/AAAAAAAAABI/uUPL_A6X1Vo/s400/2008-02-06_151850.png" alt="" id="BLOGGER_PHOTO_ID_5163779234785041442" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3/ Identity problem&lt;/span&gt;&lt;br /&gt;Object trong hướng đối tượng có 2 kiểu bằng nhau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Giống nhau hoàn toàn, ngay cả bộ nhớ cấp phát (a == b)&lt;/li&gt;&lt;li&gt;Giống nhau do implement hàm equals() (gọi là equality by value)&lt;/li&gt;&lt;/ul&gt;Trong relational database thì 2 row bằng nhau khi có khóa chính bằng nhau&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4/ Association problem&lt;/span&gt;&lt;br /&gt;Quan hệ giữa hai table trong relational database có quan tâm đến hướng. Trong khi nhìn reference giữa 2 object ta không cách nào biết được.&lt;br /&gt;&lt;br /&gt;Quan hệ nhiều nhiều giữa 2 table được diễn tả bằng một table thứ ba trong relational database. Trong khi ở object-oriented ta không cần thêm một object nào nữa.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_fLnGNScTh18/R6lwOevmnDI/AAAAAAAAABQ/Chft_fRBRR8/s1600-h/2008-02-06_153023.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_fLnGNScTh18/R6lwOevmnDI/AAAAAAAAABQ/Chft_fRBRR8/s400/2008-02-06_153023.png" alt="" id="BLOGGER_PHOTO_ID_5163781841830190130" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;5/ Data navigation problem&lt;/span&gt;&lt;br /&gt;Trong object-oriented, ta có thể truy xuất thông tin của billing information như sau:&lt;br /&gt;aUser.getBillingDetails().getAccountNumber()&lt;br /&gt;&lt;br /&gt;Trong relational database, ta phải thực hiện n câu query để thực hiện việc đó.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-8241004515501324680?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/8241004515501324680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=8241004515501324680' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/8241004515501324680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/8241004515501324680'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/understanding-objectrelational.html' title='Understanding object/relational persistence'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_fLnGNScTh18/R6lsT-vmnBI/AAAAAAAAABA/HB-jvSx0IxQ/s72-c/2008-02-06_151326.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5013769081102878640</id><published>2007-11-12T23:48:00.000+07:00</published><updated>2007-11-13T00:21:35.504+07:00</updated><title type='text'>Prototypes and Inheritance (2)</title><content type='html'>&lt;span style="font-style:italic;"&gt;tiếp theo &lt;a href="http://dzinhontech.blogspot.com/2007/11/prototypes-and-inheritance.html"&gt;bài trước&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Khi ta đọc property p của object o, javascript đầu tiên sẽ kiểm tra xem o có property tên p chưa. Nếu chưa có, nó sẽ kiếm tiếp trong prototype của object o và cứ như vậy. Quá trình này gọi là prototype-based inheritance.&lt;br /&gt;&lt;br /&gt;Tuy nhiên prototype inheritance không xuất hiện khi write property. Nếu ta set property p trong object o, javascript sẽ định nghĩa ngay lập tức property này trong o mà không quan tâm là có hoặc có nên set trong prototype hay không. Từ đó trở đi, nếu ta truy xuất property này có nghĩa là ta chỉ truy xuất property của chính object đó.&lt;br /&gt;&lt;br /&gt;Lật ngược vấn đề lại nếu chúng ta cho phép ghi property lên prototype của nó, thì property này sẽ có thể bị truy xuất và thay đổi bởi các object. Đó là điều nhập nhằng mà js không mong muốn. Vd minh họa như sau:&lt;pre&gt;&lt;br /&gt;function Foo()&lt;br /&gt;{&lt;br /&gt;    this.foo = "hello world!";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Foo.prototype.fooPro = "i am prototype";&lt;br /&gt;&lt;br /&gt;var theFoo = new Foo();&lt;br /&gt;alert(theFoo.fooPro); // alert: i am prototype&lt;br /&gt;&lt;br /&gt;var theFoo2 = new Foo();&lt;br /&gt;theFoo.fooPro = "i am child";&lt;br /&gt;alert(theFoo.fooPro); // alert: i am prototype&lt;br /&gt;alert(theFoo2.fooPro); // alert: i am prototype&lt;/pre&gt;&lt;br /&gt;Ví dụ trên cho thấy, sau khi set fooPro property trong prototype, các object của Foo đều truy xuất được (prototype-based inheritance). Khi ta set property trên một object nào đó, chỉ có mình nó là thay đổi, các object khác vẫn không bị ảnh hưởng.&lt;br /&gt;&lt;br /&gt;Cuối cùng là một số khái niệm ta cần phải nắm rõ về property/method trên instance/class:&lt;pre&gt;&lt;br /&gt;//&lt;i&gt;Instance property&lt;/i&gt;&lt;br /&gt;function Circle(radius) {&lt;br /&gt;  this.r = radius; &lt;i&gt;// r là instance property&lt;/i&gt;&lt;br /&gt;}&lt;br /&gt;//&lt;i&gt;Instance method&lt;/i&gt;&lt;br /&gt;Circle.prototype.area = function() {...}&lt;br /&gt;//&lt;i&gt;Class property&lt;/i&gt;&lt;br /&gt;Circle.PI = 3.14;&lt;br /&gt;//&lt;i&gt;Class method&lt;/i&gt;&lt;br /&gt;Circle.max= function() { ... };&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5013769081102878640?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5013769081102878640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5013769081102878640' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5013769081102878640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5013769081102878640'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/prototypes-and-inheritance-2.html' title='Prototypes and Inheritance (2)'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-4246976081463840022</id><published>2007-11-09T17:30:00.000+07:00</published><updated>2007-11-12T23:46:56.516+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Prototypes and Inheritance</title><content type='html'>&lt;span style="font-style: italic;"&gt;from Javascript - The Definitive Guide, 5th edition (Oreilly)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Method là một function được invoke như là property của một object (tức là phải được gọi trên một đối tượng nào đó). Nếu ta gọi hàm:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function calculateAreaOfRectangle(r) { return r.width * r.height }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;thì không object-oriented&lt;br /&gt;&lt;br /&gt;Mong muốn của chúng ta:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var r = new Rectangle(10, 5); // width, height&lt;br /&gt;var result = r.area();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;hiện thực:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function Rectangle(w, h) {&lt;br /&gt; this.width = w;&lt;br /&gt; this.height = h;&lt;br /&gt; this.area = function {&lt;br /&gt;   return this.width * this.height;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Mọi thứ có vẻ ok trừ 1 vấn đề sau: mỗi rectangle đều có width và height riêng, nhưng đâu nhất thiết các instance của Rectangle đều reference đến cùng một function area. Có cách nào có thể share function area cho mọi instance?&lt;br /&gt;&lt;br /&gt;Mọi javascript object đều chứa một reference (internal) đến một object khác, thông qua prototype property. Các property của object được prototype chỉ đến đều trở thành property của chính object đó. Nói cách khác javascript object thừa kế các property từ prototype của nó.&lt;br /&gt;&lt;br /&gt;Trong bài trước, ta biết toán tử new tạo ra một object mới empty và invoke constructor function như là method của object đó.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;&lt;u&gt;Note&lt;/u&gt;: một function được design để dùng với toán tử new gọi là constructor function hay là constructor&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sau khi tạo ra object, toán tử new thiết lập prototype cho object đó. Prototype của một object là property của constructor function của object đó. Tất cả các function đều có một property prototype, được tự động tạo khi function được định nghĩa. Giá trị khởi đầu của property prototype này là một object với một property duy nhất, tên là constructor (trỏ ngược lại chính function đó, chắc điên quá!!!).  Mô hình hóa phát biểu trên như sau: (tham khảo từ site http://www.mollypages.org/misc/js.mp)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.mollypages.org/misc/jsobj.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Ta nhận thấy rằng việc thừa kế xuất hiện một cách tự động trong quá trình tìm kiếm một property value. Các properties không được copy từ prototype object vào trong các object mới, chỉ đơn thuần xuất hiện như là property của chính object đó mà thôi. Có 2 hệ quả quan trọng:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Việc sử dụng prototype có thể làm giảm đi bộ nhớ cần thiết cho một object vì nó có thể thừa kế được các property.&lt;/li&gt;&lt;li&gt;Một object có thể thừa kế được property ngay cả khi chúng được add vào prototype object sau khi object đó đã được tạo ra.&lt;/li&gt;&lt;/ul&gt;Javascript cung cấp phương thức để kiểm tra property có thuộc trực tiếp của một object hay không, đó là: hasOwnProperty().&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Rectangle.prototype.area = function() { return this.width * this.height; }&lt;br /&gt;&lt;br /&gt;var r = new Rectangle(2,3);&lt;br /&gt;r.hasOwnProperty("width"); // true: vì width là property  trực tiếp của r&lt;br /&gt;r.hasOwnProperty("area"); // false: vì area là một property được thừa kế của r&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;(còn tiếp)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-4246976081463840022?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/4246976081463840022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=4246976081463840022' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4246976081463840022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4246976081463840022'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/prototypes-and-inheritance.html' title='Prototypes and Inheritance'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5125908964797232214</id><published>2007-11-07T17:58:00.000+07:00</published><updated>2007-11-12T23:44:49.343+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Object-Oriented Javascript (2)</title><content type='html'>&lt;span style="font-style: italic;"&gt;tiếp theo &lt;a href="http://dzinhontech.blogspot.com/2007/11/object-oriented-javascript.html"&gt;bài trước&lt;/a&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Closures&lt;/span&gt;&lt;br /&gt;Phần closure tác giả viết không hay như trong cuốn &lt;span style="font-style: italic;"&gt;Asp.net Ajax in Action&lt;/span&gt; (chương 3 - javascript for ajax developers).&lt;br /&gt;&lt;br /&gt;Closures là cách thức một inner function có thể tham khảo đến các biến trong một function cha, ngay cả khi function cha đó đã bị terminated. Xem ví dụ sau&lt;br /&gt;&lt;pre&gt;function parent(arg) {&lt;br /&gt;  var parentVar = "I am a variable of parent";&lt;br /&gt;&lt;br /&gt;  function child() {&lt;br /&gt;   alert(arg + parentVar);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  child();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;parent("hello, "); &lt;span style="font-style: italic;"&gt;// alert message hello, I am a variable of parent&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Hàm child trong ví dụ trên gọi là &lt;span style="font-style: italic;"&gt;nested &lt;/span&gt;function, được khai báo bên trong hàm cha. Ta thấy nó có thể truy xuất các biết định nghĩa bên ngoài function body của nó. Vấn đề này bình thường. Chuyện gì xảy ra nếu ta thay đổi như sau:&lt;pre&gt;&lt;br /&gt;function parent2(arg) {&lt;br /&gt;  var parentVar = "I am a variable of parent";&lt;br /&gt;&lt;br /&gt;  function child() {&lt;br /&gt;    alert(arg + parentVar);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return child;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var theChild = parent2("yes, ");&lt;br /&gt;theChild(); &lt;span style="font-style: italic;"&gt;// alert message - yes, I am a variable of parent&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Ta thấy rõ ràng sau khi hàm parent2 của nó được gọi và terminated, các variables của nó vẫn còn tồn tại và có thể được truy xuất bởi inner function. Đây gọi là &lt;span style="font-style: italic;"&gt;closure&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Context&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Là object vùng code mà object đang hoạt động bên trong. Biến this luôn reference đến object này (trong global context, biến this chỉ đến window object). Xem ví dụ sau:&lt;br /&gt;&lt;pre&gt;var obj = {&lt;br /&gt;  yes: function() {&lt;br /&gt;    &lt;span style="font-style: italic;"&gt;   // this == obj&lt;/span&gt;&lt;br /&gt;    this.value = true;&lt;br /&gt;  },&lt;br /&gt;  no: function() {&lt;br /&gt;    this.val = false;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;alert(obj.val == null); &lt;span style="font-style: italic;"&gt;// ban đầu chưa gọi hàm nên biến val chưa tồn tại&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;obj.yes();&lt;br /&gt;alert(obj.val == true);&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;// ta trỏ hàm no của object window vô hàm no của object obj&lt;/span&gt;&lt;br /&gt;window.no = obj.no;&lt;br /&gt;window.no(); &lt;span style="font-style: italic;"&gt;// hàm no sẽ được execute trong context của window object&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;alert(obj.val == true); &lt;span style="font-style: italic;"&gt;// do đó val của obj không bị thay đổi&lt;/span&gt;&lt;br /&gt;alert(window.val == false); &lt;span style="font-style: italic;"&gt;// hiển nhiên&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Javascript cung cấp một cách set context vào trong function khi gọi hàm, thông qua hàm call. Ví dụ:&lt;br /&gt;&lt;br /&gt;function changeColor(color) {&lt;br /&gt; this.style.color = color;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var main = document.getElementById("main");&lt;br /&gt;changeColor.&lt;span style="font-weight: bold;"&gt;call&lt;/span&gt;(main, "black"); &lt;span style="font-style: italic;"&gt;// this bây giờ trỏ vô biến main&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Object Creation&lt;/span&gt;&lt;br /&gt;Phần này thấy đơn giản nhưng cũng có nhiều điều thú vị. Trong javascript, không có khái niệm class (nên không thể tạo object với một kiểu class nào đó), chỉ có object. Object này có thể tạo ra object kia và thừa kế từ các object khác (gọi là &lt;span style="font-style: italic;"&gt;prototypal inheritance&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;Để tạo một object có 1 kiểu nào đó, javascript cung cấp phương pháp khởi tạo function như là một object. Xem ví dụ sau:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function User(name) {&lt;br /&gt; this.name = name;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var me = new User("my name"); &lt;span style="font-style: italic;"&gt;// khởi tạo object của function đó qua toán tử new  (constructor)&lt;/span&gt;&lt;br /&gt;alert(me.name == "my name"); &lt;span style="font-style: italic;"&gt;// name bây giờ trở thành property của object me, this pointer trỏ vào me&lt;/span&gt;&lt;br /&gt;alert(me.constructor == User); &lt;span style="font-style: italic;"&gt;// property constructor của me bây giờ chính là User&lt;/span&gt;&lt;/pre&gt;Ví dụ này chứng tỏ:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Mỗi object đều có constructor property&lt;/li&gt;&lt;li&gt;Constructor property trỏ vào function nào đã tạo ra nó.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5125908964797232214?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5125908964797232214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5125908964797232214' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5125908964797232214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5125908964797232214'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/object-oriented-javascript-2.html' title='Object-Oriented Javascript (2)'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-2616562040501055830</id><published>2007-11-06T23:50:00.000+07:00</published><updated>2007-11-12T23:45:29.340+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Object-Oriented Javascript</title><content type='html'>from chapter 2 of &lt;span style="font-style: italic;"&gt;Prof Javascript Techniques (Apress)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Không thể nào bỏ xuống được khi đọc. Cuốn sách này làm thay đổi quan điểm của mình về Apress - một nhà sách viết bởi mấy tác giả củ chuối của Ấn Độ, toàn là mấy cái dễ và ko có gì sâu cả :-D.&lt;br /&gt;&lt;br /&gt;Chương này tác giả đánh giá là chương quan trọng nhất trong cuốn sách, bàn về references, scope, closure, context.&lt;br /&gt;&lt;br /&gt;Thuật ngữ object-oriented javascript có vẽ thừa thải vì javascript language toàn bộ là object, có cái nào khác object đâu. Nhưng tác giả muốn nhấn mạnh ở đây vì đa số cách sử dụng toàn là functionally, hiểu sai khái niệm và mơ hồ trong việc sử dụng object của hầu hết các developer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;References&lt;/span&gt;&lt;br /&gt;Reference là 1 pointer trỏ đến location thật của object. Nếu 2 object cùng 1 reference thì sửa cái này cái kia cũng bị thay đổi theo (cái này bình thường). Tuy nhiên, một số cái ta có thể không để ý:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Javascript không có khái niệm reference đến 1 reference (Perl có), mà object khi reference sẽ reference đến một object thật sự. Do đó nếu object ban đầu trỏ đến một đối tượng khác thì các object kia reference đến nó sẽ không bị ảnh hưởng.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;var items = new Array("one", "two", "three");&lt;br /&gt;// itemsRef reference to item&lt;br /&gt;var itemsRef = items;&lt;br /&gt;&lt;br /&gt;// set items to equal a new object&lt;br /&gt;items = new Array("four", "five");&lt;br /&gt;&lt;br /&gt;alert(items != itemsRef);&lt;br /&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;Chú ý một số tác vụ modify object có thể sinh ra một object mới (như concatenate string trong js)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;&lt;pre&gt;var item = "test";&lt;br /&gt;var itemRef = item;&lt;br /&gt;item += "ing";&lt;br /&gt;// do tác vụ cộng chuỗi làm cho object item trỏ đến một đối tượng khác, không còn là "test" nữa&lt;br /&gt;alert(item != itemRef);&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;Scope&lt;/span&gt;&lt;br /&gt;Scope được tính bên trong function, không phải bên trong block (vd while, if, for). Trong js, mỗi function được execute như là một method của một object.&lt;br /&gt;&lt;br /&gt;Nếu ta khai báo biến và function khơi khơi, không chỉ định 1 scope nào hết thì nó thuộc về global scope, thật ra là thuộc về scope của object window. Xem ví dụ sau:&lt;br /&gt;&lt;pre&gt;var foo = "test";&lt;br /&gt;&lt;br /&gt;function test() {&lt;br /&gt;  var foo = "tele";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;test();&lt;br /&gt;alert(foo == "tele"); // return false; &lt;/pre&gt;do biến foo bên trong hàm test chỉ thuộc scope của hàm test nên nó không làm ảnh hưởng đến biến toàn cục foo bên ngoài.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(be continue...)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-2616562040501055830?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/2616562040501055830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=2616562040501055830' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2616562040501055830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2616562040501055830'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/object-oriented-javascript.html' title='Object-Oriented Javascript'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6795016197489134204</id><published>2007-11-06T23:16:00.000+07:00</published><updated>2007-11-12T23:45:58.889+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>Default transaction scope in ms sql storeproc</title><content type='html'>Mới thua độ ly cafe cho bác Duy. Câu hỏi được đặt ra như sau: nếu không khai báo tường minh begin/end trans trong sql store procedure thì default transaction scope từ đâu đến đâu.&lt;br /&gt;&lt;br /&gt;Mình nhớ là đã đọc ở đâu đó rồi là M$ default tạo ra 1 transaction từ đầu đến đuôi khi execute storeproc nên trả lời như vậy. Bác Duy thì reject (vì ổng đã troubleshoot trong thực tế một lần rồi). Giao cho DuyN kiểm chứng, kết quả như sau:&lt;br /&gt;&lt;br /&gt;Viết 1 storeproc in ra giá trị transaction count trong hai trường hợp, có khai báo và không khai báo begin tran&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ALTER PROCEDURE TestTrans&lt;br /&gt;AS&lt;br /&gt;&lt;br /&gt;BEGIN tran&lt;br /&gt;PRINT @@TRANCOUNT&lt;br /&gt;COMMIT tran&lt;br /&gt;&lt;br /&gt;EXEC TestTrans&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Kết quả rất đau buồn, nó in ra số 1 khi có khai báo và 0 khi bỏ đi. Không cam tâm mình tra lại cuốn SQL Bible 2005 để xem hồi trước đọc ý này ở đâu thì thấy như sau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;M$ quan điểm một sql statement là một transaction, đây là đơn vị transaction nhỏ nhất được ms tự hiểu mà ko cần khai báo.&lt;/li&gt;&lt;li&gt;Đối với trường hợp batch statement, buộc phải khai báo tường minh begin/end tran (oracle tự động set transaction khi execute một batch các statement)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;M$ cung cấp một option để setting ở mức server là SET IMPLICIT_TRANSACTIONS {On | Off} cho phép tự động start một transaction khi execute một số statement và không đang ở trong một transaction khác.&lt;/li&gt;&lt;/ul&gt;Kêu DuyN kiểm chứng lại 1 lần nữa thì đúng, do M$ default set nó về Off, developer phải set tường minh. Đây là một hậu quả của việc đọc nhiều nhưng nhớ không chính xác, không chịu kiểm chứng trong thực tế thì ... không đọc gì còn tốt hơn :-D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6795016197489134204?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6795016197489134204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6795016197489134204' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6795016197489134204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6795016197489134204'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/11/default-transaction-scope-in-ms-sql.html' title='Default transaction scope in ms sql storeproc'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-1456936562599535041</id><published>2007-10-31T00:28:00.000+07:00</published><updated>2007-11-12T23:45:58.889+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>Lock and Isolation level</title><content type='html'>&lt;span style="font-style: italic;"&gt;from chapter 51 - SQL Server 2005 Bible&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Có gì đó nhập nhằng giữa lock và isolation level của sql server nên phải xem lại. Kết luận:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Lock mang ý nghĩa rộng hơn isolation level, nó là cơ chế lock một resource không cho 2 transaction cùng truy xuất đồng thời để đảm bảo toàn vẹn dữ liệu.&lt;/li&gt;&lt;li&gt;Isolation level là một chiến thuật lock, diễn tả mức độ &lt;span style="font-style: italic;"&gt;isolation &lt;/span&gt;của một transaction, nó là một khái niệm được đặt ra và được implement bởi lock.&lt;/li&gt;&lt;/ul&gt;Isolation level bao gồm:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Read Uncommitted&lt;/li&gt;&lt;li&gt;Read Committed&lt;/li&gt;&lt;li&gt;Repeatable Read&lt;/li&gt;&lt;li&gt;Serializable&lt;/li&gt;&lt;li&gt;Snapshot&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Read Committed Isolation&lt;/li&gt;&lt;/ul&gt;Lock có 3 đặc điểm:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Granularity: size of lock (row, page, extent = 8 pages, table, database, key)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Mode: type of lock (intent shared, shared, update, intent exclusive, exclusive, shared with intent exclusive)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Duration: được quyết định bởi isolation mode. Ví dụ share-lock với mode read committed sẽ lock trong khi transaction đọc data, còn exclusive-lock sẽ giữ cho đến khi transaction committed.&lt;/li&gt;&lt;/ul&gt;Locking Hints&lt;br /&gt;&lt;br /&gt;Một khái niệm mới khá thú vị. Cũng là 1 chiến thuật lock nhưng nó không ảnh hưởng lên toàn bộ connection như isolation level. Nó chỉ riêng biệt cho một table bên trong một query. Cách viết được đặt sau tên table sau from, cú pháp &lt;span style="font-style: italic;"&gt;with (locking hint).&lt;/span&gt; Ví dụ:&lt;br /&gt;&lt;pre&gt;select messageID from MTs with (RowReadPast)&lt;/pre&gt;&lt;br /&gt;Một số locking hint:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ReadPast: bỏ qua các row bị lock thay vì phải đợi (cool)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;RowLock: lock trên row&lt;/li&gt;&lt;li&gt;PagLock: page lock&lt;/li&gt;&lt;li&gt;TabLock: tự động tăng dần đến các level lock, từ row, page, extent đến table lock&lt;/li&gt;&lt;li&gt;...&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-1456936562599535041?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/1456936562599535041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=1456936562599535041' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1456936562599535041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1456936562599535041'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/lock-and-isolation-level.html' title='Lock and Isolation level'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-5437895445087323513</id><published>2007-10-19T16:42:00.001+07:00</published><updated>2008-01-09T09:16:38.023+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dotnetnuke'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof DNN (Wrox)'/><title type='text'>Story of DNN</title><content type='html'>&lt;span style="font-style: italic;"&gt;from chapter 1 of Professional DDN (Wrox)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Không ngờ lịch sử phát triển của DNN lại dài dòng và phức tạp như vậy. Đọc nó mới hiểu thêm vấn đề là việc phát triển một open source product cũng không đơn giản. Sau đây là một số mốc chính&lt;br /&gt;&lt;br /&gt;1/ Version đầu tiên có tên là IBuySpy Workshop, là bản mở rộng của IBuySpy Portal của M$. IBuySpy có license là EULA của M$ (ko giới hạn cách sử dụng, giống open source). Version này được cộng đồng trong forum asp.net sử dụng rộng rãi và được M$ support bằng cách để cái link và reference đến nó.&lt;br /&gt;&lt;br /&gt;2/ Tác giả quyết định charge tiền cho những ai muốn download latest source code. Đây là một bước lùi và sau đó tác giả phải viết một lá thư xin lỗi cộng động vì đã đi sai đường so với định hướng ban đầu, đồng thời trả lại tiền cho những ai đăng ký. (hình thức sau này là donation)&lt;br /&gt;&lt;br /&gt;3/ M$ quyết định tài trợ cho IBuySpy Workshop và yêu cầu đổi tên product vì bây giờ nó đã phát triển khá xa so với IBuySpy. Tác giả quyết định lấy tên là DotNetNuke vào 2/2003&lt;br /&gt;&lt;br /&gt;4/License Microsoft EULA có vẻ không phù hợp vì nó không bắt buộc include bất kỳ license nào giống vậy trong các nhánh, cũng như copyright của owner.  Tác giả vừa muốn commercial và non-commercial product nên chọn BSD license (yêu cầu giữ lại copyright của nhánh gốc trên tất cả các nhánh phát triển)&lt;br /&gt;&lt;br /&gt;5/ Sự va chạm giữa các nhánh về vấn đề sharing source code. Một nhánh khác tên là DotNetNuke XXL ra đời với quảng cáo là mạnh hơn, và user có thể download latest source code vào bất kỳ lúc nào. Tác giả quyết định phủ nhận nó trong forum community của DNN.&lt;br /&gt;&lt;br /&gt;6/ Một số thông tin về quá trình hình thành core team, định hướng phát triển và ý tưởng thay đổi version của DNN.&lt;br /&gt;&lt;br /&gt;Arguments:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;DNN có phải là 1 sản phẩm open source thuần túy? Nó được phát triển trên SQL Server, VS.Net và chỉ có mã nguồn là mở. Đó là lý do tại sao M$ tài trợ cho DNN vì nếu user sử dụng DNN để triển khai thì buộc phải mua sản phẩm của M$&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Thế nào là một mã nguồn mở thuần túy? Rõ ràng ý tưởng kinh doanh trên mã nguồn mở đã có ngay từ lúc đầu nên tác giả mới chọn BSD license. Sự phát triển của DNN làm phát triển hay thụt lùi ý nghĩa open source trong cộng đồng!&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Không hiểu sao tác giả vẫn chọn VB.Net để phát triển đến giờ, botay.com :-D&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-5437895445087323513?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/5437895445087323513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=5437895445087323513' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5437895445087323513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/5437895445087323513'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/story-of-dnn.html' title='Story of DNN'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-1966892411993622996</id><published>2007-10-17T23:15:00.000+07:00</published><updated>2007-11-12T23:45:58.890+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>Oops, I refactored it again...</title><content type='html'>&lt;span style="font-style: italic;"&gt;continue from last previous post - workflows&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Trong đoạn draft implementation của bài trước, bác Phước remind là tên class có suffix là Command mặc dù khi re-design mình bỏ hoàn toàn tư tưởng của Command pattern. Để thử tìm kiếm 1 cái tên nào khác cho phù hợp.&lt;br /&gt;&lt;br /&gt;Cách đặt tên trong windows workflow foundation như sau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Nguyên cái tree (tên đúng là workflow) được đặt tên có dạng vd như là OrderProcessingWorkflow, xxxWorkflow. Trong WF (workflow foundation), để tạo workflow ta phải tạo ra 1 class thừa kế từ một class base của workflow, trong đó cơ bản nhất có 2 dạng: SequentialWorkflowActivity, hoặc StateMachineWorkflowActivity (do bản chất của workflow được M$ chia ra làm 2 loại Ordered workflow và Event-Driven workflow). Còn trong mô hình của mình viết thì nó chỉ là một cái node gốc, không hơn kém, nhưng ta vẫn có thể đặt tên như vậy nếu muốn.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Các node trong tree được đặt tên là chính cái activity đó (động từ), và không có suffix, prefix gì hết. Ví dụ như là: GetProductData, UpdateOrder... Trong WF, các class này buộc phải thừa kế từ 1 class cơ bản tên là Activity. Nhân tiện ta review cái base class này xem sao:&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://farm3.static.flickr.com/2213/1601498325_29b7b9d6fd_o.png" alt="2007-10-18_002700" height="556" width="486" /&gt;&lt;br /&gt;&lt;br /&gt;Giải thích cho sự phức tạp này là yêu cầu cần thiết cho một workflow đúng nghĩa là:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Có khả năng cho biết trạng thái hiện tại của nó, vd đang được execute ở node nào, trạng thái là gì...&lt;/li&gt;&lt;li&gt;Nó có thể bị save và reload, vì workflow có thể chạy trong 1 thời gian dài, nhiều ngày hoặc nhiều tháng.&lt;/li&gt;&lt;li&gt;Support multiple thread&lt;/li&gt;&lt;li&gt;....&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Vĩ có đưa ra một mô hình dựa trên FlowChart khá thú vị, trong đó có các khái niệm:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Process&lt;/li&gt;&lt;li&gt;Decision (or conditional)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Start, end, input/output: (cái này không cần lắm)&lt;/li&gt;&lt;/ul&gt;Từ đó ta có thể build các process, decision và liên kết chúng lại với nhau. Process chạy xong sẽ chạy tiếp decision để ra quyết định, còn decision sẽ quyết định tiếp chạy process nào. Khả năng INJECT của mô hình này có vẻ tốt hơn, tuy nhiên chúng ta vẫn phải giải quyết vấn đề interface giữa chúng với nhau (giống như hàm IsValid mà node cha gọi node code để quyết định chạy node con nào trong bài trước). Mình sẽ suy nghĩ tiếp về mô hình này, nhưng bây giờ mà change nữa là bị tụi nó giết chết :-D&lt;br /&gt;&lt;br /&gt;Một câu hỏi cuối cùng là workflow và flowchart khác nhau như thế nào?&lt;br /&gt;Tra định nghĩa trong vài cuốn sách, thì workflow:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;là một sự sắp xếp, tập hợp các hoạt động được thực thi để đạt mục đích cuối. Nó có thể bao gồm cả human process (vd reviewing, approving documents) và automated process (vd tự động truy xuất data và execute một tập các logic dựa trên data có được). Workflow có thể kéo dài vài giờ, vài ngày hoặc vài tháng (chứ không chỉ chạy một cái là xong liền trong vài giây).&lt;/li&gt;&lt;li&gt;trong wiki định nghĩa trừu tượng hơn, và còn phân biệt workflow với process, planning và scheduling&lt;/li&gt;&lt;/ul&gt;Còn flowchart theo wiki là một sự diễn tả sơ đồ của một process hoặc một giải thuật. Như vậy thì bản chất của hai cái này không giống nhau nên không thể so sánh với nhau được.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Real Implementation&lt;/span&gt;&lt;br /&gt;// chưa có thời gian post tiếp :-D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-1966892411993622996?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/1966892411993622996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=1966892411993622996' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1966892411993622996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1966892411993622996'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/oops-i-refactored-it-again.html' title='Oops, I refactored it again...'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-415481908738058450</id><published>2007-10-16T23:32:00.000+07:00</published><updated>2007-11-12T23:45:58.890+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>Workflows</title><content type='html'>&lt;span style="font-style: italic;"&gt;continue to previous post - &lt;a href="http://vinhonbooks.blogspot.com/2007/10/command-pattern.html"&gt;Command Patterns&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Có gì đó không ổn với việc refactor đoạn code trong bài viết trước:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Làm sao để xác định command nào cần execute cho mỗi incoming - tức là invoker thật sự cần phải biết về command mà nó thực hiện. Mặc dù ta cố gắng che dấu nó bằng việc implement trong CommandFactory.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Đoạn code trong hàm execute( ): thực chất là 1 chuỗi các command được thực hiện bên trong và có một đặc điểm quan trọng là các command có quan hệ với nhau. Command nào được excute tiếp theo tùy thuộc vào giá trị trả về của việc execute command trước đó.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;--&gt; áp dụng pattern này không phù hợp.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Refactor with workflow&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Hình ảnh trong đầu là 1 tree các node (gọi là task, hoặc command) nhiều chiều (graph), trong đó mỗi node sẽ chứa được các node con của mình. Sau khi execute, node dựa vào kết quả trả về để duyệt các node con và quyết định node nào được thực thi tiếp. Cứ như thế đệ quy cho đến node lá.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2059/1588934581_b87bbb20a8_o.png" width="292" height="204" alt="2007-10-17_000854" /&gt;&lt;br /&gt;&lt;br /&gt;Developer chỉ cần build tree này trước, và execute node root cho mỗi incoming.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Draft Implementation&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public abstract class NodeCommand&lt;br /&gt;{&lt;br /&gt;  public NodeCommand NodeCommandChilds;&lt;br /&gt;  public abstract void Execute(Incoming incoming);&lt;br /&gt;  public bool IsValid(object validObject);&lt;br /&gt;  ....&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class ACommand : NodeCommand&lt;br /&gt;{&lt;br /&gt;  ...&lt;br /&gt;  public bool IsValid(object validObject)&lt;br /&gt;  {&lt;br /&gt;     ACommandValidObject aValidObj = validObject as ACommandValidObject;&lt;br /&gt;     if (aValidObj.ResponseCode == '001') return true;&lt;br /&gt;     return false;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Execute(Incoming incoming)&lt;br /&gt;  {&lt;br /&gt;     response = bp.MoneyTransfer();&lt;br /&gt;     foreach(NodeCommand node in NodeCommandChilds)&lt;br /&gt;     {&lt;br /&gt;        if (node.IsValid(response)) node.Execute(incoming);&lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class TestProgram&lt;br /&gt;{&lt;br /&gt;  public void Test()&lt;br /&gt;  {&lt;br /&gt;     Incoming incoming = GetIncomingFromQueue();&lt;br /&gt;     NodeCommand node = BuildNodeCommandTree();&lt;br /&gt;     node.Execute(incoming);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Mô hình code này đã decouple được business flow của ứng dụng và việc execute business của ứng dụng đó. Nó có thể đáp ứng dễ dàng các thay đổi cũng như áp dụng MOCK OBJECT pattern trong việc test, một hiệu ứng lề khá thú vị :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-415481908738058450?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/415481908738058450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=415481908738058450' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/415481908738058450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/415481908738058450'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/workflows.html' title='Workflows'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-4526612811973190073</id><published>2007-10-15T20:13:00.000+07:00</published><updated>2007-10-16T11:58:59.160+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Design Patterns (Head First)'/><title type='text'>Command Pattern</title><content type='html'>&lt;span style="font-style: italic;"&gt;from chapter 6 - Design Patterns (Head First)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Đang có nhu cầu refactoring đoạn code trong cty và muốn dùng Command Pattern nên đọc lại nó một chút. Ngoài cuốn sách của GoF kinh điển thì cuốn Head First - Design Patterns của nhà xuất bản O'reilly khá hay, nó lấy ví dụ trong cuộc sống và cách trình bày làm cho người đọc cảm thấy ít khô khan hơn.&lt;br /&gt;&lt;br /&gt;Issue được đưa ra từ ví dụ sau: có một cái remote control có nhiều khe (slot), mỗi khe có thể được lập trình để điều khiển các thiết bị khác nhau. Có hai nút cho mỗi khe là On/Off, một nút Undo dùng chung cho tất cả các khe (xem hình)&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2244/1578948477_a70241faf5_o.png" alt="2007-10-15_220454" height="400" width="464" /&gt;&lt;br /&gt;&lt;br /&gt;Có nhiều thiết bị khác nhau, như quạt, đèn, TV, máy nước nóng (hottub)... Ta phải lập trình sau cho remote control có khả năng bật tắt trên các thiết bị đó, tùy vào ngữ cảnh user nạp thiết bị vào trong slot nào.&lt;br /&gt;&lt;br /&gt;Ý nghĩ đầu tiên về cách lập trình:&lt;br /&gt;&lt;pre&gt;if (slot1 == light) light.on();&lt;br /&gt;else if (slot1 == hottub) hottub.on();&lt;br /&gt;else....&lt;br /&gt;&lt;/pre&gt;--&gt; chuối bưởi quá :-D&lt;br /&gt;&lt;br /&gt;Có cách nào tách rời giữa remote control và các vendor class, nó không cần biết phải bật/tắt trên  class nào, chỉ đơn giản là execute một command, không quan tâm bên trong command đó làm việc gì (bản thân command tự biết). Đây gọi là decouple requester của một action và object thực hiện action đó.&lt;br /&gt;&lt;br /&gt;Command pattern thực hiện như sau:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tạo ra một &lt;span style="font-style: italic;"&gt;command object&lt;/span&gt; encapsulate một request làm một action nào đó (giống như turn on a light) và trên một đối tượng nào đó (vd trên đối tượng bóng đèn).&lt;/li&gt;&lt;li&gt;Store command object đó. Invoker khi có nhu cầu sẽ gọi execute trên command đó (giống như load các command trên mỗi button, khi user nhấn nút thì gọi command execute, không quan tâm đến nó làm cái gì)&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Implementation&lt;/span&gt;&lt;br /&gt;Tạo ra 1 interface chung Command&lt;br /&gt;&lt;pre&gt;public interface Command {&lt;br /&gt; public void execute(); // cu phap java :-D&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public class LightOnCommand implements Command {&lt;br /&gt;   Light light;&lt;br /&gt;   public LightOnCommand(Light light) {&lt;br /&gt;   this.light = light;&lt;br /&gt; }&lt;br /&gt; public void execute() {&lt;br /&gt;   light.on();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class TestRemoteControl {&lt;br /&gt; public void Test() {&lt;br /&gt;   RemoteControl remoteControl = new RemoteControl(); // class remoteControl tu viet&lt;br /&gt;   Light light = new Light();&lt;br /&gt;   LightOnCommand lightCommand = new LightOnCommand(light);&lt;br /&gt;&lt;br /&gt;   remoteControl.setCommandOnSlot(1, lightCommand); // set lightCommand on slot 1&lt;br /&gt;   remoteControl.pressButton(1); // simulate press button 1&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Trở lại đoạn code cần refactor ở cty, có khá nhiều use cases trong business flow, làm thế nào để code vừa áp dụng được pattern trên vừa thể hiện rõ business mà nó đang chạy nhất (theo như ý tưởng mà mình ngộ ra từ Craftman và bác Vĩ cũng có đề cập: code cũng chính là comment :-))&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2248/1579130695_c4a93e31f2_o.png" alt="2007-10-16_002956" height="153" width="553" /&gt;&lt;br /&gt;&lt;br /&gt;Review document ta thấy có 4 dạng business chính, wrapper lại thành 4 command chính là MoneyTransferCommand, MobileRechargeCommand...&lt;br /&gt;&lt;br /&gt;Các command này thực hiện action trên 2,3 đối tượng là YP, BP (banking Pichincha), Commerce (third party), ... (trong  command pattern, đối tượng này gọi là &lt;span style="font-weight: bold;"&gt;Receiver&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;Code được viết như sau:&lt;br /&gt;&lt;pre&gt;public class MoneyTransferCommand : Command&lt;br /&gt;{&lt;br /&gt; YP yp;&lt;br /&gt; BP bp;&lt;br /&gt; // .......&lt;br /&gt; public void Execute()&lt;br /&gt; {&lt;br /&gt;   transActionStatus = yp.ValidateTransaction(incoming);&lt;br /&gt;   swith(transactionStatus)&lt;br /&gt;   {&lt;br /&gt;     case SUCCESS:&lt;br /&gt;       bp.MoneyTransfer();&lt;br /&gt;       break;&lt;br /&gt;        //......&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;CommandFactory &lt;/span&gt;thực hiện việc tạo ra các command từ incoming rồi bỏ vào trong queue. Các thread sẽ thực hiện command trong queue&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2111/1579208867_3fb730f354_o.png" alt="2007-10-16_005025" height="404" width="424" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-4526612811973190073?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/4526612811973190073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=4526612811973190073' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4526612811973190073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4526612811973190073'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/command-pattern.html' title='Command Pattern'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-4814648546419743181</id><published>2007-10-14T16:11:00.000+07:00</published><updated>2007-12-04T00:19:09.623+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Developing Custom Controls that Raise Events (2)</title><content type='html'>&lt;span style="font-style: italic;"&gt;(from chapter 4 - Prof ASP.NET Server Controls)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Trong phần trước của chương 4, ta đã biết làm cách nào để raise 1 event cho client handle khi postback về server. Phần 2 chúng ta sẽ đề cập đến Postback Data.&lt;br /&gt;&lt;br /&gt;Ví dụ ta có 1 dropdownlist, khi user select 1 giá trị khác trong dropdownlist, ta phải có cách để biết được sự thay đổi này và raise 1 event (vd OnDropdownlistChanged)&lt;br /&gt;&lt;br /&gt;ASP.NET cung cấp &lt;span style="font-weight: bold;"&gt;IPostBackDataHandler &lt;/span&gt;interface để làm việc này, bao gồm 2 method:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;LostPostData&lt;/span&gt;(string postDataKey, NameValueCollection values): chứa giá trị của tất cả các value được postback theo dạng name/value, cho phép developer lấy được giá trị của các element trong form postback. Nếu hàm này trả về true có nghĩa là đã có sự thay đổi data, do đó hàm RaisePostDataChangedEvent bên dưới sẽ được gọi.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;RaisePostDataChangedEvent&lt;/span&gt;( ): developer implement để biết raise event.&lt;/li&gt;&lt;/ul&gt;VDropdownList được viết như sau:&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2222/1566765869_0c3e062f87_o.png" alt="2007-10-14_161434" height="331" width="524" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2203/1567649830_041ba84c7c_o.png" alt="2007-10-14_161451" height="528" width="674" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-4814648546419743181?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/4814648546419743181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=4814648546419743181' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4814648546419743181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/4814648546419743181'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/developing-custon-controls-that-raise_14.html' title='Developing Custom Controls that Raise Events (2)'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-958599443884724315</id><published>2007-10-14T15:55:00.000+07:00</published><updated>2007-12-04T00:19:09.623+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Developing Custom Controls that Raise Events</title><content type='html'>&lt;span style="font-style: italic;"&gt;(from chapter 4 - Prof ASP.NET Server Controls)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Chương này hướng dẫn cách thức develop một custom control có khả năng raise events, giống như sự kiện OnClick của asp:Button.&lt;br /&gt;&lt;br /&gt;Đầu chương là phần discuss về Event, EventDelegate, EventData. Ta thử viết một custom control &lt;span style="font-weight: bold;"&gt;VButton &lt;/span&gt;giống như asp:Button.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2324/1567592822_79dd19b3b7_o.png" alt="2007-10-14_160619" height="165" width="496" /&gt;&lt;br /&gt;&lt;br /&gt;Đầu tiên ta dự định tạo một EventData, tên là VButtonEventArgs, thừa kế từ EventArgs, nhưng do không có gì trong đó nên ta không tạo. Sau đó tạo delegate, event handler.&lt;br /&gt;&lt;br /&gt;public delegate void VButtonClickEventHandler (object sender, EventArgs e);&lt;br /&gt;....&lt;br /&gt;public event VButtonClickEventHandler vButtonClickEventHandler;&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;Vấn đề bây giờ là làm sao notify được event này cho dù client có đăng ký và postback về server đi nữa. Quay lại trường hợp click trên button Submit để postback về server của asp.net, khi đó containing page sẽ:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tìm kiếm xem html element nào thực hiện việc post back bằng cách duyệt các tag trong form khi submit.&lt;/li&gt;&lt;li&gt;Tìm kiếm trong control tree (thông qua name của element trong form đã thực hiện việc postback) để xem control đó có implement interface &lt;span style="font-weight: bold;"&gt;IPostBackEventHandler &lt;/span&gt;hay không. Nếu control đó có implement interface này, nó sẽ gọi hàm &lt;span style="font-weight: bold;"&gt;RaisePostBackEvent &lt;/span&gt;của control đó (đây là hàm của interface IPostBackEventHandler được control implement).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Do đó có 2 nguyên nhân khiến cho VButton không notify được sự kiện là:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Attribute name không phải là &lt;span style="font-weight: bold;"&gt;UniqueID &lt;/span&gt;của control, do đó containing page dựa vào đó sẽ không tìm ra được control nào thực hiện việc postback.&lt;/li&gt;&lt;li&gt;VButton không implement interface IPostBackEventHandler&lt;/li&gt;&lt;/ul&gt;Code được sửa lại như sau:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2202/1567593226_3cc5bd73d7_o.png" alt="2007-10-14_160637" height="419" width="535" /&gt;&lt;br /&gt;&lt;br /&gt;Bây giờ button name và UniqueID trùng nhau, và control đã implement interface &lt;span style="font-weight: bold;"&gt;RaisePostBackEvent &lt;/span&gt;nên developer đã có thể đăng ký sự kiện và raise event khi button này được click (postback về server)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-958599443884724315?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/958599443884724315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=958599443884724315' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/958599443884724315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/958599443884724315'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/developing-custon-controls-that-raise.html' title='Developing Custom Controls that Raise Events'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6720666790734785380</id><published>2007-10-14T15:44:00.000+07:00</published><updated>2007-11-12T23:40:36.871+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Martin C.Robert'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile Principles: Patterns and Practice in C#'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Overview of Extreme Programming</title><content type='html'>&lt;span style="font-style: italic;font-family:arial;" &gt;(from chapter 2 - Agile Principles, Patterns, and Practices in C# - Martin C.Robert)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6720666790734785380?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6720666790734785380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6720666790734785380' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6720666790734785380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6720666790734785380'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/overview-of-extreme-programming.html' title='Overview of Extreme Programming'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-2203149341582585769</id><published>2007-10-14T15:40:00.000+07:00</published><updated>2008-04-08T22:40:56.990+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile Principles: Patterns and Practice in C#'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Agile Practices</title><content type='html'>&lt;span style="font-size:100%;"&gt;&lt;span style="font-style: italic;"&gt;from chapter 1 - Agile Principles, Patterns and Practices in C# - Martin C.Robert&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Chương này nói về Manifesto của Agile Alliance&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Individuals and interactions over processes and tools&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Working software over comprehensive documentation&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Customer collaboration over contract negotiation&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Responding to change over following a plan&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;1/ &lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;Individuals and interactions over processes and tools&lt;/span&gt;&lt;br /&gt;Con người là nhân tố quan trọng nhất trong việc thành công. Một process tốt không cứu được project khỏi thất bại nếu team không có member giỏi (strong player), và process không tốt sẽ làm cho member làm việc không hiệu quả. Ngay cả một group các member giỏi vẫn có thể bị failed nếu họ không làm việc như là một nhóm.&lt;br /&gt;&lt;br /&gt;Một member giỏi không nhất thiết phải là một người có nhiều kỹ năng. Có thể chỉ là một programmer trung bình nhưng biết cách làm việc tốt với các member khác. Khả năng communication và interaction quan trọng hơn nhiều so với một tài năng lập trình nhưng mới vào nghề. Một team với các lập trình viên trung bình nhưng giao tiếp tốt sẽ thành công hơn một team bao gồm các ngôi sao nhưng không hoạt động như 1 nhóm.&lt;br /&gt;&lt;br /&gt;Sử dụng đúng tool rất quan trọng trong việc thành công. Compiler, IDEs, source control system ... đều rất cần cho nhóm developers. Tuy nhiên tool có thể bị xem trọng quá mức. Sử dụng thừa thải, tràn lan các tool cũng gây ra hậu quả xấu tương tự như thiếu các tool vậy.&lt;br /&gt;&lt;br /&gt;Lời khuyên là hãy bắt đầu với ít công cụ thôi. Đừng cho rằng bạn phải mở rộng các công cụ nếu như bạn không sử dụng nó và tìm thấy rằng nó cần phải thay đổi. Thay vì mua một đống các source control system mắc tiền, hãy tìm một hệ thống miễn phí và sử dụng nó đến khi bạn cảm thấy phải phát triển hơn nữa.  Thay vì mua license cho CASE tools, bạn hãy sử dụng bảng trắng và graph paper cho đến khi bạn thấy phải cần nhiều hơn nữa.&lt;br /&gt;&lt;br /&gt;Cần phải nhớ rằng, xây dựng một team quan trọng hơn nhiều xây dựng môi trường phát triển. Nhiều team và manager đã sai lầm trong việc xây dựng môi trương trước và mong chờ sẽ tự động có được một team. Thay vào đó, hãy làm việc để tạo ra team trước, sau đó để team configure môi trường làm việc dựa trên các yêu cầu cơ bản.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2/ &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;Working software over comprehensive documentation&lt;/span&gt;&lt;br /&gt;Làm phần mềm mà không có documentation là một tai họa. Code không phải là một phương tiện lý tưởng cho việc giao tiếp cơ bản và xây dựng cấu trúc của hệ thống. Ngoài ra, team còn cần phải xuất ra các tài liệu mà con người đọc được để miêu tả hệ thống và ý tưởng cơ bản cho việc design.&lt;br /&gt;&lt;br /&gt;Tuy nhiên, quá nhiều document cũng tai hại như là quá ít. Một đống các software document sẽ tốn một đống thời gian để viết và ngay cả thời gian để làm cho nó đồng bộ với code.&lt;br /&gt;&lt;br /&gt;Ý tưởng tốt là viết và maitain document trong một cấu trúc cơ bản nổi bật, ngắn. Ngắn có nghĩa là tối đa chỉ một hoặc hai tá giấy (dozen pages). Nổi bật (salient) có nghĩa là nó nên mô tả toàn bộ thiết kế một cách cơ bản và chỉ là cấu trúc ở mức cao nhất trong hệ thống.&lt;br /&gt;&lt;br /&gt;Làm cách nào để trainining một member mới về hệ thống. Chúng ta làm việc gần gũi với họ. Transfer kiến thức bằng cách ngồi kế bên và giúp họ. Chúng ta làm cho họ trở thành một phần của team thông qua các giao tiếp và training gần gũi đó.&lt;br /&gt;&lt;br /&gt;Hai tài liệu tốt nhất chính là team và code. Code bản thân nó không thể nói dối. Có thể khó khăn trong việc tìm hiểu nhân tố cơ bản và mục đích của đoạn code, nhưng bởi vì nó không phải là nguồn thông tin. Team sẽ nắm giữ các thông tin về thay đổi, roadmap của hệ thống trong đầu các member và cách nhanh nhất và hiệu quả nhất là đem nó ra giấy và transfer nó thông qua human-to-human interaction.&lt;br /&gt;&lt;br /&gt;Có nhiều team khá tốn thời gian trong việc document thay vì làm phần mềm. Có một luật đơn giản ngăn chặn việc đó:&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-style: italic;"&gt;Produce no document unless its need is immediate and significant.&lt;/span&gt;&lt;br /&gt;(martin's first law of documentation)&lt;br /&gt;&lt;/blockquote&gt;&lt;span style="font-weight: bold;"&gt;3/ &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;Customer collaboration over contract negotiation&lt;/span&gt;&lt;br /&gt;Software không thể được ordered như món hàng. Bạn không thể viết một mô tả cho phần mềm bạn muốn và sau đó nhờ một ai đó phát triển nó theo một schedule cố định và theo một giá cố định được. Ngày qua ngày, mọi cố gắng phát triển dự án theo cách này đã thất bại.&lt;br /&gt;&lt;br /&gt;Các manager trong công ty rất mong muốn khi nói với đội ngũ phát triển những gì họ cần và sau đó mong chờ nhân viên chạy đi đâu đó trong một lúc rồi đem về một hệ thống thỏa mãn các nhu cầu đó. Nhưng cách thức hoạt động này thường dẫn đến chất lượng thấp và thất bại.&lt;br /&gt;&lt;br /&gt;Những dự án thành công đòi hỏi sự feedback của khách hàng thường xuyên và đều đặn hơn là phụ thuộc vào contract, hoặc cam kết làm việc nào đó, khách hàng của phần mềm thường làm việc gần gũi với development team, cung cấp các feedback thường xuyên trên những cố gắng của team.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4/ &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;Responding to change over following a plan&lt;/span&gt;&lt;br /&gt;Khả năng đáp ứng thay đổi thường quyết định khả năng thành công hay thất bại của dự án. Khi xây dựng các kế hoạch, chúng ta cần phải đảm bảo được sự linh động và đáp ứng được các thay đổi về mặt business và công nghệ.&lt;br /&gt;&lt;br /&gt;Tiến trình của dự án không thể được plan quá xa trong tương lai. Lý do đầu tiên là do môi trường kinh doanh thường thay đổi, làm cho các yêu cầu phải đổi theo. Kế đến là một khi hệ thống bắt đầu chạy, khách hàng thường có khuynh hướng thay đổi requirement. Cuối cùng ngay cả chúng ta nếu biết rằng yêu cầu là gì và chắc rằng nó không đổi, thì chúng ta cũng không ước tính được chính xác hoàn toàn thời gian cần thiết để develop là bao lâu.&lt;br /&gt;&lt;br /&gt;Các manager mới tập sự thường có sở thích và tham vọng là tạo và ghi nhận một cái PERT hoặc Gantt chart cho toàn bộ project. Họ cảm thấy rằng sơ đồ này cho họ khả năng control trên toàn bộ dự án. Họ có thể track được các task của từng cá nhân, đánh dấu trên chart khi nó hoàn tất. Họ có thể so sánh được giữa ngày thật sự và ngay planning trên chart và đáp ứng sự khác nhau đó.&lt;br /&gt;&lt;br /&gt;Nhưng những gì thật sự xảy ra là cấu trúc của chart ngày càng bị giảm sút (degrade - giảm giá trị). Khi team có được kiến thức về toàn bộ hệ thống và khách hàng biết được về những nhu cầu của team thì một số task nào đó trên chart trở nên không còn cần thiết. Một số task khác sẽ được thêm vào. Tóm lại, plan sẽ bị thay đổi về hình thức, không còn được cập nhật (in dates).&lt;br /&gt;&lt;br /&gt;Một chiến thuật planning tốt hơn là chỉ làm plan chi tiết cho tuần sau, các plan cơ bản cho 3 tháng tới và để các plan thô thiển khác cho thời gian sau nữa (crude plans). Chúng ta chỉ nên biết rằng các task riêng lẽ nào sẽ được làm việc trong tuần sau. Hoặc các requirement cơ bản mà chúng ta sẽ làm việc trong 3 tháng tới. Và chi có 1 ý tưởng mơ hồ (vague idea) về những gì mà hệ thống sẽ làm sau 1 năm.&lt;br /&gt;&lt;br /&gt;Việc làm giảm tính ràng buộc (resolution) của kế hoạch có nghĩa là chúng ta chỉ thực hiện kiểm tra một cách chi tiết cho những task ngay trước mắt. Một khi plan chi tiết được thiết lập, rất khó để thay đổi, vì team đang có nhiều quán tính và cam kết trong công việc. Tuy nhiên phần còn lại của plan sẽ vẫn được linh động vì chúng ta chỉ có plan chi tiết trong 1 tuần.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-2203149341582585769?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/2203149341582585769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=2203149341582585769' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2203149341582585769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2203149341582585769'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/agile-practices.html' title='Agile Practices'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-6403174586786958262</id><published>2007-10-07T00:42:00.003+07:00</published><updated>2007-12-04T00:19:09.623+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Developing Custom Composite Controls</title><content type='html'>&lt;span style="font-style: italic;"&gt;&lt;span style="font-size:100%;"&gt;(from Prof ASP.NET Server Controls - Wrox - chapter 5)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Composite control tận dụng các control có sẵn để tạo control mới. Qua 3 chương đầu ta thấy chi phí cho việc tạo ra các thẻ html, dùng viewstate, hoặc handle event là khá nhiều và lặp đi lặp lại trong khi các control cơ bản đã có sẵn.&lt;br /&gt;&lt;br /&gt;Chương này tác giả viết khá dài, nhưng không hay và không đi vào trọng tâm của vấn đề. Reference tiếp trong &lt;a href="http://msdn2.microsoft.com/en-us/library/aa479016.aspx"&gt;Build Composite Controls&lt;/a&gt; trong msdn giúp ta hiểu rõ hơn nhiều.&lt;br /&gt;&lt;br /&gt;Cách thức tạo composite control như sau:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Inherit base class CompositeControl (optional). Tuy không nhất thiết phải thừa kế lại lớp này nhưng ta nên làm vì nó hỗ trợ khi sử dụng control ở design view (recommend from M$ :-D )&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Override method &lt;span style="font-weight: bold;"&gt;CreateChildControls &lt;/span&gt;của base class Control, khởi tạo và add các control vào trong Controls Collection của class cha.&lt;/li&gt;&lt;li&gt;Override các property và method khác (optional) như TagKey, CreateControlStyle, RenderContents...&lt;/li&gt;&lt;/ul&gt;Xem xét một ví dụ tạo 1 composite control bao gồm 1 label, 1 text, 1 submit button như sau:&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2087/1567262898_1d93bd7cb5_o.png" alt="2007-10-14_151747" height="334" width="467" /&gt;&lt;br /&gt;&lt;br /&gt;Ta gọi hàm Controls.Clear() đầu tiên để đảm bảo không control nào được add nhiều lần, cuối cùng ta bật property ChildControlsCreated =  true để thông báo các control đã được create xong.&lt;br /&gt;&lt;br /&gt;Property của composite control được public trực tiếp thông qua property của child control như sau:&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2074/1567262552_195397458d_o.png" alt="2007-10-14_151811" height="302" width="370" /&gt;&lt;br /&gt;&lt;br /&gt;Hàm EnsureChildControls được gọi trước để đảm bảo tất cả các control đã được khởi tạo xong.&lt;br /&gt;Html render:&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2185/1565638097_c21450109a_o.png" alt="2007-10-14_132949" height="93" width="811" /&gt;&lt;br /&gt;&lt;span&gt;&lt;br /&gt;Nếu muốn control được chứa trong 1 table, label, input text và button nằm trong cùng 1 row và ở 3 column của table đó thì ta có thể làm như sau:&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;Override lại TagKey, trả ra tag Table để cho container của control là table&lt;/li&gt;&lt;li&gt;Add label, textBox, button vào trong control TableCell trước khi add vào Controls collection của class cha (trong hàm CreateChildControls. (nhớ đầu tiên phải đưa tất cả nằm trong một table Row trước)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Event Bubbling&lt;/span&gt;&lt;br /&gt;Làm cách nào để composite control bắt được sự kiện của các control con và raise tiếp sự kiện của nó lên control cha?&lt;br /&gt;&lt;br /&gt;Để raise sự kiện lên control cha:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Composite control phải public một event để cho phép control sử dụng đăng ký (thông thường là CommandEventHandler)&lt;/li&gt;&lt;li&gt;Implement method &lt;span style="font-weight: bold;"&gt;RaisePostBackEvent &lt;/span&gt;trong  interface &lt;span style="font-weight: bold;"&gt;IPostBackEventHandler &lt;/span&gt;để gọi các event handler đã đăng ký, cuối cùng gọi hàm &lt;span style="font-weight: bold;"&gt;RaiseBubbleEvent &lt;/span&gt;để bubble events ra ngoài.&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://farm3.static.flickr.com/2415/1566076355_e5056826fb_o.png" alt="2007-10-14_143258" height="414" width="588" /&gt;&lt;br /&gt;&lt;br /&gt;Để catch các event của control con:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Composite control phải override lại method &lt;span style="font-weight: bold;"&gt;OnBubbleEvent &lt;/span&gt;để catch Command event được raised và bubble từ control con.&lt;/li&gt;&lt;li&gt;Để biết chính xác control nào raise event, ta phải kiểm tra command name trong hàm này.&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://farm3.static.flickr.com/2253/1566076575_a06a50ce0c_o.png" alt="2007-10-14_143344" height="387" width="652" /&gt;&lt;br /&gt;(chú ý là khi add control button trong hàm CreateChildControls, nhớ là gán thêm command name cho nó là buttonSubmit như đoạn code ở trên)&lt;br /&gt;&lt;br /&gt;Vấn đề sau cùng là control con được tạo khi nào trong control tree. Lần đầu tiên, CreateChildControls sẽ được gọi trong pre-rendering phase, sau khi postback về server lần thứ 2 trở đi, nó được gọi trong giai đoạn processing posted data (BeginProcessPostData). Nguyên nhân là trong giai đoạn process posted data, asp.net sẽ tìm các control nào có ID match trong control tree (thông qua hàm FindControl). Trong method &lt;span style="font-weight: bold;"&gt;FindControl&lt;/span&gt;, trước khi trả về control phải đảm bảo cây control phải được build xong (EnsureChildControls) nên nó được build trong giai đoạn đó.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-6403174586786958262?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/6403174586786958262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=6403174586786958262' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6403174586786958262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/6403174586786958262'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/prof-aspnet-server-control-wrox-chapter_06.html' title='Developing Custom Composite Controls'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-2201066005543404713</id><published>2007-10-07T00:42:00.001+07:00</published><updated>2007-12-04T00:19:09.624+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Developing Custom-Styled Controls</title><content type='html'>&lt;p style="font-style: italic;" class="MsoNormal"&gt;(from chapter 3 - Prof ASP.NET Server Controls)&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Chương này miêu tả các methods và properties của WebControl base class và hướng dẫn cách thức để implement một custom control. Sau đó là cách implement của một custom Style class và cách sử dụng của một custom control với nó.&lt;/p&gt;    &lt;p class="MsoNormal"&gt;WebControl kế thừa từ Control, sử dụng thêm một instance class Style của ASP.NET để hỗ trợ các property style tốt hơn. Ví dụ:&lt;/p&gt;        &lt;p class="MsoNormal"&gt;public class VcustomStyledTextBox : &lt;span style="font-weight: bold;"&gt;WebControl&lt;/span&gt;&lt;br /&gt;{&lt;/p&gt;    &lt;p class="MsoNormal" style="text-indent: 0.2in;"&gt;&lt;span style=""&gt;&lt;/span&gt;protected override void Render(HtmlTextWriter writer)&lt;span style=""&gt;  &lt;/span&gt;{ .... }&lt;/p&gt;  &lt;p class="MsoNormal"&gt;}&lt;/p&gt;    &lt;p class="MsoNormal"&gt;So với Control, WebControl tách bạch hơn trong việc Render(..) vì Control không quan tâm gì đến containing HTML element. WebControl delegate ra các method khác nhau để developer override như:&lt;/p&gt;        &lt;ul&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;&lt;span style="font-family:Wingdings;"&gt;&lt;span style=""&gt;&lt;span style=""&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;AddAttributeToRender: gắn các attribute của containing element.&lt;/li&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;&lt;span style="font-family:Wingdings;"&gt;&lt;span style=""&gt;&lt;span style=""&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;RenderBeginTag: render opening tag của containing element.&lt;!--[if !supportLists]--&gt;&lt;span style="font-family:Wingdings;"&gt;&lt;span style=""&gt;&lt;span style=""&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;RenderContent: render html content giữa open tag và close tag của containing element.&lt;/li&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;&lt;span style="font-family:Wingdings;"&gt;&lt;span style=""&gt;&lt;span style=""&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;RenderEndTag: render end tag&lt;/li&gt;&lt;/ul&gt;    &lt;p class="MsoNormal"&gt;Containing element là gì? Ví dụ control của ta render ra HTLM bao gồm label và textbox trong 1 table như sau:&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;img src="http://farm3.static.flickr.com/2078/1504213930_064d1db607_o.png" alt="2007-10-07_152157" height="107" width="376" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;o:p&gt;&lt;/o:p&gt;thì tag table là containing element của control, và property của nó (border) được xem là attribute của containg element.  &lt;p class="MsoNormal"&gt;WebControl default sẽ implement hàm Render() như sau:&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;img src="http://farm3.static.flickr.com/2256/1503356417_bd456cf46f_o.png" alt="2007-10-07_153313" height="103" width="322" /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Do đó ta không nhất thiết phải override lại toàn bộ hàm Render(..) của WebControl, mà chỉ cần override hàm RenderContent() để render nội dung bên trong, và RenderBeginTag() để render containing element.&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Bên trong hàm RenderBeginTag(…), WebControl thật ra tìm kiếm thẻ HTML được khai báo trong TagKey property để render, nên ta cũng không cần override hàm RenderBeginTag mà chỉ cần override property TagKey là đủ. Ví dụ:&lt;/p&gt;          &lt;p class="MsoNormal"&gt;protected virtual HtmlTextWriterTag &lt;span style="font-weight: bold;"&gt;TagKey&lt;/span&gt;&lt;br /&gt;{  get {return HtmlTextWriterTag.Table;}  }&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;Về attribute của containing element, hàm AddAttributeToRender thực hiện 2 việc cơ bản sau:&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;&lt;span style="font-family:Wingdings;"&gt;&lt;span style=""&gt;&lt;span style=""&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Tự động thêm vào các non-styled attribute vào. Non-styled attribute bao gồm: ClientID, AccessKey, Enabled, Tabindex, and ToolTip.&lt;/li&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;&lt;span style="font-family:Wingdings;"&gt;&lt;span style=""&gt;&lt;span style=""&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Thêm vào một số attribute khác, định nghĩa trong collection attribute của ControlStyle&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://farm3.static.flickr.com/2284/1503356643_7190f964b9_o.png" alt="2007-10-07_153327" height="182" width="347" /&gt;&lt;br /&gt;&lt;p class="MsoNormal"&gt;Quan sát trong collection này, ta thấy nó gọi hàm CreateControlStyle(..) nên ta chỉ cần override hàm này là đủ. Ví dụ:&lt;/p&gt;          &lt;p class="MsoNormal"&gt;protected override Style &lt;span style="font-weight: bold;"&gt;CreateControlStyle&lt;/span&gt;()&lt;br /&gt;{&lt;br /&gt;return new &lt;span style="font-weight: bold;"&gt;TableStyle&lt;/span&gt;(this.ViewState);&lt;br /&gt;}&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Do containing element của ta trong vd này là tag table nên ta return 1 instance TableStyle. Chú ý các instance này phải share cùng một ViewState của control.&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Cuối chương này tác giả còn hướng dẫn cách tạo ra một custom style class (style do developer định nghĩa). Cái này ít dùng và đơn giản nên không cần phải đề cập nhiều.&lt;/p&gt;Class VTextBoxCustomStyled cuối cùng được viết như sau:&lt;br /&gt;&lt;img src="http://farm3.static.flickr.com/2012/1503418063_3f6cfab9bf_o.png" alt="2007-10-07_154426" height="492" width="571" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-2201066005543404713?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/2201066005543404713/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=2201066005543404713' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2201066005543404713'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/2201066005543404713'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/prof-aspnet-server-control-wrox-chapter.html' title='Developing Custom-Styled Controls'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-8028930185013722348</id><published>2007-10-03T15:05:00.000+07:00</published><updated>2007-12-04T00:19:09.624+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='Prof Asp.Net Server Control'/><title type='text'>Developing Simple Custom Controls and User Controls</title><content type='html'>&lt;p style="font-style: italic;" class="MsoNormal"&gt;(from chapter 2 - Prof ASP.NET Server Controls)&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Chương này giới thiệu cách implement cơ bản của custom control và user control (giống như vd Hello World trong chương đầu tiên của mỗi cuốn sách).&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Để viết một custom control, ta phải thừa kế từ base class Control và override method Render()&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Ví dụ giả sử viết lại 1 custom control giống asp:TextBox của MS.&lt;/p&gt;&lt;img src="http://farm3.static.flickr.com/2118/1504036884_5cc572e670_o.png" alt="2007-10-07_145926" height="562" width="682" /&gt;&lt;br /&gt;&lt;p class="MsoNormal"&gt;Ở thời điểm runtime, containing page sẽ tạo ra 1 instance của HtmlTextWriter và truyền instance này cho các control con (recursive) để mỗi control thực hiện việc Render()&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Việc khai báo property cho phép page developer customize của thuộc tính cần thiết của control. Các cách gắn property vào control lúc design time:&lt;/p&gt;    &lt;ul&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;&lt;span style=";font-family:Wingdings;font-size:9;"  &gt;&lt;span style=""&gt;&lt;span style=""&gt;       &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;!--[endif]--&gt;Property level attribute: [BrowsableAttribute], [DescriptionAttribute], [DefaultValueAttribute], [CategoryAttribute] (edit property trong toolbox property-design).&lt;/li&gt;&lt;li&gt;&lt;!--[endif]--&gt;Class level attribute: cho phép set các default value cho property khi kéo thả control vào web&lt;br /&gt;[ToolboxData("&lt;{0}:VTextBox textValue='vinh test'&gt;&lt;!--{0}:VTextBox--&gt;")]&lt;br /&gt;public class VTextBox : Control { … }&lt;!--[endif]--&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Assembly level attribute: cho phép thay đổi tagPrefix default của control khi kéo thả&lt;/li&gt;&lt;/ul&gt;    &lt;p class="MsoNormal"&gt;Phần thú vị nhất của chương này là State Management, để cho control lưu được trạng thái sau mỗi lần postback, ta nên lưu giữ các giá trị được set vào trong ViewState.&lt;/p&gt;Mọi việc đều ok giống như hướng dẫn trong sách, tuy nhiên nếu tò mò liên tưởng đến asp:TextBox của M$, nếu bạn disable ViewState của asp:TextBox thì sau mỗi lần postback, nó vẫn load lại được giá trị như thường!!!. Điều đó gợi cho chúng ta câu hỏi:    &lt;ul&gt;&lt;li&gt;&lt;!--[if !supportLists]--&gt;Ta có thể có dùng một cách khác không phải ViewState để thực hiện việc này (giống như asp:TextBox)&lt;/li&gt;&lt;li&gt;Vậy ViewState thật sự được dùng khi nào&lt;/li&gt;&lt;/ul&gt;  &lt;p class="MsoNormal"&gt;Hai câu trả lời này có trong article Understanding ASP.NET ViewState (&lt;a href="http://msdn2.microsoft.com/en-us/library/ms972976.aspx"&gt;http://msdn2.microsoft.com/en-us/library/ms972976.aspx&lt;/a&gt;)&lt;span style=""&gt;  &lt;/span&gt;ViewState dùng để lưu lại trạng thái của các lần postbacks. Và lý do mà asp:TextBox có thể load lại value khi disable ViewState là do nó implement 1 interface IPostBackDataHandler, dùng để load data khi nó được post back về server.&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Giai đoạn Load Postback Data của một Page life cycle là giai đoạn thứ 3, (sau Initialize và Load View State), chỉ được gọi khi page được post back. Page class sẽ tìm các server control được post back, sau đó kiếm tra xem control nào implement interface IPostBackDataHandler để gọi hàm LoadPostData( ) của control đó. Asp:TextBox không quan tâm đến giá trị đã lưu trước đó, mà chỉ cần lấy giá trị mới nhất lúc post back, set trở lại vào trong control.&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Điều này không có nghĩa là ta không cần viewstate và chỉ cần implement interface này là đủ. Tùy trường hợp ví dụ như asp:DropdownBox, nếu ta không lưu lại giá trị trước đó và so với giá trị mới khi post back thì không thể nào biết được sự kiện OnDropdownlistChange được.&lt;/p&gt;    &lt;p class="MsoNormal"&gt;Phần cuối chươngchương nói về cách implement một user control. Nó cũng đơn giản như viết 1 trang web (trừ ext .ascx, không có thẻ html, body, form) nên cũng không có gì để nói :-) &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-8028930185013722348?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/8028930185013722348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=8028930185013722348' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/8028930185013722348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/8028930185013722348'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/10/aspnet-server-control-wrox-chapter-2.html' title='Developing Simple Custom Controls and User Controls'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170860146330122341.post-1862346457733044442</id><published>2007-09-29T17:00:00.001+07:00</published><updated>2007-10-03T15:03:21.326+07:00</updated><title type='text'>Lời mở đầu</title><content type='html'>Ý tưởng này xuất phát từ cái blog của &lt;a href="http://www.davidhayden.com/davidhayden/default.aspx"&gt;David Hayden&lt;/a&gt;, thấy phần book review của ổng hay, đơn giản chỉ là comment lại những gì mình đã đọc.&lt;br /&gt;&lt;br /&gt;Còn cái blog title thì chôm từ blog của &lt;a href="http://www.joelonsoftware.com/"&gt;&lt;span style="font-style: italic;"&gt;Joe on Software&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Để xem cuốn nào cần comment trước đây :-D, các cuốn mình đang review là:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Hibernate in Action.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Lucene in Action.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ASP.NET 2005 Server Control and Development.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Agile Principles and Practices (C.Robert Martin)&lt;/li&gt;&lt;/ul&gt;Nói chung cuốn nào cũng hay và cực kỳ căn bản, đọc lại lần nào cũng có cái hay riêng :-D&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170860146330122341-1862346457733044442?l=dzinhontech.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dzinhontech.blogspot.com/feeds/1862346457733044442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1170860146330122341&amp;postID=1862346457733044442' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1862346457733044442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170860146330122341/posts/default/1862346457733044442'/><link rel='alternate' type='text/html' href='http://dzinhontech.blogspot.com/2007/09/li-m-u_29.html' title='Lời mở đầu'/><author><name>Nguyễn Thế Vinh</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_fLnGNScTh18/SfawYc_od4I/AAAAAAAACCU/lN0qX8GKzt0/S220/myAvatar.jpg'/></author><thr:total>0</thr:total></entry></feed>
