
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>DevGuli</title>
	<atom:link href="http://www.devguli.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.devguli.com/blog</link>
	<description>Devguli -- coding for a living</description>
	<pubDate>Mon, 03 Nov 2008 15:09:35 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.1</generator>
	<language>en</language>
			<item>
		<title>วางปืนคืนเมือง</title>
		<link>http://www.devguli.com/blog/thai/put_down_the_gun_and_return_home/</link>
		<comments>http://www.devguli.com/blog/thai/put_down_the_gun_and_return_home/#comments</comments>
		<pubDate>Tue, 07 Oct 2008 19:08:21 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[thai]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=83</guid>
		<description><![CDATA[วันนี้เห็นภาพคนไทยฆ่ากันเอง แล้วก็ได้แต่สังเวชใจ นึกไปถึงคุณ เกษียร เตชะพีระ เขียนถึงเหตุที่เขาวางปืนคืนเมือง ในยุคคอมมิวนิสต์
There is no abstraction in the world that is worth taking other people&#8221;s lives for.

ไม่มีหลักการนามธรรมอันใดในโลกมีค่าพอให้เราไปเอาชีวิตผู้อื่นมาสังเวย
ไม่ว่าสังคมนิยม-คอมมิวนิสต์
หรือประชาธิปไตย! 

]]></description>
			<content:encoded><![CDATA[<p>วันนี้เห็นภาพคนไทยฆ่ากันเอง แล้วก็ได้แต่สังเวชใจ นึกไปถึงคุณ เกษียร เตชะพีระ เขียนถึงเหตุที่เขาวางปืนคืนเมือง ในยุคคอมมิวนิสต์</p>
<blockquote><p>There is no abstraction in the world that is worth taking other people&#8221;s lives for.</p>
</blockquote>
<blockquote><p>ไม่มีหลักการนามธรรมอันใดในโลกมีค่าพอให้เราไปเอาชีวิตผู้อื่นมาสังเวย<br />
ไม่ว่าสังคมนิยม-คอมมิวนิสต์<br />
หรือประชาธิปไตย! </p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.devguli.com/blog/thai/put_down_the_gun_and_return_home/feed/</wfw:commentRss>
		</item>
		<item>
		<title>ผมรู้แล้วว่าทำไมผู้ชายถึงเลว</title>
		<link>http://www.devguli.com/blog/thai/why_man_is_bad/</link>
		<comments>http://www.devguli.com/blog/thai/why_man_is_bad/#comments</comments>
		<pubDate>Mon, 15 Sep 2008 16:35:34 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[thai]]></category>

		<category><![CDATA[thought]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=78</guid>
		<description><![CDATA[คุณคงจะได้ยินผู้หญิงพูดว่า &#8220;ผู้ชายมันก็เลวกันทั้งโลก&#8221; แนวความคิดนี้เป็นทียอมรับกันแพร่หลายมากในปัจจุบัน การสำรวจเบื้องต้นพบว่า กลุ่มผู้หญิงที่ผ่านการอกหักมาแล้ว ไม่ต่ำกว่าสามครั้งจะมีแนวโน้มคล้อยตามแนวความคิดนี้ มากเป็นพิเศษ จนถึงทุกวันนี้ก็ยังไม่มีสถาบันวิจัยใด ทำการค้นคว้าทดลองอย่างจริงจัง พอที่จะสรุปได้ว่าสมมุติฐานนี้เป็นจริงหรือไม่ เข้าใจว่าเป็นเพราะการสุ่มตัวอย่างผู้ชายทั้งโลกนั้น ต้องใช้งบประมาณมหาศาล อีกทั้งในปัจจุบัน กระบวนการตรวจสอบ ว่ากลุ่มตัวอย่างเป็น &#8220;ชายแท้&#8221; หรือไม่นั้นก็ยังมีค่าความคลาดเคลื่อนมากเหลือเกิน หากว่าแนวสมมุติฐานนี้เป็นจริง ผมขอเสนอหนึ่งทฤษฎีที่อาจจะสามารถนำมาอธิบายได้ว่า ทำไมผู้ชายสมัยนี้ถึงเลว
ย้อนกลับไปในสมัยที่ผู้ชายดีๆ ยังมีอยู่มากบนพื้นโลก ผู้ชายเหล่านี้ล้วนแต่เป็นสุภาพบุรุษ ผู้ดำรงตนอยู่ในคลองธรรมอันควร และไม่เคยได้นำตัวเองไปสุ่มเสี่ยง ต่อสิ่งอบายทั้งหลายแต่อย่างใด แต่ทั้งๆที่ประกอบไปด้วยคุณสมบัติที่ดีเหล่านั้น ผู้ชายเหล่านี้กลับไม่ได้รับความสนใจ จากผู้หญิงเท่าที่ควร ที่รักดีนั้นถูกมองเป็นน่าเบื่อ ที่มั่นคงนั้นถูกเห็นเป็นจืดชืด ผู้หญิงกลับลุ่มหลงในคุณสมบัติด้านตรงข้าม กับที่พวกเขามีโดยสิ้นเชิง จนถึงขนาดมีคำกล่าวว่า &#8220;ผู้หญิงมักชอบผู้ชายเลว&#8221; เหตุการณ์เป็นเช่นนี้มานับร้อยๆปี จากทฤษฎีการอยู่รอดของ Charles Darwin ที่กล่าวไว้ในทฤษฎีการคัดสรรตามธรรมชาติว่า คุณสมบัตที่ดี่ที่เอื้ออำนวยต่อการอยู่รอดนั้น จะถูกถ่ายทอดต่อไปยังลูกหลาน แต่คุณสมบัติที่ไม่เอื้ออำนวย ต่อการดำรงอยู่ของเผ่าพันธุ์นั้นจะลดน้อยลง และหาได้ยากขึ้นเรื่อยๆในรุ่นต่อๆไป ตลอดระยะเวลาร้อยๆปีนี้ พันธุกรรมของผู้ชาย ได้ปรับปรุงเปลี่ยนแปลงมาเรื่อยๆ ยีนส์ที่ส่งผลต่อพฤติกรรมเลวๆ ได้มีจำนวนมากขึ้นเรื่อยๆ เพื่อสงผลต่อความดึงดูดใจจากผู้หญิงนั่นเอง
นักวิทยาศาสตร์บางท่านตั้งข้อสังเกตุว่า อัตราการเพิ่มของยีนส์เลวในผู้ชายนั้น เพิ่มขึ้นทุกปีจนอาจจะอยู่ในระดับ &#8220;เลวกว่าหมา&#8221; ก็เป็นได้ แม้จะมีผู้หญิงที่ยังมีความสนใจในผู้ชายดีอยู่บ้าง [...]]]></description>
			<content:encoded><![CDATA[<p>คุณคงจะได้ยินผู้หญิงพูดว่า &#8220;<strong>ผู้ชายมันก็เลวกันทั้งโลก</strong>&#8221; แนวความคิดนี้เป็นทียอมรับกันแพร่หลายมากในปัจจุบัน การสำรวจเบื้องต้นพบว่า กลุ่มผู้หญิงที่ผ่านการอกหักมาแล้ว ไม่ต่ำกว่าสามครั้งจะมีแนวโน้มคล้อยตามแนวความคิดนี้ มากเป็นพิเศษ จนถึงทุกวันนี้ก็ยังไม่มีสถาบันวิจัยใด ทำการค้นคว้าทดลองอย่างจริงจัง พอที่จะสรุปได้ว่าสมมุติฐานนี้เป็นจริงหรือไม่ เข้าใจว่าเป็นเพราะการสุ่มตัวอย่างผู้ชายทั้งโลกนั้น ต้องใช้งบประมาณมหาศาล อีกทั้งในปัจจุบัน กระบวนการตรวจสอบ ว่ากลุ่มตัวอย่างเป็น &#8220;ชายแท้&#8221; หรือไม่นั้นก็ยังมีค่าความคลาดเคลื่อนมากเหลือเกิน หากว่าแนวสมมุติฐานนี้เป็นจริง ผมขอเสนอหนึ่งทฤษฎีที่อาจจะสามารถนำมาอธิบายได้ว่า ทำไมผู้ชายสมัยนี้ถึงเลว</p>
<p>ย้อนกลับไปในสมัยที่ผู้ชายดีๆ ยังมีอยู่มากบนพื้นโลก ผู้ชายเหล่านี้ล้วนแต่เป็นสุภาพบุรุษ ผู้ดำรงตนอยู่ในคลองธรรมอันควร และไม่เคยได้นำตัวเองไปสุ่มเสี่ยง ต่อสิ่งอบายทั้งหลายแต่อย่างใด แต่ทั้งๆที่ประกอบไปด้วยคุณสมบัติที่ดีเหล่านั้น ผู้ชายเหล่านี้กลับไม่ได้รับความสนใจ จากผู้หญิงเท่าที่ควร ที่รักดีนั้นถูกมองเป็นน่าเบื่อ ที่มั่นคงนั้นถูกเห็นเป็นจืดชืด ผู้หญิงกลับลุ่มหลงในคุณสมบัติด้านตรงข้าม กับที่พวกเขามีโดยสิ้นเชิง จนถึงขนาดมีคำกล่าวว่า &#8220;<strong>ผู้หญิงมักชอบผู้ชายเลว</strong>&#8221; เหตุการณ์เป็นเช่นนี้มานับร้อยๆปี จากทฤษฎีการอยู่รอดของ <a href="http://en.wikipedia.org/wiki/Charles_Darwin">Charles Darwin</a> ที่กล่าวไว้<a href="http://en.wikipedia.org/wiki/Natural_selection">ในทฤษฎีการคัดสรรตามธรรมชาติ</a>ว่า คุณสมบัตที่ดี่ที่เอื้ออำนวยต่อการอยู่รอดนั้น จะถูกถ่ายทอดต่อไปยังลูกหลาน แต่คุณสมบัติที่ไม่เอื้ออำนวย ต่อการดำรงอยู่ของเผ่าพันธุ์นั้นจะลดน้อยลง และหาได้ยากขึ้นเรื่อยๆในรุ่นต่อๆไป ตลอดระยะเวลาร้อยๆปีนี้ พันธุกรรมของผู้ชาย ได้ปรับปรุงเปลี่ยนแปลงมาเรื่อยๆ ยีนส์ที่ส่งผลต่อพฤติกรรมเลวๆ ได้มีจำนวนมากขึ้นเรื่อยๆ เพื่อสงผลต่อความดึงดูดใจจากผู้หญิงนั่นเอง</p>
<p>นักวิทยาศาสตร์บางท่านตั้งข้อสังเกตุว่า อัตราการเพิ่มของยีนส์เลวในผู้ชายนั้น เพิ่มขึ้นทุกปีจนอาจจะอยู่ในระดับ &#8220;<strong>เลวกว่าหมา</strong>&#8221; ก็เป็นได้ แม้จะมีผู้หญิงที่ยังมีความสนใจในผู้ชายดีอยู่บ้าง แต่นั้นก็เป็นเพียงผู้หญิงส่วนน้อยในสังคม นักวิทยาศาสตร์ยังได้กล่าวเตือนผู้ชายในปัจจุบัน ที่ยังฝืนทำตัว &#8220;รักดี&#8221; อยู่ว่า พวกเขาได้ทำตัวฝืนวิถีของธรรมชาติอยู่ ผู้ชายจะเลวนั้นเป็นสิ่งถูกต้องแล้ว การที่ผู้ชายที่เลวกว่าหมา แต่ก็ยังมีผู้หญิงมารักนั้นเป็นข้อพิสูจน์หนึ่ง ว่าการคัดสรรของธรรมชาติใ ห้ผู้ชายเลวนั้นเป็นทิศทางที่ถูกต้องแล้ว</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devguli.com/blog/thai/why_man_is_bad/feed/</wfw:commentRss>
		</item>
		<item>
		<title>DeadLock</title>
		<link>http://www.devguli.com/blog/translated/deadlock/</link>
		<comments>http://www.devguli.com/blog/translated/deadlock/#comments</comments>
		<pubDate>Sun, 14 Sep 2008 16:27:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[translated]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=64</guid>
		<description><![CDATA[
Translate from DeadLock and Deadlock Prevention of Jakob Jenkov&#8217;s tutorial
Thanks a ton to Jakob for allowing me to translate his excellent  tutorial

Deadlock คือการที่เธรด (thread) สองเธรดหรือมากกว่านั้นถูกบล็อคการทำงานไว้เพื่อรอเข้าถือจองล็อค (lock) ที่มีเธรดอื่นใน deadlock นั้นได้ทำการจองไว้เรียบร้อยแล้ว. Deadlock สามารถเกิดขึ้นได้เมื่อเธรดต้องการล็อดเดียวกันพร้อมๆกัน เพียงแต่ได้ทำการขอจองล็อคเหล่านั้นในลำดับที่ต่างกันไป
ตัวอย่างเช่น เธรด1 ทำการจองล็อค A ไว้แล้ว และกำลังพยายามจะขอจองล็อค B แต่ในขณะนั้นเองเธรด2 ที่ได้ทำการจองล็อค B ไว้ได้แล้วก็พยายามจะขอจองล๊อค A ที่เธรด1 ถืออยู่ เธรด1 จะไม่มีวันได้ล๊อค B ไปและเธรด2 ก็ไม่มีวันจะได้ล็อค A ไป นอกจากนั้นแล้วทั้งสองเธรดต่างก็ไม่มีทางจะรู้ได้เลยว่าตัวเองต่างก็ไม่มีวันจะได้ล็อคที่ต้องการมา สถานการณ์เช่นนี้เรียกว่าเกิด [...]]]></description>
			<content:encoded><![CDATA[<p><strong>
<p>Translate from <a href="http://tutorials.jenkov.com/java-concurrency/deadlock.html">DeadLock</a> and <a href="http://tutorials.jenkov.com/java-concurrency/deadlock-prevention.html">Deadlock Prevention</a> of Jakob Jenkov&#8217;s <a href="http://tutorials.jenkov.com/java-concurrency/index.html">tutorial</a><br />
Thanks a ton to Jakob for allowing me to translate his excellent  tutorial</p>
<p></strong></p>
<p>Deadlock คือการที่เธรด (thread) สองเธรดหรือมากกว่านั้นถูกบล็อคการทำงานไว้เพื่อรอเข้าถือจองล็อค (lock) ที่มีเธรดอื่นใน deadlock นั้นได้ทำการจองไว้เรียบร้อยแล้ว. Deadlock สามารถเกิดขึ้นได้เมื่อเธรดต้องการล็อดเดียวกันพร้อมๆกัน เพียงแต่ได้ทำการขอจองล็อคเหล่านั้นในลำดับที่ต่างกันไป<br />
ตัวอย่างเช่น เธรด1 ทำการจองล็อค A ไว้แล้ว และกำลังพยายามจะขอจองล็อค B แต่ในขณะนั้นเองเธรด2 ที่ได้ทำการจองล็อค B ไว้ได้แล้วก็พยายามจะขอจองล๊อค A ที่เธรด1 ถืออยู่ เธรด1 จะไม่มีวันได้ล๊อค B ไปและเธรด2 ก็ไม่มีวันจะได้ล็อค A ไป นอกจากนั้นแล้วทั้งสองเธรดต่างก็ไม่มีทางจะรู้ได้เลยว่าตัวเองต่างก็ไม่มีวันจะได้ล็อคที่ต้องการมา สถานการณ์เช่นนี้เรียกว่าเกิด deadlock ขึ้น<br />
สถานการณ์นี้อาจจะแสดงให้เห็นได้ดังนี้<br />
Thread 1  locks A, waits for B<br />
Thread 2  locks B, waits for A</p>
<p>ตัวอย่างโค้ดด้านล่างนี้คือคลาส TreeNode ที่ได้ทำการเรียกเมธอดที่ทำการ synchronized ในแต่ละอินสแตนต่างกันไป</p>
<pre class="syntax-highlight:java">
public class TreeNode {
  TreeNode parent   = null;
  List     children = new ArrayList();

  public synchronized void addChild(TreeNode child){
    if(!this.children.contains(child)) {
      this.children.add(child);
      child.setParentOnly(this);
    }
  }
  public synchronized void addChildOnly(TreeNode child){
    if(!this.children.contains(child) ) {
      this.children.add(child);
    }
  }
  public synchronized void setParent(TreeNode parent){
    this.parent = parent;
    parent.addChildOnly(this);
  }
  public synchronized void setParentOnly(TreeNode parent){
    this.parent = parent;
  }
}
</pre>
<p>จากตัวอย่างด้านบน deadlock สามารถเกิดขึ้นได้ ถ้าเธรด(1) ทำการเรียก parent.addChild(child) พร้อมๆกับที่เธรด(2) ทำการเรียก child.setParent(parent) บนอินสแตนของ parent และ child ชุดเดียวกันนี้ ด้านล่างเป็นโค้ดจำลองเพื่อแสดงให้เห็นสถานการณ์นี้</p>
<p><strong>Thread 1: parent.addChild(child); //locks parent<br />
          &#8211;> child.setParentOnly(parent);</p>
<p>Thread 2: child.setParent(parent); //locks child<br />
          &#8211;> parent.addChildOnly()</strong></p>
<p>เธรด1 เรียกใช้งาน parent.addChild(child) ซึ่งจากการที่ addChild() นั้นเป็น synchronized ดังนั้นเธรด1 ได้ทำการจองล็อคตัว parent อ็อบเจกต์ไว้ทำให้เธรดอื่นไม่สามารถเข้าใช้งานตัว parent อ็อบเจกต์นี้ได้ (ดูเพิ่มเติมข้อ1)  ในขณะเดียวกันนั้นเธรด2 ก็ทำการเรียกใช้ child.setParent(parent) ซึ่ง setParent() ก็เป็น synchronized ดังนั้นเธรด2 ก็ได้ทำการจองล็อคตัว child อ็อบเจกต์ไว้ทำให้เธรดอื่นไม่สามารถเข้าใช้งานตัว child อ็อบเจกต์นี้ได้ (ดูเพิ่มเติมข้อ1)  จากนั้นเธรด1 ก็ได้พยายามจะเรียกใช้งาน child.setParentOnly() แต่ก็พบว่า child อ็อบเจกต์ได้ถูกล็อคไปแล้วโดยเธรด2 ดังนั้นเธรด1จะเข้าสู่สถานะบล็อค (เพื่อรอจนกว่าคนที่ล็อค child อ็อบเจกต์ไว้จะปล่อยล็อค เธรด1จึงจะสามารถเข้าทำงานเมธอดนี้ได้) ในลักษณะเดียวกันนั้น เธรด2 ก็ได้พยายามจะเรียก parent.addChildOnly() แล้วก็พบว่า parent อ็อบเจกต์นั้นได้ถูกล็อคไปแล้วโดยเธรด1 เป็นผลให้ เธรด2เข้าสู่สถานะบล็อคเช่นเดียวกัน ถึงตอนนี้ทั้งสองเธรดต่างก็อยู่ในสถานะบล็อคเพื่อรอขอล็อคที่อีกเธรดหนึ่งถืออยู่ </p>
<p>สถานการณ์ดังกล่าวข้างบนนั้นจะเกิดขึ้นได้เมื่อเธรดหนึ่งเรียก parent.addChild(child) และ อีกเธรดหนึ่งจะเรียก child.setParent(parent) ในขณะเวลาเดียวกัน บนชุด parent และ child อ็อบเจกต์เดียวกัน โค้ดตัวอย่างนี้อาจจะประมวลผลได้ถูกต้องไปเป็นระยะเวลานานจนกระทั่งเกิด deadlock ขึ้นสักวันนึงในทันทีทันใด</p>
<p>โปรดสังเกตุว่าเพื่อที่ deadlock จะเกิดขึ้นได้นั้นทั้งสองเธรดจะต้องพยายามจะขอจองล็อค “ในขณะเวลาเดียวกัน” จริงๆ ถ้าหากว่าเธรด1 เร็วกว่าเธรด2 สักนิดนึงและได้ทำการจองได้ทั้งล็อค A และ B แล้วละก็ จะทำให้เธรด2 เข้าสู่สถานะบล็อคตั้งแต่ตอนที่พยายามจะขอล็อค B แล้ว ซึ่งก็จะไม่ทำให้เกิด deadlock ขึ้น และเนื่องจากการจัดตารางลำดับเวลาการประมวลผลของเธรดนั้นเป็นสิ่งที่คาดเดาไม่ได้ ดังนั้นเราจึงไม่สามารถทำนายได้ว่า deadlock “จะเกิดขึ้นเมื่อไร” เราบอกได้แต่เพียง “มันสามารถเกิดขึ้นได้”</p>
<p>Deadlock ที่ซับซ้อนขึ้น<br />
Deadlock ยังสามารถเกิดขึ้นได้กับเธรดมากกว่าสองซึ่งทำให้เราตรวจหามันได้ยากขึ้น. ด้านล่างเป็นตัวอย่าง เธรด 4 ตัวเกิด deadlock กัน<br />
Thread 1  locks A, waits for B<br />
Thread 2  locks B, waits for C<br />
Thread 3  locks C, waits for D<br />
Thread 4  locks D, waits for A</p>
<p>การป้องกันไม่ให้เกิด deadlock (Deadlock Prevention)<br />
ในบางสถานการณ์นั้นเราสามารถป้องกัน deadlock ไม่ให้เกิดขึ้นได้ ผมจะอธิบายเทคนิคสามข้อดังต่อไปนี้<br />
1.	การจัดลำดับการจองล็อค (Lock Ordering)<br />
2.	การจำกัดเวลาในการจองล็อค (Lock Timeout)<br />
3.	การตรวจหา deadlock (Deadlock Detection )</p>
<p><strong>1. การจัดลำดับการจองล็อค</strong><br />
Deadlock เกิดขึ้นเมื่อเธรดมากกว่าหนึ่งต้องการจองล็อคตัวเดียวกันแต่ทำการเข้าจองในลำดับที่ต่างกัน ถ้าเราสามารถบังคับให้การจองล็อคทั้งหมดนั้นเป็นไปในลำดับเดียวกันในทุกๆเธรดแล้ว deadlock ก็จะไม่เกิด พิจารณาตัวอย่างต่อไปนี้</p>
<p>Thread 1:</p>
<p>  lock A<br />
  lock B</p>
<p>Thread 2:</p>
<p>   wait for A<br />
   lock C (when A locked)</p>
<p>Thread 3:</p>
<p>   wait for A<br />
   wait for B<br />
   wait for C</p>
<p>เมื่อเธรดใดๆ ต้องการล็อคหลายตัวแล้ว เธรดนั้นจะต้องเข้าของล็อคนั้นๆในลำดับที่ถูกกำหนดไว้ก่อนแล้ว เธรดจะต้องไม่จองล็อคสลับลำดับที่ได้กำหนดไว้ ตัวอย่างเช่น ทั้งเธรด 2 และเธรด 3 ต่างก็ไม่สามารถจะเข้าขอจองล็อค C ได้ถ้าไม่ได้ถือครองล็อค A ไว้ได้ก่อนแล้ว และเมื่อเธรด 1 ถือครองล็อค A ไว้อยู่ ดังนั้นเธรด 2 และเธรด 3 ต้องรอให้เธรด 1 ปล่อยการถือครองล็อค A เสียก่อน จากนั้นจึงเข้าถือครองล็อค A ไว้ได้สำเร็จจึงจะสามารถขอเข้าจองล็อค B และ C ต่อไป การจัดลำดับการเข้าจองล็อคนั้นเป็นวิธีที่เรียบง่ายและได้ผลดีในการป้องกันการเกิด deadlock อย่างไรก็ตาม วิธีนี้จะใช้ได้ก็ต่อเมื่อคุณรู้การใช้งานล็อคทั้งหมดล่วงหน้าก่อนการเข้าจองล็อคใดๆ  ซึ่งนั่นก็ไม่ใช่สิ่งที่จะสามารถรู้ล่วงหน้าได้ในทุกกรณี</p>
<p><strong>2. การจำกัดเวลาในการจองล็อค</strong><br />
วิธีการป้องกัน deadlock อีกวิธีหนึ่งคือการกำหนดเวลาในการพยายามจองล็อค นั่นคือเธรดจะพยายามขอจองล็อคในระยะเวลาหนึ่งก่อนที่จะเลิกล้มความพยายาม ถ้าเธรดใดไม่สามารถเข้าถือครองล็อคที่ต้องการไว้ได้ทั้งหมดในระยะเวลาที่กำหนด เธรดนั้นจะถอยกลับโดยปล่อยการถือครองล็อคทั้งหมดที่ถือไว้  จากนั้นทำการรอเป็นระยะเวลาหนึ่งก่อนจะพยายามใหม่ ที่ทำการรอเป็นช่วงเวลาหนึ่งนั้นก็เพื่อจะเปิดโอกาสให้เธรดอื่นเข้าขอจองล็อค (ที่เธรดนั้นได้เคยถือครองไว้ก่อนจะถอยกลับ) และทำให้โปรแกรมสามารถทำงานต่อไปได้</p>
<p>ด้านล่างนี้เป็นตัวอย่างของเธรดสองตัวพยายามจะเข้าจองล็อคชุดเดียวกันในลำดับที่ต่างกัน ซึ่งจะเห็นการถอยกลับและพยายามเข้าจองล็อคใหม่อีกครั้งของเธรดทั้งสองตัว</p>
<p>Thread 1 locks A<br />
Thread 2 locks B</p>
<p>Thread 1 attempts to lock B but is blocked<br />
Thread 2 attempts to lock A but is blocked</p>
<p>Thread 1&#8217;s lock attempt on B times out<br />
Thread 1 backs up and releases A as well<br />
Thread 1 waits randomly (e.g. 257 millis) before retrying.</p>
<p>Thread 2&#8217;s lock attempt on A times out<br />
Thread 2 backs up and releases B as well<br />
Thread 2 waits randomly (e.g. 43 millis) before retrying</p>
<p>ในตัวอย่างนั้นเธรด 2 จะพยายามเข้าจองล็อคใหม่อีกครั้งก่อนเธรด 1 เป็นระยะเวลาประมาณ 200 มิลลิเซค ซึ่งมีความเป็นไปได้สูงที่เธรด 2 จะประสบความสำเร็จในการเข้าจองล็อคทั้งสองตัว จากนั้นเธรด 1 จึงจะพยายามเข้าจองล็อค A ใหม่อีกครั้ง และเมื่อเธรด 2 ทำการประมวลผลเสร็จแล้ว (และทำการปล่อยการถือครองล็อคที่ถืออยู่) เธรด 1 ก็จะมีโอกาสจะเข้าจองล็อคทั้งสองตัวบ้าง (นอกจากว่าเธรด 2 หรือเธรดอื่นๆเข้าทำการจองล็อคชุดนี้แทรกระหว่างนั้น) ข้อควรจำอย่างหนึ่งคือ การที่เธรดไม่สามารถจองล็อคได้สำเร็จ ในระยะเวลาที่กำหนดนั้นไม่ได้หมายความว่าเป็นเพราะมี deadlock เกิดขึ้นเสมอไป อาจจะเป็นได้ว่าเธรดที่ถือครองล็อค (จนทำให้เธรดอื่นหมดเวลาในการพยายามเข้าจองล็อค ) อยู่นั้น ใช้เวลานานในการทำงานจนสำเร็จ นอกจากนั้นแล้ว ถ้ามีเธรดจำนวนมากเข้าแย่งกันขอจองล็อคแล้ว มีโอกาสสูงที่เธรดจะเข้าแย่งกันจองล็อคพร้อมๆกันครั้งแล้วครั้งเล่า แม้ว่าจะเกิดกระบวนการถอยกลับแล้วพยายามใหม่แล้วก็ตาม เหตุการณ์เช่นนี้ อาจจะไม่เกิดกับเธรดแค่สองตัวที่ทำการรอเป็นเวลา 0 ถึง 500 มิลลิเซค ก่อนพยายามใหม่อีกครั้ง แต่ถ้าหากพูดถึงเธรด 10 – 20 ตัวละก็อีกเรื่องหนึ่ง มีโอกาสสูงที่สองเธรดจะรอเป็นเวลาเท่าๆกัน (หรือใกล้เคียงกันพอที่จะทำให้เกิดปัญหา) ก่อนจะเริ่มพยายามจองล็อคใหม่อีกครั้ง ปัญหาอย่างหนึ่งของการกำหนดเวลาในการจองขอจองล็อคก็คือ เราไม่สามารถจะจำกัดเวลาสำหรับ synchronized block (โค้ดส่วนที่ถูกครอบด้วย synchronized keyword) ได้ เราอาจจะต้องสร้างล็อคขึ้นเองหรือไม่ก็ใช้ล็อคของ Java 5 ใน java.util.concurrency</p>
<p><strong>3. การตรวจหา deadlock</strong><br />
การตรวจหา deadlock นั้นจะถูกใช้ในกรณีที่การจัดลำดับการจองล็อคและการกำหนดเวลาในการพยายามจองล็อคนั้นไม่สามารถทำได้ โดยทุกครั้งที่เธรดได้ทำการถือครองล็อคไว้ เธรดนั้นจะถูกบันทึกไว้ในโครงสร้างข้อมูล (map , graph  etc.) ของเธรดและล็อค  นอกจากนั้นแล้วเมื่อไรก็ตามที่เธรดทำการขอเข้าจองล็อคตัวใด ข้อมูลการร้องงขอนี้ก็จะถูกบันทำไว้ด้วย เมื่อเธรดหนึ่งเธรดใดขอเข้าจองล็อคแล้วถูกปฏิเสธ เธรดนั้นสามารถตรวจสอบกราฟของล็อคเพื่อตรวจหา deadlock ได้ ตัวอย่างเช่น ถ้าเธรด A ขอจองล็อค 7 ที่ถูกถือครองไว้โดยเธรด B แล้ว เธรด A สามารถตรวจสอบได้ว่าเธรด B ได้ทำการขอจองล็อคใดๆที่เธรด A ได้ถือครองอยู่หรือไม่ ถ้าใช่ก็แสดงว่าเกิด deadlock ขึ้น (เธรด A ถือครองล็อค 1 ไว้อยู่และกำลังพยายามขอจองล็อค 7 ขณะเดียวกันเธรด B ได้ทำการถือครองล็อค 7 ไว้และกำลังพยายามจะขอจองล็อค 1) แน่นอนว่ารูปแบบการเกิด deadlock นั้นสามารถมีความซับซ้อน ได้มากกว่าการที่สองเธรดต่างถือครองล็อคที่อีกเธรดหนึ่งต้องการ เธรด Aอาจจะรอเธรด B, เธรด B รอเธรด C, เธรด C รอเธรด D และเธรด D รอเธรด A การที่เธรด A จะสามารถตรวจหา deadlock ได้นั้น เธรด A ต้องตรวจสอบล็อคทั้งหมดที่ที่เธรด B ต้องการทั้งหมดทั้งทางตรงและทางอ้อม จากล็อคที่เธรด B ร้องขอโดยตรงนั้น เธรด A จะต้องมองไปถึงเธรด C (เพราะ เธรด C ถือล็อคตัวหนึ่งที่เธรด B ต้องการอยู่ เธรด A จึงเหมือนกับรอเธรด C ไปด้วย) จากเธรด C มองต่อไปจนถึงเธรด D จึงจะพบว่ามีล็อคตัวหนึ่ง (ที่เธรด B ต้องการทั้งทางตรงและทางอ้อม) ที่ถูกถือครองไว้แล้วโดยเธรด A ถึงตอนนี้จึงสรุปได้ว่าเกิด deadlock ขึ้น ตัวอย่างด้านล่างคือกราฟของล็อคที่ถูกถือครองอยู่ (taken) และกำลังถูกร้องขอการเข้าจอง (requested)  ของเธรดสี่ตัว A, B, C และ D โครงสร้างข้อมูลเช่นนี้สามารถนำมาใช้ตรวจหา deadlock ได้</p>
<p><img alt="" src="http://www.devguli.com/blog/wp-content/uploads/2008/09/deadlock-detection-graph.png" title="deadlock-detection-graph" class="alignnone" width="225" height="355" /></p>
<p>ถามว่าจะทำอย่างไรถ้าเกิด deadlock ขึ้น?<br />
ทางหนึ่งที่เป็นไปได้คือปล่อยการถือครองล็อคทั้งหมด ทำการถอยกลับรอเป็นระยะเวลาหนึ่งแล้วเริ่มพยายามใหม่อีกครั้ง ซึ่งเป็นวิธีเดียวที่ใช้กับการจำกัดเวลาการพยายามจองล็อคต่างกันที่ทำเฉพาะตอนที่เกิด deadlock ขึ้นจริงๆเท่านั้น อย่างไรก็ตาม ถ้าเธรดจำนวนมากทำการเแย่งขอจองล็อคตัวเดียวกันแล้ว ก็มีโอกาสที่เธรดจะเกิด deadlock ซ้ำๆกันบ่อยคร้งแท้ว่าจะทำการถอยกลับและรอพยายามใหม่แล้วก็ตาม</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devguli.com/blog/translated/deadlock/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Spurious Wakeup</title>
		<link>http://www.devguli.com/blog/eng/spurious-wakeup/</link>
		<comments>http://www.devguli.com/blog/eng/spurious-wakeup/#comments</comments>
		<pubDate>Sun, 14 Sep 2008 16:09:50 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[eng]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=59</guid>
		<description><![CDATA[I have finished reading Effective Java long time ago. It is such a great book. The more I passed through each page the more I realized how little I know about java programming. The distance between “coder”  and  “developer” is really far
I read most of the item listed in the book. One of [...]]]></description>
			<content:encoded><![CDATA[<p>I have finished reading <a href="http://java.sun.com/docs/books/effective/">Effective Java</a> long time ago. It is such a great book. The more I passed through each page the more I realized how little I know about java programming. The distance between <b>“coder” </b> and <b> “developer”</b> is really far</p>
<p>I read most of the item listed in the book. One of the items I have skipped was <b>Item 50: Never invoke wait outside a loop</b>. The code below show the concept of this item</p>
<pre class="syntax-highlight:java">
synchronized (obj) {
    while (&lt;condition does not hold&gt;)
        obj.wait();

     ... // Perform action appropriate to condition
 }
</pre>
<p>Looking at the name of the practice, I thought I knew all the reasons behind it so I just skipped it. Today I found an interesting post asking <a href="http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&#038;f=27&#038;t=002733">What is spurious wakeup</a>. I read it and found that <b>“threads can wake up on wait() for no reason at all”!!!!! </b> There are quite many good references for this fact and one of them is, guess what, the item 50 of Effective Java. I would have known it for long time ago if I just read it. One of the reasons behind it stated in the item is that</p>
<blockquote><p>
The waiting thread could wake up in the absence of a notify. This is known as a spurious wakeup. Although The Java Language Specification [JLS] does not mention this possibility, many JVM implementations use threading facilities in which spurious wakeups are known to occur, albeit rarely [Posix, 11.4.3.6.1]
</p></blockquote>
<p>I thought sometimes it was OK to call wait() without condition-checking loop if the object to wait on represented just one condition, the object was shared only between waiting and  notifying threads and the code’s execution order guaranteed that <a href="http://www.ibm.com/developerworks/java/library/j-perf01215/index.html">wait leaks</a> would not occur. Now giving that JVM implementation can send spurious wakeup signal, the condition checking loop is A MUST</p>
<p>Apparently, the spurious wakeup is an issue (I doubt that it is a well known issue) that intermediate to expert developers know <a href=" http://www.jroller.com/tackline/entry/on_while_free_waits">it can happen </a> but it just has been clarified in <a href=" http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.8.1">JLS third edition</a> which has been revised <a href="http://weblogs.java.net/blog/tomwhite/archive/2005/02/the_java_langua_1.html">as part of JDK 5 development</a>. The javadoc of wait method in JDK 5 has also been updated</p>
<blockquote><p>
A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops
</p></blockquote>
<p>You may wonder (like me) why JLS designer decided to allow this kind of thing to happen. It’s not that I don’t want to use condition checking loop. The checking is the best practice that developers should always do no matter of them knowing anything about the spurious wakeup or not. But I just don’t see the benefit of allowing the wakeup for no reason. It turns out that this is something about performance as stated in <a href="http://www.amazon.com/Multithreaded-Programming-Technology-Microsystems-Press/dp/0130170070">Multithread Programming with Java</a></p>
<blockquote><p>
Due to some arcania in the hardware design of modern SMP machines, it proves to be highly convenient to define them like this. The hardware runs a little faster, and the programmer needs to reevaluate the condition anyway
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.devguli.com/blog/eng/spurious-wakeup/feed/</wfw:commentRss>
		</item>
		<item>
		<title>10 เรื่องที่โปรเจคเมเนเจอร์อยากให้ดีเวลลอปเปอร์เข้าใจ</title>
		<link>http://www.devguli.com/blog/translated/10things_pm_wish_developers_understood/</link>
		<comments>http://www.devguli.com/blog/translated/10things_pm_wish_developers_understood/#comments</comments>
		<pubDate>Sun, 14 Sep 2008 16:01:36 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[translated]]></category>

		<category><![CDATA[management]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=56</guid>
		<description><![CDATA[Translated from : 10 things Project Managers wish Developers understood
Author : Frank Kelly
ตลอดสองสามเดือนมานี้ผมยุ่งมาก ในการปรับตัวกับบทบาทใหม่ในการเป็น โปรเจคเมเนเจอร์ โดยทางเทคนิคแล้วผมจะถูกเรียกว่า “team lead” หรือ “director” หรือ “senior manager” แล้วแต่ว่าใครจะเรียก หน้าที่จริงๆของผม คือประสานงานกับโปรดักเมเนเจอร์ ในการบริหารจัดการทีมพัฒนาซอฟแวร์ ตามแผนงานของโปรเจค การได้มาอยู่ใน “อีกด้านหนึ่ง” สักพักนึงแล้วนั้นเหมือนกับการได้เปิดตาสู่โลกใหม่ มันเป็นเรื่องจริงที่ว่ามันเป็นเรื่องยากมากๆในการเข้าถึงใครสักคนนั้น จนกว่าคุณจะได้ลองทำลองเป็นอย่างที่คนๆนั้นเป็น จากที่ได้เคยร่วมงานกับโปรเจคเมเนเจอร์มา ผมเคยสงสัยอยู่ตลอดว่าทำไมเรื่องบางอย่างถึงดำเนินไปในบางแนวทาง มันเป็นแนวทางที่ผมเคยคิดว่าผมจะเลือกทำสิ่งที่ต่างออกไป ตอนนี้ผมคงไม่คิดอย่างนั้นอีกแล้ว จากประสบการณ์ในการพยายามทำในสิ่งเหล่านั้น ที่เคยพูดไว้หรือคิดไว้ ผมอยากจะเขียนถึงสิ่งต่างๆ ที่ดีเวลลอปเปอร์ในทีมไม่ได้นึกไปถึง จนกว่าพวกเขาจะได้ลองมาเป็นคนจัดการเอง
10) การท่วมล้นของข้อมูล: ผมก็อยากจะตั้งสมาธิ กับเรื่องใดเรื่องหนึ่ง เป็นอย่างๆไปเหมือนกัน แต่ผมมีคนเป็นล้านพุ่งเข้าหาผมจากทุกทิศทาง
ไม่ว่าจะเป็นโปรดักเมเนเจอร์, ซัพพอร์ท (support), เมเนจเมนท์ (management), เหล่าลูกค้ากับความต้องการต่างๆ, ดีเวลลอปเปอร์ รวมไปถึงเมเนเจอร์, อาร์คีเทค [...]]]></description>
			<content:encoded><![CDATA[<p><b>Translated from : </b><a href=”http://softarc.blogspot.com/2008/02/ten-things-developers-would-know-if.html”>10 things Project Managers wish Developers understood</a><br />
<b>Author : Frank Kelly</b></p>
<p>ตลอดสองสามเดือนมานี้ผมยุ่งมาก ในการปรับตัวกับบทบาทใหม่ในการเป็น โปรเจคเมเนเจอร์ โดยทางเทคนิคแล้วผมจะถูกเรียกว่า “team lead” หรือ “director” หรือ “senior manager” แล้วแต่ว่าใครจะเรียก หน้าที่จริงๆของผม คือประสานงานกับโปรดักเมเนเจอร์ ในการบริหารจัดการทีมพัฒนาซอฟแวร์ ตามแผนงานของโปรเจค การได้มาอยู่ใน “อีกด้านหนึ่ง” สักพักนึงแล้วนั้นเหมือนกับการได้เปิดตาสู่โลกใหม่ มันเป็นเรื่องจริงที่ว่ามันเป็นเรื่องยากมากๆในการเข้าถึงใครสักคนนั้น จนกว่าคุณจะได้ลองทำลองเป็นอย่างที่คนๆนั้นเป็น จากที่ได้เคยร่วมงานกับโปรเจคเมเนเจอร์มา ผมเคยสงสัยอยู่ตลอดว่าทำไมเรื่องบางอย่างถึงดำเนินไปในบางแนวทาง มันเป็นแนวทางที่ผมเคยคิดว่าผมจะเลือกทำสิ่งที่ต่างออกไป ตอนนี้ผมคงไม่คิดอย่างนั้นอีกแล้ว จากประสบการณ์ในการพยายามทำในสิ่งเหล่านั้น ที่เคยพูดไว้หรือคิดไว้ ผมอยากจะเขียนถึงสิ่งต่างๆ ที่ดีเวลลอปเปอร์ในทีมไม่ได้นึกไปถึง จนกว่าพวกเขาจะได้ลองมาเป็นคนจัดการเอง</p>
<p><b>10) การท่วมล้นของข้อมูล: ผมก็อยากจะตั้งสมาธิ กับเรื่องใดเรื่องหนึ่ง เป็นอย่างๆไปเหมือนกัน แต่ผมมีคนเป็นล้านพุ่งเข้าหาผมจากทุกทิศทาง</b><br />
ไม่ว่าจะเป็นโปรดักเมเนเจอร์, ซัพพอร์ท (support), เมเนจเมนท์ (management), เหล่าลูกค้ากับความต้องการต่างๆ, ดีเวลลอปเปอร์ รวมไปถึงเมเนเจอร์, อาร์คีเทค (architects) และดีเวลลอปเปอร์ในทีมอื่นๆอีก มันเป็นข้อมูลและความสัมพันธ์เป็นจำนวนมากที่ต้องบริหารจัดการ ดังนั้นอย่าแปลกใจถ้าคุณเจอกับการโต้ตอบในลักษณะต่อไปนี้</p>
<p><b>ดีเวลลอปเปอร์ :</b> คุณยังจำปัญหาเรื่อง GUI ในสัปดาห์ที่แล้วได้หรือเปล่า?<br />
(อีกนัยหนึ่งคือ: เจ้าปัญหาร้ายแรงนั่น ที่ผมหาทางแก้ได้ในที่สุดนั่นไง)<br />
<b>เมเนเจอร์:</b> ปัญหาอันไหนละ?<br />
(อีกนัยหนึ่งคือ: ผมเพิ่งจะใช้เวลา 3 ชั่วโมงในการตอบเมลล์กับคนอื่นอีก 14 คน และผมเหนื่อยเหลือเกิน)</p>
<p><b>9) ความกดดันในการทำงาน: เมเนเจอร์บางครั้งอาจจะตื่นเต้น และรู้สึกกดดันจนเกินไป จนพยายามที่จะ “ชี้ทางแก้ปัญหา” ให้กับดีเวลลอปเปอร์. ปฏิเสธ (อย่างสุภาพ) ไปก็ได้ถ้าหากเห็นว่ามันไม่เหมาะสม</b><br />
เมื่อคุณมาเป็นเมเนเจอร์และไม่ได้เขียนโค้ดอีกต่อไป คุณจะต้องแบกรับความรับผิดชอบ(ความกังวล) ในการทำงานให้เสร็จตามกำหนด แต่คุณมีอำนาจในการควบคุมมันน้อยเหลือเกิน เหล่าเมเนเจอร์ที่ใช้เวลาเป็นดีเวลลอปเปอร์ไปด้วยในบางครั้ง มักจะคิดว่าพวกเขาสามารถที่จะชี้ทางแก้ปัญหาให้กับดีเวลลอปเปอร์คนอื่นๆได้ แต่นั่นมันไม่ได้ผลหรอก ดีเวลลอปเปอร์เป็นพวกมีความคิดสร้างสรรค์และ(ไม่ว่าจะดีหรือไม่) เป็นพวกยึดมั่นในความคิดของตัวเอง (egotistical) สูง (ผมก็เป็นหนึ่งในนั้น) และพวกเราชอบที่จะทำอะไรให้ประทับใจหัวหน้า มากกว่าที่จะแค่รับคำสั่งไปปฏิบัติตาม ผมได้เรียนรู้ที่จะถามคำถาม ในเชิงชี้นำหรือในเชิงการหาข้อมูล มากว่าการชี้ทางแก้ปัญหา ตัวอย่างเช่น</p>
<ul>
<li>จะมีกี่ไฟล์ที่ได้รับผมกระทบจากการแก้ปัญหานี้ละ</li>
<li>การเปลี่ยนของ SQL อันนี้จะกระทบกับประสิทธิภาพการทำงานยังไง </li>
<li>การเปลี่ยนของ GUI นี้จะกระทบต่อการทำงานของลูกค้าหลักของเรายังไง</li>
<li>ถ้าเราแก้ปัญหานี้แล้วมันจะไปทำให้เกิดปัญหาที่อื่นหรือเปล่า</li>
</ul>
<p><b>8 ) ความเสี่ยงของโปรเจค: เทคโนโลยีใหม่ = ความเสี่ยง</b><br />
ทุกครั้งที่คุณเริ่มต้นโปรเจคใหม่ ดีเวลลอปเปอร์ก็ชอบที่จะใช้เทคโนโลยีใหม่ๆ แต่คุณจะทุ่มทุกอย่างลงไปกับเทคโนโลยีใหม่ไม่ได้ เทคโนโลยีใหม่ย่อมจะนำความเสี่ยงมาสู่โปรเจค ประสิทธิภาพอาจจะเพิ่มขึ้นแต่มันจะคุ้มกับความเสี่ยงหรือเปล่า<br />
ผมเคยมีเมเนเจอร์ที่เอาแต่ปฏิเสธเทคโนโลยีใหม่ๆ เขาไม่เข้าใจถึงความต้องการของดีเวลลอปเปอร์ ในการพัฒนาตัวเองและเรียนรู้เทคโนโลยียอดฮิต (อย่างเช่น Ajax)/ เฟรมเวิร์ค (อย่างเช่น Spring) มันช่างไม่เป็นที่น่าจูงใจเอาเสียเลย อีกอย่างหนึ่งคือ เทคโนโลยีมักจะช่วยให้ทำงานได้ประสิทธิภาพมากขึ้น มันไม่ใช่แนวคิดที่มองการณ์ไกลเลย ในการเอาแต่ปฏิเสธเทคโนโลยีใหม่ๆ<br />
ผมก็เคยมีเมเนเจอร์ ที่คอยแต่มองหาเทคโนโลยีใหม่ๆเหมือนกัน แล้วโปรเจคก็ไปติดแหง่ก อยู่กับปัญหาทางเทคนิคอันแล้วอันเล่า และไม่เคยทำให้ซอฟแวร์ทำงานได้ ภายในเวลาที่กำหนดได้เลย เนื่องจากดีเวลลอปเปอร์ต้องใช้เวลามาก ในการเรียนรู้เทคโนโลยีใหม่ๆเหล่านั้น ในฐานะที่ผมก็เป็นดีเวลลอปเปอร์ ผมเข้าใจว่าพวกเขารักที่จะเรียนรู้ แต่ในฐานะของเมเนเจอร์ ผมต้องชั่งน้ำหนัก ระหว่างแรงจูงใจของดีเวลลอปเปอร์ กับเป้าหมายในการทำให้โปรดักสำเร็จใช้งานได้</p>
<p><b>7) ประสิทธิภาพ: เมเนเจอร์ที่ยอดเยี่ยม จะกันดีเวลลอปเปอร์ออกจากเรื่องบ้าบอต่างๆ ที่เกิดขึ้นในระหว่างการบริหารงาน อย่างเช่น เสปคที่ไม่ได้เรื่อง, แนวความคิดแปลกๆ, การเปลี่ยนลำดับความสำคัญของงาน  หรือการประชุมที่มากจนเกินไป</b><br />
ถ้าคุณคิดว่าหัวหน้างี่เง่า (<a href=”http://en.wikipedia.org/wiki/Pointy_Haired_Boss”>pointy haired boss</a>) ของคุณนั้นช่างเสียสติจริงๆ รอให้คุณเห็นพวกคนที่เขาทำงานให้ก่อนเถอะ ปัญหาในการพัฒนาซอฟแวร์ในปัจจุบันก็คือ มันต้องเกี่ยวข้องกับทีมมากมายหลายทีม ทีมของดีเวลลอปเปอร์, ทีมของ QA, ซัพพอร์ท, ฝ่ายลูกค้าสัมพันธ์, ฝ่ายที่ปรึกษา และทุกคนต่างก็มีมุมมองและเป้าหมายของตัวเอง พวกเขามีมุมมองในภาพที่ใหญ่กว่า หรือเป็นอีกมุมมองหนึ่งต่างออกไปเลย ดีเวลลอปเปอร์บางคนก็พอคิดได้ว่า ขณะที่สิ่งเหล่านี้ดำเนินไปนั้น  เมเนเจอร์กำลังกันเวลาไว้ เพื่อให้คุณทำงานได้อย่างมีประสิทธิภาพ และทำในสิ่งที่คุณรัก นั่นคือการเขียนโค้ด</p>
<p><b>6) การแสวงหารายได้: บริษัทนั้นอยู่เพื่อต้องการทำเงิน บางครั้งคุณก็ต้องทำเรื่องที่คล้ายว่าโง่ๆเพื่อที่จะได้เงินมา</b><br />
ลูกค้านั้นก็ใช่ว่าจะมีเหตุมีผลทุกคนไป พวกเขามีเป้าหมายและมุมมองของตัวเอง บางครั้งพนักงานงายก็ต้องยอมสัญญาว่าจะทำอะไรบางอย่าง เพื่อให้ข้อตกลงทางการค้าเกิดขึ้นได้ แน่นอนละว่ามันฟังดูมีเหตุมีผลเพียงพอทีเดียว ถ้าหากว่าคุณไม่ใช่คนที่จะต้องสร้างมันขึ้นมานะ แน่นอน คุณมีสิทธิ์ที่จะต่อว่าเอากับพนักงานขายนั้น แต่ก็นั่นละ ยังไงบริษัทที่คุณทำงานอยู่ก็ถูกตั้งขึ้นมาเพื่อ (หวังว่าจะ) หาเงิน พนักงานเขายก็เปรียบได้กับคนหาช่องทางทำเงิน ดังนั้นดีที่สุดที่คุณทำได้คือ สร้างความสัมพันธ์ที่เกื้อกูลกัน โดยพวกเขาทำการปรึกษาคุณว่าอะไรที่สามารถเป็นไปได้ และอะไรที่เกินขอบเขตไป</p>
<p><b>5) ทำในสิ่งที่เราไม่อยากทำ: เราเรียกประชุมก็เพราะมันเป็นสิ่งที่จำเป็น</b><br />
ผมรู้ว่าการประชุมนั้นขัดจังหวะการเขียนโค้ด อันเป็นกิจกรรมสุดโปรดของคุณ แต่มันก็เป็นสิ่งที่ช่วยไม่ได้เลยในบางโอกาส ผมต้องการรู้ว่า คุณเข้าไจความเป็นไปในขณะนี้หรือไม่ จริงๆแล้วผมแทบจะไม่รู้เลยว่า ความคืบหน้าเป็นอย่างไรบ้างแล้ว จนกว่ามันใกล้ๆจะเสร็จโน่นแหละ  โชคดีอยู่บ้าง ในฐานะที่ผมก็เป็นดีเวลลอปเปอร์(ในบางโอกาส)  ผมใช้เครื่องมือเช่น FindBugs, PMD, CheckStyle เพื่อตรวจสอบโค้ด และใช้ Unit Test กับ Code Coverage เพื่อประเมิณคุณภาพของโค้ดอย่างคร่าวๆ แต่สิ่งที่วัดได้ก็เป็นแค่ตัวแทนของค่าที่ต้องการจริง นั่นก็คือ คุณภาพของโปรดักในมุมมองของผู้ใช้งานเท่านั้นแล้วมีวิธีอื่นไหนอีก ที่จะทำให้ผมรู้ความไปต่างๆได้อย่างรวดเร็ว การประชุมไงละ ขอโทษด้วยแล้วกันนะ</p>
<p><b>4) การท่วมล้นของจ้อมูล ภาคสอง: อย่าหวังว่าผมจะจดจำสิ่งต่างๆได้ทุกเรื่องไป</b><br />
ผมลืมไปเลยที่เราได้สัญญากันว่าจะนัดคุยกันเรื่องปัญหานั้น ผมอยากจะใส่ตัวเรียกเตือนความจำไว้ใน Outlook แต่ก็ดันถูกขัดจังหวะสองครั้ง แล้วยังถูกตามตัวไปหาหัวหน้าอีก แล้วผมก็ลืมไปเลย เข้าใจเมเนเจอร์ของคุณสักนิดเถอะ พวกเขาได้พยายาม (หรือควรจะพยายาม) แล้วจริงๆ ถ้าหากผมหลงลืมไปก็ช่วยเตือนด้วย ให้ผมอยู่ในวงข่าวสาร แค่หาโอกาสเหมาะๆบอกผมเท่านั้นละ</p>
<p><b>3) การท่วมล้นของจ้อมูล ภาคสาม: อย่าเพิ่งลงไปถึงรายละเอียดเลย บอกผมก่อนเถอะว่าทำไมผมต้องสนใจมันด้วย</b><br />
ดีเวลลอปเปอร์มีมากมายหลายแนว บางคนก็เก็บงำข้อมูลต่างๆ ก้มหน้าก้มตาอยู่ในห้องทำงานของตัวเอง โผล่มาอีกทีก็ต่อเมื่องานเสร็จแล้ว ที่น่ากลัวก็คือ คุณไม่มีทางรู้เลยว่าพวกเขาทำงานเสร็จแล้ว จนกว่า … อืม .. จนกว่าพวกเขาจะทำเสร็จนั่นละ  ผมจัดการกับคนประเภทนี้ค่อนข้างง่าย  ก็แค่ไปสอบถามเป็นระยะๆ และหวังว่าคงไม่ต้องทำบ่อย อีกฝั่งนึง คือดีเวลลอปเปอร์ที่คิดเอาว่า คุณคงอยากจะรู้ไปซะทุกรายละเอียดยิบย่อย และหวังให้คุณรู้ถึงผลกระทบของมัน ตัวอย่างเช่น</p>
<p><b>ดีเวลลอปเปอร์:</b> “คุณรู้เรื่องคลาส Finder ที่ทีม X ทำขึ้นมาหรือยัง พวกเขาสร้าง Factory เมธอดอันไหม่ที่ไม่ปลอดภัยในการทำงานกับ thread ขึ้นมา ผมคิดว่ามันบ้ามากเลยละ พวกเขาไม่รู้เลยหรือไงว่า thread pool ของเราจะ … … …”<br />
<b>เมเนเจอร์</b>(ผู้ที่กำลังครุ่นคิดอย่างหนักกับเรื่องอื่นอยู่ อย่างความขึ้นต่อกันต่างๆในโปรเจค): (คิดในใจ) เขาจะคิดว่าผมเป็นพวกงี่เง่าหรือเปล่านะ ถ้าผมจะตอบแค่ว่า “หือ” </p>
<p>มันจะดีกว่าถ้าคุณบอกแค่ผลกระทบและเอาเรื่องรายลเอียดไว้ทีหลัง เช่น “ผมคิดว่าการทำงานแบบผู้ใช้งานหลายคนของเราจะมีปัญหากับโค้ดอันไหม่ของทีม X”  นั่นมันตรงประเด็นเลย (มีปัญหาเกิดขึ้น) และยังทำให้ผมไม่ต้องไปคิดมากมาย (ทำไมผมต้องสนใจเรื่อง ความปลอดภัยในการทำงานกับ thread ของคลาสบางคลาสด้วย) </p>
<p><b>2) คุณ (ดีเวลลอปเปอร์) ไม่ได้สำคัญอยู่คนเดียว</b><br />
ผมเคยสำคัญผิดแบบนั้นไปพักหนึ่ง สมัยเมื่อผมยังเป็นดีเวลลอปเปอร์อยู่ ผมคิดจริงๆว่าในปัญหาทั้งหมดที่เผชิญอยู่ ปัญหาที่ผมรับผิดชอบอยู่นั้นสำคัญที่สุด ผมจะรู้สึกหัวเสีย เมื่อปัญหาที่ผมมองว่าสำคัญถูกมองข้ามไป ในความเป็นจริงแล้วก็คือ ผมไม่คิดไปถึงปัญหาอื่นอีกหลายๆอย่าง ที่มีความสำคัญกว่าเลย ว้าว มันขึ้นอยู่กับมุมมองจริงๆ </p>
<p><b>1) เป็นราชานี่มันก็ดีเหมือนกัน</b><br />
ความจริงแล้วมันดีมากๆเลยละ สมัยก่อนที่ผมเป็นอาร์คีเทค หรือหัวหน้าวิศวกรซอฟแวร์ ผมอยู่ในตำแหน่งที่สามารถให้ข้อมูลช่วยการตัดสินใจได้ แต่ก็ไม่ได้มีอำนาจจริงๆอะไรเลย  ตอนนี้ผมมีผู้คนที่ต้องรายงานตรงต่อผม ผมควบคุมการประเมินงาน และอัตตราการขึ้นเงินเดือนของพวกเขา ดูว่าพวกเขาควรจะทำงานแค่ไหน และล่าช้าได้เท่าไร หรือตัดสินใจว่าควรจะทำโปรเจคไหนต่อไป ผมยังคงมองตัวเองว่าเป็นอาร์คีเทคอยู่ และผมรักที่จะเขียนโค้ด แต่มันดีมากเลยที่ผมรู้เรื่องเทคโนโลยี หรือเรื่องแรงจูงใจของดีเวลลอปเปอร์ และยังมีส่วนสำคัญในการตัดสินใจว่าอะไรควรจะต้องทำหรือทำอย่างไรในที่สุด</p>
<p>ตัวอย่างเช่น เรามีโค้ดที่ค่อนข้างซับซ้อนที่เราเพิ่งเขียนขึ้นไม่นานมานี้ ด้วยความซับซ้อน(ที่จำเป็น) นั้นทำให้โค้ดค่อนข้างบอบบาง ถ้าอยู่ในฐานะที่เป็นดีเวลลอปเปอร์ ผมอาจจะรู้สึกขี้เกียจ หรืออาจจะไม่มีเวลามากพอที่จะเขียนยูนิทเทสให้ครอบลุมทั้งหมด ถ้าอยู่ในฐานะที่เป็นอาร์คีเทค ผมรู้ว่านั่นเป็นสิ่งที่เราต้องทำ แต่บ่อยครั้งที่ผมไม่สามารถกล่อมโปรเจคเมเนเจอร์ ให้จัดเวลาให้ดีเวลลอปเปอร์ได้เขียนยูนิทเทสนั้นได้ ตอนนี้ผมเป็นโปรเจคเมเนเจอร์ ที่สามารถอ่านตัวเลขค่า code coverage ได้อีกทั้งยังรู้เรื่องการใช้งานโปรดักของผู้ใช้อีกด้วย ผมก็เลยให้ดีเวลลอปเปอร์เขียนยูนิทเทสประมาณ 40 ตัว ครอบคลุมการประมวลผลลักษณะต่างๆทั้งหมด กินเวลาเกือบเดือน ถึงจะเสร็จเรียบร้อย (กระอักเลยทีเดียว) แต่กระบวนการนั้นก็ช่วยให้เราค้นพบจุดบกพร่องใหม่ 3 จุด ในที่สุดแล้ว เราได้จำกัดความเสี่ยงหลายๆอย่าง ที่เคยมีอยู่ในโค้ดส่วนนั้น เมื่อมีจุดบกพร่องในโปรแกรมอันใหม่พบจากการใช้งานจริงหรือมาจาก QA เราก็จะสามารถแก้ไขมัน และสั่งประมวลผลยูนิทเทสนั้นอีกครั้ง ได้อย่างรวดเร็ว เรายังได้เพิ่มยูนิทเทส สำหรับตรวจสอบจุดบกพร่อง ที่เราเพิ่งพบนั้นด้วย คราวนี้ในฐานะที่เป็นเมเนเจอร์ ผมรู้ว่าเมื่อเราประมวลผลยูนิสเทสแล้วไม่พบปัญหาใดๆ ถึงแม้ว่าโค้ดจะมีความซับซ้อนเพียงใด เราก็ยังอยู่ในสถานะที่ดีทีเดียวเมื่อมองในมุมของ การประเมินความเสี่ยงเทียบกับคุณภาพ แน่นอนว่ามันเป็นสิ่งที่ต้องชั่งส่วนได้ส่วนเสีย เราพบว่าเราต้องเขียนยูนิทเทสเพิ่มเติม สำหรับโค้ดส่วนนี้ (ประเมินจากการใช้งาน ของผู้ใช้โปรแกรมในช่วงทดลองใช้) แต่เรากำลังอยู่ในความกดดัน ในการทำโค้ดส่วนอื่นให้เสร็จ ภายในไม่กี่สัปดาห์ข้างหน้า ดังนั้นเราจึงชะลอเรื่องยูนิทเทสนี้ไว้ก่อน แล้วจึงจะกลับมาทำทีหลัง<br />
นอกจากนั้นมันยังมีส่วนดีเกี่ยวกับการจ้างงานอีกด้วย เมเนเจอร์น้อยเหลือเกิน ที่รู้วิธีดูดีเวลลอปเปอร์เก่งๆ อาศัยที่ผมเคยเป็นดีเวลลอปเปอร์ และมีประสบการณ์ไม่น้อย เกี่ยวกับคำถามในการคัดคน และเทคนิคในการสัมภาษณ์ ผมคิดว่าผมค่อนข้างเก่งทีเดียวในการชี้ตัวคนมีความสามารถ ผมมักจะทำการคัดเบื้องต้นก่อน เพื่อ ลูกทีมของผม จะได้ทำการสัมภาษณ์ผู้สมัครที่มีคุณภาพ ที่ไม่ได้ดีแต่พูดหรือมีคำหรูๆในใบสมัครงาน แต่ไม่มีความสามารถมารองรับเท่านั้น</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devguli.com/blog/translated/10things_pm_wish_developers_understood/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Reflections on Java Reflection</title>
		<link>http://www.devguli.com/blog/translated/reflections-on-java-reflection/</link>
		<comments>http://www.devguli.com/blog/translated/reflections-on-java-reflection/#comments</comments>
		<pubDate>Sun, 14 Sep 2008 15:50:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[translated]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=52</guid>
		<description><![CDATA[Translated from :  Reflections on Java Reflection
Author : Russ Olsen
Thanks to Russ who allow me to translate his blog here
Russ is also the author of Design Patterns in Ruby 

ในชีวิตปกติ รีเฟลคชัน (reflection) คือภาพสะท้อนในกระจก แต่ในโลกของการเขียนโปรแกรมแล้ว รีเฟลคชันคือการที่โปรแกรม สามารถรู้ถึงโครงสร้างของตัวเอง และยังสามารถปรับเปลี่ยนโครงสร้างนั้นได้อีกด้วย Java reflection API มอบหน้าต่างให้คุณมองเข้าไปสู่ ค่าพื้นฐานต่างๆของภาษา นั่นคือ คลาส, ฟิลด์และเมท็อด ผ่านทาง Java API ธรรมดาง่ายๆ
การเข้าใจ รีเฟลคชัน จะช่วยให้คุณ เข้าใจเครื่องมือต่างๆ ที่คุณใช้อยู่ประจำวันเช่น eclipseทำ [...]]]></description>
			<content:encoded><![CDATA[<p><b>Translated from : </b><a href="http://www.onjava.com/pub/a/onjava/2007/03/15/reflections-on-java-reflection.html"> Reflections on Java Reflection</a><br/><br />
<b>Author : <a href="http://www.jroller.com/rolsen/">Russ Olsen</a></b><br/><br />
Thanks to Russ who allow me to translate his blog here</p>
<p>Russ is also the author of <a href=" http://www.amazon.com/Design-Patterns-Ruby-Addison-Wesley-Professional/dp/0321490452/ref=sr_1_1?ie=UTF8&#038;s=books&#038;qid=1197033759&#038;sr=1-1">Design Patterns in Ruby </a><br />
<img src="http://farm3.static.flickr.com/2193/2092669277_8a2d862b4f.jpg?v=0" align="middle"/></p>
<p>ในชีวิตปกติ รีเฟลคชัน (reflection) คือภาพสะท้อนในกระจก แต่ในโลกของการเขียนโปรแกรมแล้ว รีเฟลคชันคือการที่โปรแกรม สามารถรู้ถึงโครงสร้างของตัวเอง และยังสามารถปรับเปลี่ยนโครงสร้างนั้นได้อีกด้วย Java reflection API มอบหน้าต่างให้คุณมองเข้าไปสู่ ค่าพื้นฐานต่างๆของภาษา นั่นคือ คลาส, ฟิลด์และเมท็อด ผ่านทาง Java API ธรรมดาง่ายๆ<br />
การเข้าใจ รีเฟลคชัน จะช่วยให้คุณ เข้าใจเครื่องมือต่างๆ ที่คุณใช้อยู่ประจำวันเช่น eclipseทำ auto-completion ของชื่อเมท็อดได้อย่างไร หรือ Tomcat สามารถสร้าง servlet ขึ้นมาใช้งานได้ จากเพียงแค่ชื่อของคลาสใน web.xml ได้อย่างไร และ Spring ควบคุมการทำ dependency injection ราวกับมายากลนั้นได้อย่างไร ด้วยรีเฟลคชันคุณสามารถเขียนโค้ดที่มีความยืนหยุ่น ได้มากกว่าระดับไดนามิค นั่นคือโปรแกรมของคุณ สามารถจัดการกับคลาส ที่มันไม่เคยเจอมาก่อนเลยได้<br />
<b>การเรียก Class มาใช้งาน</b><br />
ดังที่ผมได้พูดไปแล้วว่า แนวคิดพื้นฐานของรีแฟลคชัน คือการใช้ Java API ธรรมดาๆเข้าไปดูการทำงานข้างในของตัวโปรแกรม เนื่องจากสิ่งที่เป็นพื้นฐานที่สุดของจาวาคือคลาส (ลองเขียนจาวาโปรแกรมสักอันโดยไม่ต้องใช้คลาสดูสิ) ดังนั้นจุดเริ่มต้นที่ดีที่สุดก็คือการเรียนรู้คลาสที่ชื่อว่า “Class” ทุกคลาสในจาวาจะมีอ็อบเจกต์ตัวหนึ่งซึ่งเก็บข้อมูลทุกอย่างเกี่ยวกับคลาสนั้นไว้ อ็อบเจกต์นั้นเป็นอ็อบเจกต์ของคลาส Class คุณสามารถดึงเอาข้อมูลทุกอย่างของคลาสนั้นจากมันได้  (ต่อไปนี้จะขอเรียกอ็อบเจกต์ตัวนี้ว่า คลาสอ็อบเจกต์)เช่น ชื่อของคลาส หรือข้อมูลว่าคลาสนั้นเป็น public หรือ abstract หรือ final หรือไม่ หรือแม้กระทั่งข้อมูลของคลาสแม่ของมัน เรามาลองใช้รีเฟลคชันส่องดูคลาส Employee ง่ายๆอันนี้กัน</p>
<pre class="syntax-highlight:java">
package com.russolsen.reflect;

public class Employee
{
   public String _firstName;
   public String _lastName;
   private int _salary;

      public Employee(){
        this( &quot;John&quot;, &quot;Smith&quot;, 50000);
      }

   public Employee(String fn, String ln, int salary){
      _firstName = fn;
      _lastName = ln;
      _salary = salary;
   }

   public int getSalary(){
      return _salary;
   }

   public void setSalary(int salary){
      _salary = salary;
   }

   public String toString(){
      return &quot;Employee: &quot; + _firstName +  &quot; &quot;
             + _lastName + &quot; &quot; + _salary;
   }

}
</pre>
<p>วิธีที่ง่ายที่สุดในการนำเอาคลาสอ็อบเจกต์มาใช้ ก็คือเพียงแค่ใช้เมท็อด getClass เรียกเอาคลาสอ็อบเจกต์ของอ็อบเจกต์ตัวนั้นออกมา โค้ดข้างล่างนี้สร้างอ็อบเจคต์ของ Employee ดึงเอาคลาสอ็อบเจกต์ของมันออกมา และทำการแสดงค่าข้อมูลบางอย่างของคลาสนั้น</p>
<pre class="syntax-highlight:java">
package com.russolsen.reflect;
import java.lang.reflect.Modifier;

public class GetClassExample{

   public static void main(String[] args){

      Employee employee = new Employee();

      Class klass = employee.getClass();

      System.out.println( &quot;Class name: &quot; + klass.getName());
      System.out.println( &quot;Class super class: &quot; + klass.getSuperclass());

      int mods = klass.getModifiers();
      System.out.println( &quot;Class is public: &quot; + Modifier.isPublic(mods));
      System.out.println( &quot;Class is final: &quot; +  Modifier.isFinal(mods));
      System.out.println( &quot;Class is abstract: &quot; + Modifier.isAbstract(mods));
   }
}
</pre>
<p>ทำการสั่งประมวลผลโค้ดข้างบนนี้แล้วคุณจะเห็น:<br />
Class name: com.russolsen.reflect.Employee<br />
Class super class: class java.lang.Object<br />
Class is public: true<br />
Class is final: false<br />
Class is abstract: false</p>
<p>ดังที่เห็นในตัวอย่าง การดึงข้อมูลชื่อของคลาสและคลาสแม่นั้น ง่ายพอๆกับการเรียกเมท็อดสำหรับดึงข้อมูลธรรมดาๆ ถ้าคุณอยากจะรู้ว่าคลาสนั้นเป็น public หรือ abstract หรือ final กระบวนการจะยากขึ้นเล็กน้อย ข้อมูลเหล่านั้นจะถูกแสดงอยู่ในรูปของ int ตัวหนึ่งซึ่งได้มาจากการเรียกเมท็อด getModifiers และคุณสามารถใช้สแตติคเมท็อดทั้งหลายในคลาส Modifier เพื่อช่วยตีความค่าตัวเลขนั้น </p>
<p>การเรียกเมท็อด getClass นั้น ไม่ได้เป็นทางเดียวในการเรียกใช้งานคลาสอ็อบเจกต์ คุณยังสามารถเรียกเอาคลาสอ็อบเจกต์ออกมาโดยตรงจากชื่อคลาสนั้นก็ได้</p>
<p><b>Class klass = Employee.class;</b></p>
<p>วิธีที่สามในการดึงคลาสออกมาใช้งานนั้น น่าสนใจทีเดียว คุณสามารถสร้างอ็อบเจกต์ของ Class จากสตริงได้ ซึ่งสตริงนั้นก็ต้องมีค่าเป็นชื่อของคลาสด้วย วิธีการก็คือเรียกเมท็อด forName ของคลาส Class</p>
<pre class="syntax-highlight:java">
Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);

System.out.println( &quot;Class name: &quot; + klass.getName());
System.out.println( &quot;Class super class: &quot; + klass.getSuperclass());

// Print out the rest...
</pre>
<p>สิ่งหนึ่งที่ต้องจดจำไว้เสมอก็คือ คุณจะต้องใช้ชื่อเต็มของคลาส ซึ่งเป็นชื่อที่รวมชื่อของ package ของคลาสนั้นด้วย คุณไม่สามารถใช้แค่ชื่อ “Employee” คุณจะต้องใช้ “com.russolsen.reflect.Employee” จากการใช้งานเมท็อด forName นั้นเราก็เริ่มจะเห็นพลังความสามารถพื้นฐานของรีเฟลคชัน นั่นคือคุณสามารถใช้ชื่อของคลาสในสตริง เพื่อสร้างคลาสอ็อบเจกต์เอามาใช้งานได้</p>
<p><b>สร้างอินสแตนได้ในทันที</b><br />
การดึงเอาคลาสอ็อบเจกต์ออกมาเพื่อเรียกดูค่าต่างๆของมัน ก็มีความน่าสนใจ และมีประโยชน์ในตัวของมันเองอยู่แล้ว แต่การได้ ”ทำอะไรบางอย่าง” จริงๆกับคลาสนั้นเป็นสิ่งที่ทำให้รีเฟลคชัน เริ่มที่จะดูน่าตื่นเต้นขึ้น และสิ่งที่ชัดเจนที่สุดในการใช้งาน Class ก็คือการสร้างอินสแตนใหม่ของคลาสนั้น<br />
วิธีที่ง่ายที่สุดก็คือการใช้เมท็อด newInstance โปรแกรมข้างล่าง ได้แสดงตัวอย่าง การรับตัวแปรจากภายนอกโปรแกรม (ซึ่งควรจะมีค่าเป็นชื่อของคลาสๆหนึ่ง) สร้างคลาสจากค่านั้นแล้วสร้างอินสแตนใหม่จากคลาสนั้น</p>
<pre class="syntax-highlight:java">
package com.russolsen.reflect;

public class NewInstanceExample{
   public static void main(String[] args) throws ClassNotFoundException,
      		InstantiationException, IllegalAccessException {

      Class klass = Class.forName(args[0]);
      Object theNewObject = klass.newInstance();
      System.out.println(&quot;Just made: &quot; + theNewObject);
   }
}
</pre>
<p>ประมวลผลโค้ดข้างบนพร้อมกับส่งค่า “com.russolsen.reflect.Employee” เข้าไปในโปรแกรม และคุณก็จะได้อ็อบเจกต์ใหม่เอี่ยมของ Employee ออกมา</p>
<p><b>Just made: Employee: John Smith 50000</b></p>
<p>ส่งประมวลผลอีกครั้ง พร้อมกับใส่ค่า “java.util.Date” แล้วคุณจะได้</p>
<p><b> Just made: Tue Feb 27 20:25:20 EST 2007</b></p>
<p>ลองนึกถึงความยืดหยุ่นของโปรแกรมที่คุณได้ จากเพียงแค่โค้ดไม่กี่บรรทัด โปรแกรมข้างบนนั้นแทบไม่มีข้อมูลอะไรเลยเกี่ยวกับ Employee หรือ Date แต่ก็ยังสามารถสร้างอินสแตนจากทั้งสองคลาสได้ นี่เป็นวิธีใช้จาวาอีกแบบหนึ่ง</p>
<p><b>มากกว่าคอนสตรัคเตอร์ที่ไม่มี argument</b><br />
การเรียกเมท็อด Class.newInstance นั้นก็เหมือนกับการเรียกใช้ new แบบไม่ใส่ argument แต่จะเกิดอะไรขึ้นละ ถ้าคุณเรียก newInstance บนคลาสที่ไม่มีคอนสตรัคเตอร์ที่ไม่มี argument คำตอบคือคุณจะเจอกับ InstantiationException นะสิ<br />
ข่าวดีก็คือคุณสามารถสร้างอินสแตนของคลาสที่มี constructor แบบรับ argument ได้ เพียงแค่ต้องทำงานเพิ่มอีกนิดหน่อย สิ่งที่ต้องทำคือ หาคอนสตรัคเตอร์ที่คุณต้องการของคลาสนั้น และเรียกใช้มันพร้อมกับ ส่งค่า argument ที่ถูกต้องเข้าไป วิธีสำหรับค้นหาคอนสตรัคเตอร์ในฝันของคุณก็คือ การเรียกเมท็อด getConstructor พร้อมกับข้อมูลอธิบายลักษณะคอนสตรัคเตอร์ ที่คุณกำลังค้นหา สิ่งที่คุณจะได้กลับมาคืออ็อบเจกต์ของคลาส Constructor ซึ่งคุณสามารถนำไปใช้สร้างอินสแตนใหม่ได้<br />
มาดูกันว่าทั้งหมดนี้ทำงานอย่างไร</p>
<pre class="syntax-highlight:java">
      Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);

      Class[] paramTypes = {
            String.class,
            String.class,
            Integer.TYPE };

      Constructor cons = klass.getConstructor(paramTypes);

      System.out.println( &quot;Found the constructor: &quot; + cons);

      Object[] args = {
            &quot;Fred&quot;,
            &quot;Fintstone&quot;,
            new Integer(90000) };

      Object theObject = cons.newInstance(args);
      System.out.println( &quot;New object: &quot; + theObject);
</pre>
<p>ความแตกต่างอย่างเดียว ระหว่างคอนสตรัคเตอร์แต่ละตัว ก็คือพารามิเตอร์ที่มันรับ ดังนั้นวิธีที่คุณจะบอก getConstructor ว่าคุณกำลังมองหาคอนสตรัคเตอร์ตัวไหนอยู่ ก็คือการส่งอาเรย์ของคลาสเข้าไป โดยแต่ละคลาสในอาเรย์ จะแทนแต่ละพารามิเตอร์ในคอนสตรัคเตอร์ ตัวอย่างข้างบนนั้น ทำการค้นหาคอนสตรัคเตอร์ ที่รับค่าสตริง 2 ตัวกับ int หนึ่งตัว เมื่อคุณได้คอนสตรัคเตอร์แล้ว การสร้างอ็อบเจกต์ใหม่นั้นก็แสนง่าย เพียงแค่เรียกเมท็อด newInstance โดยส่งอาเรย์อีกหนึ่งอันเข้าไป คราวนี้อาเรย์นั้นจะบรรจุค่าของ argument </p>
<p>มีข้อควรระวังอย่างหนึ่ง ในการกำหนดชนิดของพารามิเตอร์ สำหรับคอนสตรัคเตอร์ที่คุณกำลังค้นหาอยู่ คุณควรจะระมัดระวัง ในการแยกความแตกต่างระหว่าง argument ที่เป็น primitive กับอ็อบเจคที่เป็น wrapper ของมัน ต้องดูให้แน่ว่าคอนสตรัคเตอร์นั้นรับ argument ที่เป็น int หรือเป็นอ็อบเจกต์ของคลาส java.lang.Integer ถ้าคุณกำลังมองหาคอนสตรัคเตอร์ที่รับ wrapper ของชนิดข้อมูล primitive เช่น  java.lang.Integer ก็ให้ใช้คลาสของ wrapper นั้นเลยเช่น Integer.class แต่ถ้าคุณต้องการคอนสตรัคเตอร์ที่รับค่า primitive int คุณจะต้องใช้ Integer.TYPE ซึ่งเป็นสิ่งที่ใช้แทนคลาสของ primitive int</p>
<p><b>ค้นลึกลงไปในคลาส</b><br />
ดังที่คุณได้เห็นจากตัวอย่างแรก อ็อบเจกต์ของ Class สามารถให้ข้อมูลพื้นฐานทุกอย่าง เกี่ยวกับคลาสแก่คุณได้ ข้อมูลเช่นชื่อ หรือคลาสที่เป็นคลาสแม่ แต่คุณสามารถทำได้มากกว่า การเรียกดูข้อมูลในระดับพื้นฐานเช่นชื่อหรืออันดับ หรือตัวเลข serial เหล่านี้ ตัวอย่างเช่น คุณสามารถที่จะค้นหาเมท็อดที่เป็น public ทั้งหมดในคลาสโดยใช้เมท็อด getMethods</p>
<pre class="syntax-highlight:java">
Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);

Method[] methods = klass.getMethods();

for(Method m : methods ){
         System.out.println( &quot;Found a method: &quot; + m );
}
</pre>
<p> getMethods จะคืนกลับอาเรย์ของอ็อบเจกต์ของคลาส Method แต่ละอันคือหนึ่งเมท็อดที่เป็น public ที่สามารถเรียกใช้ได้บนอินสแตนของคลาสนั้น</p>
<p>Found a method: public java.lang.String com.russolsen.reflect.Employee.toString()<br />
Found a method: public int com.russolsen.reflect.Employee.getSalary()<br />
Found a method: public void com.russolsen.reflect.Employee.setSalary(int)<br />
Found a method: public native int java.lang.Object.hashCode()<br />
Found a method: public final native java.lang.Class java.lang.Object.getClass()<br />
Found a method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException<br />
Found a method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException<br />
Found a method: public final void java.lang.Object.wait() throws java.lang.InterruptedException<br />
Found a method: public boolean java.lang.Object.equals(java.lang.Object)<br />
Found a method: public java.lang.String java.lang.Object.toString()<br />
Found a method: public final native void java.lang.Object.notify()<br />
Found a method: public final native void java.lang.Object.notifyAll()</p>
<p>เพราะว่าเมท็อด getMethods นั้นมองคลาสในมุมมองของผู้ใช้งานคลาสนั้น ดังนั้นอาเรย์จะบรรจุเมท็อดที่เป็น public ทั้งหมดที่ถูกประกาศไว้ในคลาสของมันเอง รวมไปถึงคลาสแม่และคลาสเหนือคลาสแม่ขึ้นไปเรื่อยๆ จนถึงคลาส Object</p>
<p>ถ้าคุณสนใจเมท็อดอันใดอันหนึ่งโดยเฉพาะ คุณสามารถใช้ getMethod (โปรดสังเกตุว่าชื่อเมท็อดอยู่ในรูปเอกพจน์) ซึ่งทำงานคล้ายกับ getConstructor เว้นแต่ว่าคุณจะต้องใส่ชื่อเมท็อดพร้อมกับชนิดของพารามิเตอร์ โค้ดข้างล่างนี้ทำการค้นหาเมท็อดชื่อ setSalary ซึ่งรับพารามิเตอร์เป็น int หนึ่งตัว</p>
<pre class="syntax-highlight:java">
Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);
Class[] paramTypes = {Integer.TYPE };
Method setSalaryMethod = klass.getMethod(&quot;setSalary&quot;, paramTypes);

System.out.println( &quot;Found method: &quot; + setSalaryMethod);
</pre>
<p>การเรียกใช้เมท็อดผ่านทางรีเฟลคชันนั้น คล้ายกันมากกับการเรียกคอนสตรัคเตอร์ คุณต้องการเพียงแค่อ็อบเจกต์ของคลาส Method ที่เราได้พูดไปแล้วก่อนหน้านี้ รวมไปถึงอาเรย์ของ argument ที่จะส่งเข้าไปในเมท็อด และตัวอินแสตนจริงๆ ที่บรรจุเมท็อดที่คุณกำลังจะเรียก โค้ดข้างล่างนี้เรียกใช้เมท็อด setSalary บนอ็อบเจกต์ Employee เพื่อทำการเพิ่มเงินเดือน</p>
<pre class="syntax-highlight:java">
     Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);

      Class[] paramTypes = {Integer.TYPE };
      Method setSalaryMethod = klass.getMethod(&quot;setSalary&quot;, paramTypes);

      Object theObject = klass.newInstance();
      Object[] parameters = { new Integer(90000) };

      setSalaryMethod.invoke(theObject, parameters);
</pre>
<p>ทำไมต้องมาเสียเวลากับสิ่งเหล่านี้ เพื่อแลกกับการพิมพ์แค่ theObject.setSalary(90000) ละ  อืม ลองดูโค้ดข้างบนอีกสักครั้ง นอกจากชื่อคลาสในบรรทัดแรกแล้ว โปรแกรมข้างบนนั้นมีความเป็น generic ในทั้งโปรแกรม คุณสามารถใช้มันเรียกเมท็อด setSalary ของอินสแตนใดๆบนคลาสใดก็ได้ ด้วยการปรับแต่งเล็กน้อย คุณก็จะสามารถใช้โค้ดข้างบนนั้น เรียกเมท็อดใดๆบนคลาสใดๆก็ได้</p>
<p><b>เล่นกับฟิลด์ (field) </b><br />
คุณไม่ได้ถูกจำกัดให้ใช้รีเฟลคชันจัดการได้เฉพาะเมท็อด คุณยังสามารถทำอะไรก็ได้กับฟิลด์อีกด้วย เราได้เห็นการใช้งาน getMethods ไปแล้ว การใช้งาน getFields นั้นก็คล้ายๆกันคือจะส่งกลับอาเรย์ของ Field แต่ละอ็อบเจกต์ในอาเรย์นั้น ก็คือแต่ละอินสแตนฟิลด์ (instance field) ที่อยู่ในคลาสนั้น รวมไปถึงอินสแตนฟิลด์ที่ถูกประกาศในคลาสแม่ของมัน</p>
<pre class="syntax-highlight:java">
Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);

System.out.println( &quot;Class name: &quot; + klass.getName());
Field[] fields = klass.getFields();

for(Field f : fields ){
    System.out.println( &quot;Found field: &quot; + f);
}
</pre>
<p>เนื่องจาก Employee มีฟิลด์แค่สองตัว ดังนั้นคุณจะได้อาเรย์ที่มีสมาชิกสองตัวกลับมา</p>
<p>Class name: com.russolsen.reflect.Employee<br />
Found field: public java.lang.String com.russolsen.reflect.Employee._firstName<br />
Found field: public java.lang.String com.russolsen.reflect.Employee._lastName</p>
<p>คุณสามารถที่เจาะจงดึงเอาฟิลด์อันใดอันหนึ่งมาด้วยชื่อโดยการใช้เมท็อด getField </p>
<p>Field field = klass.getField(&#8221;_firstName&#8221;);<br />
System.out.println(&#8221;Found field: &#8221; + field);</p>
<p>เมื่อคุณได้อ็อบเจกต์ของ Field แล้ว การดึงค่าของฟิลด์นั้นก็ง่ายพอๆกับการเรียกเมท็อด get และการกำหนดค่าให้กับฟิลด์นั้นก็เพียงแค่เรียกเมท็อด set</p>
<pre class="syntax-highlight:java">
      Object theObject = new Employee(&quot;Tom&quot;, &quot;Smith&quot;, 25);

      Class klass = Class.forName(&quot;com.russolsen.reflect.Employee&quot;);

      Field field = klass.getField(&quot;_firstName&quot;);

      Object oldValue = field.get(theObject);
      System.out.println( &quot;Old first name is: &quot; + oldValue);

      field.set( theObject, &quot;Harry&quot;);
      Object newValue = field.get(theObject);
      System.out.println( &quot;New first name is: &quot; + newValue);
</pre>
<p>ส่งประมวลผลโค้ดข้างต้นแล้วมองดูฟิลด์ _firstName เปลี่ยนค่าไปให้เห็นกับตาคุณ</p>
<p>Old first name is: Tom<br />
New first name is: Harry</p>
<p><b>แหกกฎ</b><br />
การศึกษาเกี่ยวกับรีเฟลคชันนั้น จะครบถ้วนไปไม่ได้ หากไม่ได้พูดถึงการฝ่าฝืนกฎศักสิทธิ์อันหนึ่งของจาวาทุกคนคงรู้ดีว่าไพรเวทเมท็อด (private method) นั้นไม่สามารถเข้าถึงได้จากภายนอกคลาส ที่ประกาศมันไว้ วิธีการใช้งานทั่วไปนั้นไม่สามารถทำได้ แต่กับรีเฟลคชันแล้ว คุณแทบจะทำอะไรได้ทุกอย่าง</p>
<p>สิ่งแรกที่คุณต้องการในการเรียกใช้ไพรเวทเมท็อดก็คืออ็อบเจกต์ Method ของเมท็อดที่คุณต้องการเรียก คุณไม่สามารถดึงมันออกมาได้จากการเรียก getMethod เพราะ getMethod จะส่งกลับเฉพาะเมท็อดที่เป็น public เท่านั้น วิธีที่คุณจะได้มาซึ่งไพรเวทเมท็อด(หรือ protected) ก็คือการใช้ getDeclaredMethod  ในขณะที่ getMethod มองคลาสในมุมมองของผู้ใช้งาน และส่งกลับเฉพาะเมท็อดที่เป็น public แต่ getDeclaredMethod จะส่งกลับทุกเมท็อดที่ถูกประกาศไว้ในคลาสนั้น ตัวอย่างข้างล่างเราใช้ getDeclaredMethod ในการดึงไพรเวทเมท็อด removeRange ของคลาส java.util.ArrayList </p>
<pre class="syntax-highlight:java">
ArrayList list = new ArrayList();
list.add(&quot;Larry&quot;);
list.add(&quot;Moe&quot;);
list.add(&quot;Curley&quot;);

System.out.println(&quot;The list is: &quot; + list);

Class klass = list.getClass();

Class[] paramTypes = { Integer.TYPE, Integer.TYPE };
Method m = klass.getDeclaredMethod(&quot;removeRange&quot;, paramTypes);

Object[] arguments = { new Integer(0), new Integer(2) };
m.setAccessible(true);
m.invoke(list, arguments);
System.out.println(&quot;The new list is: &quot; + list);
</pre>
<p>เมื่อคุณได้ไพรเวทเมท็อดมาแล้ว การจะเรียกใช้มันได้ก็เพียงแค่ปิดการป้องกันสุดท้ายโดยการเรียก setAccessable</p>
<p>The list is: [Larry, Moe, Curley]<br />
The new list is: [Curley] </p>
<p>เมท็อด removeRange จะทำการดึงเอาไอเทมในช่วงหนึ่งออกจากลิสต์ นี่อาจจะออกแนวไสยศาสตร์วูดูเลยทีเดียว คุณพึ่งจะเข้าไปเรียกไพรเวทเมท็อดของ java.util class แต่ถามว่าคุณอยากจะสร้างนิสัยในการเขียนโค้ดที่คลุมเครือ อย่างการเรียกใช้งานไพรเวทเมท็อดแบบนี้ไหมละ ก็คงไม่หรอก แต่อย่างน้อยมันก็เป็นสิ่งที่รู้กัน ว่ามีประโยชน์ ในบางสถาณการณ์ที่จำเป็นจริงๆ</p>
<p><b>สรุป</b><br />
รีเฟลคชันอนุญาตให้โปรแกรมของคุณ ทำในสิ่งที่เหมือนจะไม่เป็นไปตากกฎของจาวา คุณสามารถเขียนโค้ด เพื่อหาข้อมูลทุกอย่างของคลาสหนึ่งๆ ที่โค้ดของคุณเพิ่งเจอเป็นครั้งแรก และเรียกใช้งานข้อมูลที่เพิ่งจะค้นพบในคลาสใหม่นั้นได้ทันที เช่นมันสามารถสร้างอินสแตนไหม่ หรือเรียกใช้เมท็อด และดึงค่าหรือกำหนดค่าให้กับฟิลด์  คุณสามารถทำได้แม้กระทั่ง เล่นเล่นกับข้อมูลที่เป็นไพรเวทข้างในคลาส ความเข้าใจที่ดีในรีเฟลคชันเป็นสิ่งที่สำคัญในการทำความเข้าใจ เครื่องมือที่ซับซ้อนบางอย่าง ในโลกของจาวา และมันก็เป็นสิ่งที่คุณต้องการ ในการเขียนโปรแกรมที่ทำได้มากกว่าโปรแกรมจาวาทั่วไป</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devguli.com/blog/translated/reflections-on-java-reflection/feed/</wfw:commentRss>
		</item>
		<item>
		<title>คุณว่าอยากจะเขียนยูนิตเทสรึ</title>
		<link>http://www.devguli.com/blog/translated/so-you-wanna-write-a-unit-test/</link>
		<comments>http://www.devguli.com/blog/translated/so-you-wanna-write-a-unit-test/#comments</comments>
		<pubDate>Sun, 14 Sep 2008 15:37:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[translated]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.devguli.com/blog/?p=47</guid>
		<description><![CDATA[เรื่อง : คุณอยากจะเขียนยูนิตเทสอย่างนั้นรึ
Translated from :  So You Wanna Write A Unit Test
Author : Russ Olsen
Thanks to Russ who allow me to translate his blog here
Russ is also the author of Design Patterns in Ruby 

ผมเห็นนักพัฒนา (developers) หลายๆคน มองการเขียนยูนิตเทส ในทำนองเดียวกับที่ นักบุญออกัสตินคิดต่อการสำรวมในกาม: ข้าแต่พระเจ้า ประทานพลังแก่ข้าในการเขียนยูนิตเทสเถิด, แต่ยังก่อนอย่าพึ่งเป็นตอนนี้. เรารู้ดีว่าเราควรจะเขียนยูนิตเทส แต่เราก็คอยแต่จะผัดผ่อนมันเรื่อยมา ต่อไปนี้เป็น 5 ประการที่พึงท่องไว้ในใจ  ขณะที่คุณพยายามสร้างความมุ่งมั่นในการเขียนเทส
1. อย่าเสียเวลาไปทั้งวัน
ยูนิตเทสที่ใช้เวลาประมวลผลหนึ่งชั่วโมง จะถูกประมวลผลอย่างมากสุด ชั่วโมงละหนึ่งครั้ง นี่ไม่ได้ล้อเล่นนะ [...]]]></description>
			<content:encoded><![CDATA[<p><b>เรื่อง : คุณอยากจะเขียนยูนิตเทสอย่างนั้นรึ</b><br />
<b>Translated from : </b><a href="http://www.jroller.com/rolsen/entry/so_you_wanna_write_a"> So You Wanna Write A Unit Test</a><br/><br />
Author : <a href="http://www.jroller.com/rolsen/">Russ Olsen</a></b><br/><br />
Thanks to Russ who allow me to translate his blog here</p>
<p>Russ is also the author of <a href=" http://www.amazon.com/Design-Patterns-Ruby-Addison-Wesley-Professional/dp/0321490452/ref=sr_1_1?ie=UTF8&#038;s=books&#038;qid=1197033759&#038;sr=1-1">Design Patterns in Ruby </a><br />
<img src="http://farm3.static.flickr.com/2193/2092669277_8a2d862b4f.jpg?v=0" align="middle"/></p>
<p>ผมเห็นนักพัฒนา (developers) หลายๆคน มองการเขียนยูนิตเทส ในทำนองเดียวกับที่ นักบุญออกัสตินคิดต่อการสำรวมในกาม: ข้าแต่พระเจ้า ประทานพลังแก่ข้าในการเขียนยูนิตเทสเถิด, แต่ยังก่อนอย่าพึ่งเป็นตอนนี้. เรารู้ดีว่าเราควรจะเขียนยูนิตเทส แต่เราก็คอยแต่จะผัดผ่อนมันเรื่อยมา ต่อไปนี้เป็น 5 ประการที่พึงท่องไว้ในใจ  ขณะที่คุณพยายามสร้างความมุ่งมั่นในการเขียนเทส</p>
<p><b>1. อย่าเสียเวลาไปทั้งวัน</b><br />
ยูนิตเทสที่ใช้เวลาประมวลผลหนึ่งชั่วโมง จะถูกประมวลผลอย่างมากสุด ชั่วโมงละหนึ่งครั้ง นี่ไม่ได้ล้อเล่นนะ ยูนิตเทสที่ใช้เวลาประมวลเป็นชั่วโมง จะถูกใช้งานหนึ่งหรือสองครั้งต่อสัปดาห์ถ้าคุณโชคดี ถ้าคุณอยากให้นักพัฒนาใช้งานยูนิตเทสของคุณละก็ มันจำเป็นจะต้องเร็ว และสามารถประมวลผลอย่างอัตโนมัติ และมันต้องไม่กินเวลาในการเตรียมการ (setup) สองชั่วโมงหรือแม้แต่แค่สองนาที</p>
<p>อย่าเข้าใจผมผิด เทสที่ใช้เวลานานในการประมวลผลเป็นสิ่งที่ดี ความจริงมันยอดเยี่ยมเลยละ เทสที่ใช้เวลานานที่กระหน่ำใช้งานฐานข้อมูล หรือเทสที่ต้องการเซิร์ฟเวอร์ทุกตัวในการทำงาน และเทสที่ใช้ประสิทธิภาพของระบบจนหยดสุดท้าย ต่างก็ยอดเยี่ยมทั้งนั้น เพียงแต่ว่ามันไม่ใช่ยูนิตเทสเท่านั้นเอง ยูนิตเทสความจะประมวลผลอย่างรวดเร็ว ด้วยการเตรียมการที่นักพัฒนาทุกคนมี มันคือแนวป้องกันแรกของคุณ เพื่อที่มันจะถูกใช้งานอย่างได้ผลละก็ มันจะต้องถูกใช้งานบ่อยๆครั้ง</p>
<p><b>2.ขอความเงียบด้วยครับ</b><br />
เอาละ มันอาจะเป็นเฉพาะผมคนเดียวก็ได้ ที่เกลียดยูนิตเทสที่จู้จี้จุกจิก ตอนผมใช้งานเทสอันใดอันหนึ่ง ผมต้องการแค่คำตอบง่ายๆสำหรับคำถามง่ายๆ: ใช้งานได้ หรือใช้งานไม่ได้. ผมไม่ได้อยากรู้ว่าตอนนี้มีกี่อีเลเมนต์ในลิสต์ หรือคุณกำลังเข้าทำงานที่เมธอดนั้นเมธอดนี้ และผมไม่ได้ต้องการความช่วยเหลือ ว่าวันนี้เป็นวันที่เท่าไรแน่ๆ</p>
<p>ถ้าหากเทสนั้นไม่ผ่านผมก็จะพึ่ง debugger เพื่อนยาก หรือใส่คำสั่งบันทึกการทำงาน (logging) ของผมเอง. ผมมันคนง่ายๆประเภทแค่ ใช่ หรือ ไม่ใช่</p>
<p><b>3.เลิกดักจับ exception. </b><br />
คำถาม : อะไรคือข้อแตกต่างระหว่าง JUnit เทส นี้ : </p>
<pre class="syntax-highlight:java">
public void testWidget() throws WidgetException {
    Widget w = new Widget();
    w.doSomething();
}
</pre>
<p>กับเทสนี้</p>
<pre class="syntax-highlight:java">
public void testWidget() {
    try {
      Widget w = new Widget();
      w.doSomething();
    }
    catch( WidgetException we) {
      fail(&quot;widget failed&quot;, we );
    }
}
</pre>
<p>ข้อแตกต่างก็คือ, WidgetException ควรจะถูกโยนออกไปหรือไม่, โค้ดอันแรกจะประมวลผลไม่ผ่านพร้อมๆกับแสดง stack trace ชี้ไปยังจุดที่ประมวลผมผิดพลาด โค้ดอันที่สองจะเกิด exception บอกว่าเทสนั้นไม่ผ่านเหมือนกัน แต่มันจะแสดง stack trace ชี้กลับไปยังคำสั่ง fail()  ข้อมูลจริงๆที่คุณต้องการเพื่อจะรู้ว่าเกิดอะไรขึ้นกันแน่ ถูกฝังไว้ที่ไหนสักแห่งหนึ่งใน stack trace ที่ซ้อนไว้อีกชั้น พูดง่ายๆคือตัวอย่างแรกนั้นไม่เพียงแต่จะกระชับกว่า แต่ยังทำงานดีกว่าอีกด้วย</p>
<p>ถ้าคุณมีการดักจับ exception อย่างนี้เยอะไปหมด ในเทสของคุณละก็  คุณช่างพยายามเหลือเกิน เพื่อให้มั่นใจว่าความสนใจของทุกๆคน จะถูกนำพาไปยังจุดหมายที่ผิดไป นักมายากลมีคำเฉพาะ สำหรับเรียกสิ่งนี้ว่า การเบี่ยงเบนความสนใจ นักมายากลชอบที่จะเบี่ยงเบนความสนใจ พวกเขาพยายามจะทำให้รู้สึกไม่แน่ชัด ว่าอะไรเกิดขึ้นกันแน่ ทำให้คุณหันไปมีเป้าหมายอื่นในใจ</p>
<p>โปรดจำว่าถ้าหากไม่ใช่เทสเกี่ยวกับการจัดการ exception (exception handling) ที่ซับซ้อน เมื่อใดที่เทสโยนส่ง exception ออกไปเมื่อนั้นงานของคุณได้จบลงแล้ว เก็บเอาความกระตือรือล้นในการจัดการ exception สำหรับครั้งต่อไป ในคราวที่มันจะทำให้โปรแกรมของคุณดีขึ้น</p>
<p><b>4. อย่าเขียน “สงครามและสันติภาพ (<a href="http://en.wikipedia.org/wiki/War_and_Peace">War And Peace</a>)” ของเทสเมธอด</b><br />
ทำไมทุกครั้งเมื่อวิศวกรบางคนเริ่มต้นเขียนยูนิตเทส เขาจะต้องย้อนเวลากลับไป เมื่อตอนราตรีฉลองจบการศึกษา (prom night) โปรแกรมเมอร์ธรรมดา ท่าทางเรียบร้อยคนหนึ่ง ผู้ไม่เคยคิดจะเขียนเมธอดยาวถึงขนาด 60 บรรทัด มาก่อนเลยในชีวิต กลับมึนเมาพ่นเอา เทสเมธอดยาว 1700  บรรทัดออกมา แบบไม่มีคอมเมนต์ ไม่มีโครงสร้าง ไม่เห็นแนวความคิด จริงที่ข้อกำหนดต่างๆย่อมแตกต่างออกไปเมื่อคุณเขียนยูนิตเทส ไม่, คุณไม่ต้องทำตามข้อตกลงไปเสียทั้งหมด และก็ไม่ต้องระวังไปเสียทุกอย่าง. แต่ขอเพียงแค่สงสารพวกเราคนที่ต้องมานั่งจัดการกับสิ่งที่คุณทำไว้บ้าง</p>
<p>ถ้าคุณเขียนเทสเมธอดขนาดใหญ่ ที่ไม่มีแบบแผนอะไรเลย แล้วสามารถประมวลผลได้ปกติ มันก็เยี่ยม แต่ถ้าหากมันเทสไม่ผ่านละ จุดผิดพลาดที่บรรทัด 1543 มักจะหายากกว่าจุดผิดพลาดที่ 43</p>
<p><b>5.อย่าล้มเลิกความตั้งใจ</b><br />
ไปที่ร้านหนังสือที่ไหนก็ได้ หรือค้นหาในเวบไซท์ทางวิศวกรรมซอฟแวร์ ที่เวบไหนก็ได้ มันไม่เป็นการยากเลยที่จะเจอคำพูดเป็นพันๆคำ เกี่ยวกับการเขียนยูนิตเทสให้ครอบคลุมทุกด้าน สำหรับทุกๆ เส้นทางการประมวลผล (execution code path)</p>
<p>ในความเป็นจริงบางครั้งไม่มีเวลามากพอ หรือไม่มีความเข้าใจในโค้ดที่เราพยายามจะเทส  จะทำอย่างไรละ? อืม ก็เขียนยูนิตเทสอะไรก็ได้ เท่าที่คุณจะเขียนได้นั่นละ. อย่างน้อยที่สุด เขียนแค่ยูนิตเทสที่ใช้งานโค้ดสักนิดหน่อย 