วันก่อน ระหว่างที่รอรถเมล์กลับบ้าน
ผมเหลือบไปเห็นคนตาบอด 2 คน กำลังรอรถเมล์อยู่
เป็นผู้ชายคน กับผู้หญิงคน
ผู้หญิงลงไปยืนในถนน 1 ก้าว
แวบแรกที่เห็น ก็เกิด บทสนทนาในใจ
"เอ๊ะใช่คนตาบอดหรือเปล่า"
"แต่เขามีไม้เท้าทั้งคู่นี่ ต้องเป็นคนตาบอดแน่เลย"
แวบที่สอง ก็ตามมา
"เราควรจะเข้าไปช่วยเขาดีหรือเปล่า?"
"เขามีใครคอยช่วยอยู่หรือยัง?"
หลังจากปล่อยให้เกิดบทสนทนาในใจอยู่พักหนึ่ง
ผมก็เลยเดินเข้าไปช่วยพาขึ้นรถ
(สำหรับคนที่ไม่เคยพาคนตาบอดเดิน
ขอแนะนำให้ใช้วิธี ให้เขาจับแขนบริเวณข้อศอกเรา
วิธีนี้เพื่อนตาบอดคนหนึ่งของผม เขาแนะนำมา)
ระหว่างที่รอรถเมล์กับเขา
ผมก็รู้สึกว่าเขาช่างเป็นคนที่มีความอดทนเสียเหลือเกิน
ของเรา ขนาดมองเห็น เวลาที่รถเมล์ขาดระยะ
มองไปสุดลูกหูลูกตา แล้วยังไม่เห็นรถเมล์สักคน
เรายังรู้สึกหงุดหงิดเลย แต่นี่เขาต้องรอ 2 ทอด
ทอดที่ 1 รอให้คนมาช่วย ซึ่งไม่รู้ว่าจะมีคนมาช่วยเมื่อไร
ทอดที่ 2 รอรถเมล์มา (ซึ่งมองไม่เห็นอีกต่างหาก)
ดังนั้นในระหว่างที่รอรถเมล์ ผมก็เลยพูดบรรยายให้เขาฟังไปด้วย
ว่าตอนนี้รถเมล์สายนี้เข้านะ สายนี้มาแล้วแต่หมดระยะแล้ว(พวกป้ายแดง)
หลังจากพาส่งขึ้นรถแล้ว
ระหว่างนั่งรถกลับมาบ้าน
ก็นึกในใจ คนอื่นๆที่อยู่ป้ายรถเมล์มองเห็นคนตาบอดนี้หรือไม่
แล้ว ทำไมไม่มีคนเข้าไปช่วยเขาเลย
ผมคิดว่า คนกลุ่มหนึ่งที่ป้ายรถเมล์นั้น
มองไม่เห็นคนตาบอด
คนที่มีบทสนทนาภายในกับตัวเองตลอดเวลา
จะมีข้อจำกัดในการเห็นโลกภายนอก
(ข้อนี้ยกตัวอย่างให้เห็นได้จาก คนที่เดินไป
คุยโทรศัพท์มือถือไป ถ้าเราสังเกตอิริยาบทเขา จะเห็นว่าเ
ขาจะเดินตัวลอยๆ ,สายตาจะไม่ focus จับที่ไหนเป็นหลัก
ถ้ามองให้ดี จะเห็นว่า สายตาของเขาบ่งบอกว่า
เขากำลังอยู่ในโลกของการสนทนา)
คนกลุ่มนี้ก็อาจจะกำลังครุ่นคิดถึงปัญหาของตนเองอยู่
(เรื่องงาน, เรื่องแฟน, เรื่องเงิน, ...)
ส่วนอีกกลุ่มหนึ่งมองเห็น
แต่ไม่เกินความคิดเชิงคำถามที่ว่า "แล้วเขาจะขึ้นรถเมล์อย่างไร"
ทำให้ไม่ได้ประเมินต่อไปว่า ตนเองควรจะเข้าไปช่วยเขาดีหรือไม่
คนกลุ่มนี้น่าจะเป็นคนที่ขาด ความสามารถในการคิดในมุมมองของผู้อื่น
อีกกลุ่มหนึ่ง ก็น่าจะเป็นพวกที่มองเห็น แต่ไม่กล้า
กรณีแบบนี้ก็น่าจะมีเยอะพอสมควรนะ
พวกนี้น่าจะคิดว่า การเดินออกไปให้ความช่วยเหลือคนตาบอด
เหมือนกับการเดินออกไปหน้าเวที ที่มีคนคอยจับตามอง
(กรณีนี้เขาคิดไปเองว่า ทุกคนมองเห็นคนตาบอดนั้น
และกำลังให้ความสนใจ คอยมองไปที่คนตาบอดนั้นอยู่)
อีกกลุ่มก็อาจจะเป็นพวกที่
รู้ว่าตัวเองเข้าไปช่วยเขาได้
แต่มีเหตุผลว่า กำลังรีบอยู่
ให้คนที่มีเวลา เป็นคนช่วยดีกว่า
....
ข้อสงสังของผมก็คือ
ในวันนั้น กลุ่มไหนมีปริมาณสัดส่วนเป็นอย่างไร
น่าจะมีใครทำการทดลองเรื่องนี้บ้างนะ
ไม่ใช่ด้วยการสัมภาษณ์นะ เพราะคนเราไม่ตอบ
ตามที่เป็นจริง
แต่ให้ทดลองให้คนตาบอดไปยืนรอรถเมล์
แล้วจับเวลาดูว่าต้องใช้เวลาเท่าไรจึงจะมีคนมาให้ความช่วยเหลือ
ตัวเลขนี้น่าจะเอาไปเป็นส่วนหนึ่งของ "ดัชนีคุณภาพชีวิต" ได้
นึกถึงเรื่องการทดลองนี้แล้วนึกถึง
การทดลองที่ Reader digest เคยทำ
เขาทดลองวางกระเป๋าสตางค์ (ที่มีที่อยู่เขียนไว้ข้างใน) ทิ้งไว้ตามที่ต่างๆ
แล้วแอบดูว่าคนที่เก็บมีลักษณะท่าทางอย่างไร
จากนั้นก็คอยติดตามว่า มีการติดต่อหรือส่งคืนกระเป๋าสตางค์หรือไม่
Reader digest เขาทดลองกับหลายๆประเทศใน Asia
ผลที่ได้ค่อนข้างตรงกันอย่างหนึ่งก็คือ
ถ้าคนที่เก็บได้เป็นคนที่ฐานะไม่ค่อยดี
มันจะได้กระเป๋าพร้อมเงินคืน
ส่วนกรณีที่คนเก็บได้ดูดีมีฐานะ
ก็จะไม่ได้กระเป๋าคืน
ที่ติดใจก็คือ ที่เมืองไทย
มีกระเป๋าใบหนึ่งถูกทดลองทิ้งไว้ใน มหาวิทยาลัยจุฬาฯ
ผลเป็นไปตามที่คาด
กระเป๋านั้นไม่ได้ถูกพบเห็นอีกเลย
(คนเก็บได้เป็นนิสิต)
สำหรับตัวผมเอง
ผมคิดว่าทั้งเรื่องคนตาบอดหรือเรื่องกระเป๋าสตางค์
มีรากเหง้าตัวหนึ่งที่ตรงกันก็คือ "Ignorance"
..
จบดีกว่า ปัญหาพวกนี้คุยกับข้ามคืนก็ไม่จบ
Saturday, September 17, 2005
Monday, September 12, 2005
Hierarchical Data in Database
ปกติผมจะเก็บ tree structure ใน db ด้วย schema แบบนี้
ซึ่งก็ ok แหล่ะ แต่การท่อง tree หรือหา path ของ tree ค่อนข้างกิน io ไปนิด
ซึ่งปกติที่ทำก็คือ load ทั้งหมดมา build เป็น graph structure แล้วเก็บ
มันไว้ใน cache
วันนี้ไปอ่านเจอที่ Gijs Van Tulder เขียนไว้ ในเรื่อง
Storing Hierarchical Data in a Database
ก็เลยได้เปิดหูเปิดตา ว่ามันมี technique อื่นที่ช่วยไม่ให้
การ select หา subtree หรือ path สามารถทำได้ใน select เดียว
ดยเขาจะสร้าง table ให้มี structure แบบนี้
ก็คือนอกจากจะมีการเก็บ parent id แล้ว
ก็ยังเก็บค่า left, right ไว้ด้วย
ซึ่งค่านี้ได้จากการ generate โดยการท่อง tree
แบบ Depth-First search
เวลาจะ select หา subtree ก็แค่
ค่า x และ y ก็คือ ค่า left_id, right_id ของ node ที่ต้องการหา subtree
...
มีที่น่าสนใจอีกเยอะเลย เช่นจะ select หา path ยังไร,
ผลลัพท์ที่ได้จะสร้างเป็น tree ได้อย่างไร,
การ add note เข้าไปใหม่ จะต้อง update ค่า left, right อย่างไร
ลองไปอ่านดูแล้วกันครับ
Note: อ่านแล้วก็อดนึกถึงคำพูดที่ว่า พวกเด็กที่เรียกไฟฟ้าเขียนโปรแกรมกันทื่อๆ
ก็ถูกของเขาแหล่ะ เพราะเราไม่ได้เรียนมาทาง computer โดยตรง
ก็เลยไม่ได้ผ่านหูผ่านตากับพวก algorithm แบบนี้
create table foo (
id long,
....
parent_id long
)
ซึ่งก็ ok แหล่ะ แต่การท่อง tree หรือหา path ของ tree ค่อนข้างกิน io ไปนิด
ซึ่งปกติที่ทำก็คือ load ทั้งหมดมา build เป็น graph structure แล้วเก็บ
มันไว้ใน cache
วันนี้ไปอ่านเจอที่ Gijs Van Tulder เขียนไว้ ในเรื่อง
Storing Hierarchical Data in a Database
ก็เลยได้เปิดหูเปิดตา ว่ามันมี technique อื่นที่ช่วยไม่ให้
การ select หา subtree หรือ path สามารถทำได้ใน select เดียว
ดยเขาจะสร้าง table ให้มี structure แบบนี้
create table foo (
id long,
..
parent_id long,
left_id long,
right_id long
)
ก็คือนอกจากจะมีการเก็บ parent id แล้ว
ก็ยังเก็บค่า left, right ไว้ด้วย
ซึ่งค่านี้ได้จากการ generate โดยการท่อง tree
แบบ Depth-First search
เวลาจะ select หา subtree ก็แค่
select * from foo where left_id between x and y
ค่า x และ y ก็คือ ค่า left_id, right_id ของ node ที่ต้องการหา subtree
...
มีที่น่าสนใจอีกเยอะเลย เช่นจะ select หา path ยังไร,
ผลลัพท์ที่ได้จะสร้างเป็น tree ได้อย่างไร,
การ add note เข้าไปใหม่ จะต้อง update ค่า left, right อย่างไร
ลองไปอ่านดูแล้วกันครับ
Note: อ่านแล้วก็อดนึกถึงคำพูดที่ว่า พวกเด็กที่เรียกไฟฟ้าเขียนโปรแกรมกันทื่อๆ
ก็ถูกของเขาแหล่ะ เพราะเราไม่ได้เรียนมาทาง computer โดยตรง
ก็เลยไม่ได้ผ่านหูผ่านตากับพวก algorithm แบบนี้
Related link from Roti
Commons CLI
วันนี้เขียนโปรแกรม utility เล็กๆ ที่สามารถ run จาก command line ได้
ก็เลยมองหา api ที่ช่วย parse argument
Commons CLI ของ jakarta เป็น api ที่ช่วยในการ parse argument
โดยมีวิธีการใช้ดังนี้
ก็เลยมองหา api ที่ช่วย parse argument
Commons CLI ของ jakarta เป็น api ที่ช่วยในการ parse argument
โดยมีวิธีการใช้ดังนี้
- ระบุ Options ที่โปรแกรมเรารับได้
Options opt = new Options();
opt.addOption("d", true, "extract only specific date");
opt.addOption("w", true, "extract only week that contain given date");
opt.addOption("m", true, "extract for specific month,year");
parameter ตัวที่ 2 ที่เรา pass ให้ method addOption ก็คือ การระบุว่า
option ตัวนี้ต้องมี argument ตามมาด้วยหรือไม่
อย่างในตัวอย่างที่เขียนนี้ ผู้ใช้สามารถเรียกใช้โปรแกรมได้ดังนี้program -d yymmdd -w yymmdd -y yymm infile outfile
กรณีที่เราต้องการให้เลือกเฉพาะ option ใด option หนึ่งเท่านั้น
ก็สามารถเขียนได้ดังนี้Options opt = new Options();
OptionGroup grp = new OptionGroup();
grp.addOption(new Option("d", true, "extract only specific date"));
grp.addOption(new Option("w", true, "extract only week that contain given date")
);
grp.addOption(new Option("m", true, "extract for specific month,year"));
opt.addOptionGroup(grp);
ก็คือเราเปลี่ยนไปใช้ Optiongroup ในการ control ไม่ให้มีการเลือก
มากกว่า 1 choice พร้อมๆกัน - ทำการ parse String[] args โดยระบุ parser ซึ่งเลือกได้ 3 แบบคือ BasicParser, GnuParser, PosixParser
PosixParser parser = new PosixParser();
CommandLine cmdline = parser.parse(opt, args); - เลือกใช้งาน argument หรือ option ผ่านทาง method
getOptionValue
หรือgetArgs()CommandLine cmdline = parser.parse(opt, args);
File inFile = new File(cmdline.getArgs()[0]);
if (cmdline.hasOption("d")) {
Calendar[] ranges = parseDate(cmdline.getOptionValue("d"));
...
}
if (cmdline.hasOption("w")) {
filter.setWeek(parseWeek(cmdline.getOptionValue("w")));
}
Related link from Roti
Hibernate Generic DAO + JDK5.0
อ่านเจอใน blog ของ Hibernate Link
เป็น idea ที่ดีมาก ใช้แต่ 1.4 จนลืมนึกถึง feature generic ของ 1.5 ไปเลย
ตัวอย่างที่เขาใช้
เป็น idea ที่ดีมาก ใช้แต่ 1.4 จนลืมนึกถึง feature generic ของ 1.5 ไปเลย
ตัวอย่างที่เขาใช้
public interface GenericDAO<T, ID extends Serializable> {
T findById(ID id, boolean lock);
List<T> findAll();
List<T> findByExample(T exampleInstance);
T makePersistent(T entity);
void makeTransient(T entity);
}
public interface ItemDAO extends GenericDAO<Item, Long> {
public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";
Bid getMaxBid(Long itemId);
Bid getMinBid(Long itemId);
}
Related link from Roti
Sunday, September 11, 2005
Eclipse RCP & ClassLoader & Spring
เมื่อก่อนที่เคยเขียน RCP เคยพยายาม integrate
SpringFramework เข้ามาใช้ แต่ติดปัญหาว่า
เรื่องการ load class
โดย spring จะเรียกใช้ current ClassLoader โดยวิธี
(Hibernate ก็ใช้วิธีนี้เหมือนกัน เข้าใจว่า framework ที่เกิดกับ
serverside นิยมใช้วิธีนี้กัน)
ซึ่งเมื่อนำมาใช้กับ Eclipse จะเกิดปัญหาทันที
เนื่องจาก classloader ที่ได้ มันจะเป็นคนละตัวกับ plugin ที่เราเขียน
ทำให้มองไม่เห็น class ที่เรา provide ให้
วิธีแก้ที่เห็นคนอื่นทำ ก็คือเขาจะ override ClassLoader ของ current thread
ชั่วคราว โดยทำ ณ ขณะที่ทำการ load ApplicationContext
Note: คิดว่าวิธีนี้ยังไม่ support กรณีเราออกแบบให้มีการ merge
spring config file จากหลายๆ plugin เข้ามารวมที่ context เดียว
SpringFramework เข้ามาใช้ แต่ติดปัญหาว่า
เรื่องการ load class
โดย spring จะเรียกใช้ current ClassLoader โดยวิธี
Thread.currentThread().getContextClassLoader();
(Hibernate ก็ใช้วิธีนี้เหมือนกัน เข้าใจว่า framework ที่เกิดกับ
serverside นิยมใช้วิธีนี้กัน)
ซึ่งเมื่อนำมาใช้กับ Eclipse จะเกิดปัญหาทันที
เนื่องจาก classloader ที่ได้ มันจะเป็นคนละตัวกับ plugin ที่เราเขียน
ทำให้มองไม่เห็น class ที่เรา provide ให้
วิธีแก้ที่เห็นคนอื่นทำ ก็คือเขาจะ override ClassLoader ของ current thread
ชั่วคราว โดยทำ ณ ขณะที่ทำการ load ApplicationContext
public void start(BundleContext context) throws Exception {
super.start(context);
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
applicationContext = new ClassPathXmlApplicationContext(SPRING_CONFIGS)
;
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
Note: คิดว่าวิธีนี้ยังไม่ support กรณีเราออกแบบให้มีการ merge
spring config file จากหลายๆ plugin เข้ามารวมที่ context เดียว
Related link from Roti
fvLogger, JavaScript Logging
อ่านเจอใน JavaScript Logging
เป็น api แนวเดียวกับ log4j แต่ใช้กับ javascript
วิธีการใช้
include logger.js กับ logger.css
บริเวณที่ log information จะใช้ div แบบนี้

ถ้าไม่ต้องการหรูมากนัก ก็ใส่เพียงเท่านี้ก็พอ

เวลา log ก็เรียก function debug, info, warn, error, fatal
เป็น api แนวเดียวกับ log4j แต่ใช้กับ javascript
วิธีการใช้
include logger.js กับ logger.css
<script type="text/javascript" src="logger.js"></script>
<link rel="stylesheet" type="text/css" href="logger.css" />
บริเวณที่ log information จะใช้ div แบบนี้
<div id="fvlogger">
<dl>
<dt>fvlogger</dt>
<dd class="all"><a href="#fvlogger" onclick="showAll();" title="show all" id="abcdef">all</a></dd>
<dd class="debug"><a href="#fvlogger" onclick="showDebug();" title="show debug" id="showDebug">debug</a></dd>
<dd class="info"><a href="#fvlogger" title="show info" id="showInfo">info</a></dd>
<dd class="warn"><a href="#fvlogger" onclick="showWarn();" title="show warnings" id="showWarn">warn</a></dd>
<dd class="error"><a href="#fvlogger" onclick="showError();" title="show errors" id="showError">error</a></dd>
<dd class="fatal"><a href="#fvlogger" onclick="showFatal();" title="show fatals" id="showFatal">fatal</a></dd>
<dd><a href="#fvlogger" onclick="eraseLog(true);" title="erase">erase</a></dd>
</dl>
</div>

ถ้าไม่ต้องการหรูมากนัก ก็ใส่เพียงเท่านี้ก็พอ
<div id="fvlogger"></div>

เวลา log ก็เรียก function debug, info, warn, error, fatal
function foo() {
debug("foo start");
....
}
Related link from Roti
Thursday, September 08, 2005
Link (2005-09-08)
- Refactoring Thumbnails
- The New Holy Trinity (Spring + AspectJ + @annotation)
- ColorFan Component
Note:
วันนี้เข้าไปวาดรูปเล่นมา 1 รูป เพลินดี แก้เครียดได้หมือนกัน
http://artpad.art.com/?imiapk946w4
Related link from Roti
Tuesday, September 06, 2005
Ruby กับ Design by Concern
ผมได้ทดลองเขียน program เพื่อทำการ plot graph แสดงการใช้งานของ user
โดยใช้ Ruby language เขียน
ตัวโปรแกรมมี Requirement ดังนี้
output หน้าตาประมาณนี้

ออกแบบ class diagram ดังนี้

ใน class diagram จะแสดงว่า Requirement ข้อไหน implement
ใน class อะไรบ้าง
ปัญหาที่เรามักเจอในการ design โปรแกรม ก็คือ Requirement หนึ่งๆ
มักจะกระจายตัวอยู่ใน class (เผอิญตัวอย่างนี้เป็นโปรแกรมเล็กๆ ก็เลยไม่ค่อยเห็นประเด็นนี้เท่าไร)
ทำให้เมื่อมีการเปลี่ยนแปลง requirement เกิดขึ้นต้องกระทบกับหลายๆ class
หรือไม่ก็ ถ้ามีการเพิ่ม requirement ใหม่ๆ เข้ามา Requirement
นั้นก็มักจะทำให้เกิดการเปลี่ยนแปลง class หลายๆ class
ตัว Ruby เอง เปิดโอกาสให้เรา implement class แยกกระจายหลายๆ
ที่ได้ ทำให้เราสามารถจัดกลุ่ม file ตาม concern (requirement)
แทนที่จะแยก 1 file 1 class
ในโปรแกรมที่ผมลองทำ ผมทดลองแยก Requirement R3
ออกเป็นเป็นอีก file
ภายใน file นี้ผมก็ extend class Plotter กับ class UserTimeLine
เฉพาะส่วนที่เกี่ย่วข้องกับ Requirement ที่ 3 นี้เท่านั้น

ซ้ายบนเป็น class UserTimeLine ส่วนซ้ายล่างเป็น class Plotter
ทางขวาเป็น file ที่ implement Requirement R3
เส้นสีแดง แสดงว่ามีการ extend class
ส่วนสีน้ำเงิน แสดง dependency ภายในของ Requirement R3
ข้อดีของวิธีนี้ อืมม์ ไม่ค่อยแน่ใจนัก
มันทำให้เรา concentrate บน Requirement มากขึ้น
เห็น class ที่ต้อง implement feature นั้นๆ อยู่เป็นกลุ่มก้อนเดียวกัน
(อาจจะทำให้ง่ายต่อการจัดการ)
เวลาอ่าน class ต่าง ก็อาจจะอ่านง่ายขึ้น เพราะจะเห็นแต่ส่วนที่ตัวเองเกี่ยวข้องเท่านั้น
(อันนี้อาจเป็นข้อเสียก็ได้ เห็นแต่ส่วนตัวเอง ทำให้ลืมนึกไปว่า
อาจจะมีคนอื่นใช้อยู่ก็ได้ ทำให้แก้ไขไม่ระวัง)
Note: extend ในที่นี้ไม่ได้หมายถึง extends แบบที่เราทำใน Java
แต่มีความหมายเป็นการ Merge เสียมากกว่า
โดยใช้ Ruby language เขียน
ตัวโปรแกรมมี Requirement ดังนี้
- R1 สามารถอ่านข้อมูลจาก file wtmp ใน aix ได้โดยตรง
- R2 สามารถสรุปข้อมูลนำมา plot เป็น graph แสดงเวลาการทำงานของ user
ในช่วงวันที่กำหนดได้ - R3 แสดงช่วงเวลาที่มีจำนวน concurrent user เกินกว่าค่าที่กำหนด
output หน้าตาประมาณนี้

ออกแบบ class diagram ดังนี้

ใน class diagram จะแสดงว่า Requirement ข้อไหน implement
ใน class อะไรบ้าง
ปัญหาที่เรามักเจอในการ design โปรแกรม ก็คือ Requirement หนึ่งๆ
มักจะกระจายตัวอยู่ใน class (เผอิญตัวอย่างนี้เป็นโปรแกรมเล็กๆ ก็เลยไม่ค่อยเห็นประเด็นนี้เท่าไร)
ทำให้เมื่อมีการเปลี่ยนแปลง requirement เกิดขึ้นต้องกระทบกับหลายๆ class
หรือไม่ก็ ถ้ามีการเพิ่ม requirement ใหม่ๆ เข้ามา Requirement
นั้นก็มักจะทำให้เกิดการเปลี่ยนแปลง class หลายๆ class
ตัว Ruby เอง เปิดโอกาสให้เรา implement class แยกกระจายหลายๆ
ที่ได้ ทำให้เราสามารถจัดกลุ่ม file ตาม concern (requirement)
แทนที่จะแยก 1 file 1 class
ในโปรแกรมที่ผมลองทำ ผมทดลองแยก Requirement R3
ออกเป็นเป็นอีก file
ภายใน file นี้ผมก็ extend class Plotter กับ class UserTimeLine
เฉพาะส่วนที่เกี่ย่วข้องกับ Requirement ที่ 3 นี้เท่านั้น

ซ้ายบนเป็น class UserTimeLine ส่วนซ้ายล่างเป็น class Plotter
ทางขวาเป็น file ที่ implement Requirement R3
เส้นสีแดง แสดงว่ามีการ extend class
ส่วนสีน้ำเงิน แสดง dependency ภายในของ Requirement R3
ข้อดีของวิธีนี้ อืมม์ ไม่ค่อยแน่ใจนัก
มันทำให้เรา concentrate บน Requirement มากขึ้น
เห็น class ที่ต้อง implement feature นั้นๆ อยู่เป็นกลุ่มก้อนเดียวกัน
(อาจจะทำให้ง่ายต่อการจัดการ)
เวลาอ่าน class ต่าง ก็อาจจะอ่านง่ายขึ้น เพราะจะเห็นแต่ส่วนที่ตัวเองเกี่ยวข้องเท่านั้น
(อันนี้อาจเป็นข้อเสียก็ได้ เห็นแต่ส่วนตัวเอง ทำให้ลืมนึกไปว่า
อาจจะมีคนอื่นใช้อยู่ก็ได้ ทำให้แก้ไขไม่ระวัง)
Note: extend ในที่นี้ไม่ได้หมายถึง extends แบบที่เราทำใน Java
แต่มีความหมายเป็นการ Merge เสียมากกว่า
Related link from Roti
Monday, September 05, 2005
ตัวอย่างการ extend class ของ Ruby
ช่วง 2 วันนี้เมามันอยู่กับการเขียน Ruby
โดยพยายามจะเขียนโปรแกรมที่ generate Graph แสดงภาพรวมการ
ทำงานของ User ในระบบ
ตอนนี้เริ่มเขียน version ที่ 2 แล้ว หลังจากปล้ำให้ version แรกทำงานได้แล้ว
ใน version ใหม่นี้ สิ่งที่พยายามจะทำ ก็คือการพยายามใช้ feature
ของ Ruby ให้มากขึ้น (แทนที่จะเขียนโดยใช้กรอบความคิดที่เคยชิน
จากการเขียน Java)
feature หนึ่งที่พยายามจะดู หรือหาว่า จะนำมาใช้อย่างไรก็คือ
การ extend class
ตัวอย่างที่เจอแล้วรู้สึกชอบก็คือ ใน module RMagick
(ซึ่งเป็น api ที่ใช้ในการ manipulate image)
Class RVG (Ruby Vector Graphic) ใน RMagic
จะมี constructor ที่รับตัวเลข 2 ตัว เพื่อใช้
ในการกำหนด ขนาดของ image ที่จะสร้างขึ้น
คำถามของคนที่จะใช้ api นี้ก็คือ เราจะ pass ค่าอะไรเข้าไป, และเป็นหน่วยอะไร
เช่นถ้าผมต้องการรูปขนาด 4x5 นิ้ว ผมควรจะใช้ width, height เป็นเท่าไรดี
และถ้ามีหลายๆหน่วย api ควรจะ implement Unit Converter Function
นี้ไว้ที่ไหนดี
RVG เลือกใช้วิธีที่ดูเข้าทีมาก เขาเปิดโอกาสให้เราเขียนได้ดังนี้
ที่นี้ ruby จะเข้าใจ syntax นี้อย่างไร
ruby จะมองว่า 4.in ก็คือ การเรียกใช้ method ที่ชื่อ
ของ Object Fixnum (4 ก็คือ Fixnum Object)
ส่วน 15.5.cm ก็คือ การเรียกใช้ method
Float Object นั้น
ประเด็นก็คือ method
มันไม่ได้ build-in มาใน Fixnum หรือ Float ตั้งแต่ต้น
แต่ ruby เปิดโอกาสให้เรา modify class definition ได้
โดยในกรณีนี้
RMagic กำหนด syntax มาให้เราเรียกใช้ดังนี้
ถ้ามีการกำหนดค่านี้เมื่อไร RVG จะเข้าไป add method ใน
Fixnum กับ Float Class ให้เราทันที โดยอาศัยข้อมูล dpi ที่กำหนด
เป็นค่าที่ใช้ในการ convert
ถ้าตามไปดูใน source code ของ RVG จะเห็นวิธีที่เขาเขียน
จะเห็นว่ามีการ iterate Float class กับ Fixnum class
เพื่อเพิ่ม method เข้าไป (ผ่านทางคำสั่ง class_eval)
ดูแล้วชอบจริงๆ
โดยพยายามจะเขียนโปรแกรมที่ generate Graph แสดงภาพรวมการ
ทำงานของ User ในระบบ
ตอนนี้เริ่มเขียน version ที่ 2 แล้ว หลังจากปล้ำให้ version แรกทำงานได้แล้ว
ใน version ใหม่นี้ สิ่งที่พยายามจะทำ ก็คือการพยายามใช้ feature
ของ Ruby ให้มากขึ้น (แทนที่จะเขียนโดยใช้กรอบความคิดที่เคยชิน
จากการเขียน Java)
feature หนึ่งที่พยายามจะดู หรือหาว่า จะนำมาใช้อย่างไรก็คือ
การ extend class
ตัวอย่างที่เจอแล้วรู้สึกชอบก็คือ ใน module RMagick
(ซึ่งเป็น api ที่ใช้ในการ manipulate image)
Class RVG (Ruby Vector Graphic) ใน RMagic
จะมี constructor ที่รับตัวเลข 2 ตัว เพื่อใช้
ในการกำหนด ขนาดของ image ที่จะสร้างขึ้น
rvg = RVG.new(width,height)
คำถามของคนที่จะใช้ api นี้ก็คือ เราจะ pass ค่าอะไรเข้าไป, และเป็นหน่วยอะไร
เช่นถ้าผมต้องการรูปขนาด 4x5 นิ้ว ผมควรจะใช้ width, height เป็นเท่าไรดี
และถ้ามีหลายๆหน่วย api ควรจะ implement Unit Converter Function
นี้ไว้ที่ไหนดี
RVG เลือกใช้วิธีที่ดูเข้าทีมาก เขาเปิดโอกาสให้เราเขียนได้ดังนี้
rvg1 = RVG.new(4.in, 5.in) # หน่วยเป็นนิ้ว
rvg2 = RVG.new(30.cm, 15.5.cm) # หน่วยเป็นเซนติเมตร
ที่นี้ ruby จะเข้าใจ syntax นี้อย่างไร
ruby จะมองว่า 4.in ก็คือ การเรียกใช้ method ที่ชื่อ
inของ Object Fixnum (4 ก็คือ Fixnum Object)
ส่วน 15.5.cm ก็คือ การเรียกใช้ method
cm ของFloat Object นั้น
ประเด็นก็คือ method
in หรือ cm มันไม่ได้ build-in มาใน Fixnum หรือ Float ตั้งแต่ต้น
แต่ ruby เปิดโอกาสให้เรา modify class definition ได้
โดยในกรณีนี้
RMagic กำหนด syntax มาให้เราเรียกใช้ดังนี้
RVG::dpi = 72
ถ้ามีการกำหนดค่านี้เมื่อไร RVG จะเข้าไป add method ใน
Fixnum กับ Float Class ให้เราทันที โดยอาศัยข้อมูล dpi ที่กำหนด
เป็นค่าที่ใช้ในการ convert
ถ้าตามไปดูใน source code ของ RVG จะเห็นวิธีที่เขาเขียน
def dpi=(n)
if !defined?(@dpi)
[Float, Fixnum].each do |c|
c.class_eval do
# the default measurement - 1px is 1 pixel
def px
self
end
# inches
def in
self * ::Magick::RVG.dpi
end
# millimeters
def mm
self * ::Magick::RVG.dpi / 25.4
end
# centimeters
def cm
self * ::Magick::RVG.dpi / 2.54
end
.....
จะเห็นว่ามีการ iterate Float class กับ Fixnum class
เพื่อเพิ่ม method เข้าไป (ผ่านทางคำสั่ง class_eval)
ดูแล้วชอบจริงๆ
Related link from Roti
Saturday, September 03, 2005
Link น่าสนใจ (2005-09-03)
- CSS Table Gallery
- Moving by Bicycle
- Spot The Fake Smile Experiment
***** ควรลองเป็นอย่างยิ่ง
Related link from Roti
Friday, September 02, 2005
ความหมายของคำว่า Maven
ใช้ Maven มาตั้งนาน พึ่งรู้ความหมายของคำนี้
ไปได้มาจาก David Geary 's Weblog ในหัวข้อเรื่อง Tipping Rails
ที่เล่าถึงประเด็นที่เขาเชื่อว่า Rails จะถึงจุด tipping point ในไม่ช้า
People who love to acquire knowledge on all sorts of topics and, perhaps more importantly, love to tell others about what they've learned (in fact, the word maven comes from Yiddish and means someone who accumulates knowledge).
ไปได้มาจาก David Geary 's Weblog ในหัวข้อเรื่อง Tipping Rails
ที่เล่าถึงประเด็นที่เขาเชื่อว่า Rails จะถึงจุด tipping point ในไม่ช้า
Related link from Roti
Ruby กับการ parse binary file
ช่วงนี้มี project ที่จะลองใช้ ruby parse wtmp file
และทำการ generate Image ที่แสดงลักษณะการใช้งานของ user ออกมา
(จริงๆแล้วมันไม่ต้องเวอร์ขนาดนี้ก็ได้ แต่อยากลอง feature ต่างๆดู)
เริ่มแรกสิ่งที่ต้อง solve ก็คือการ parse wtmp file
ขอยกตัวอย่างเฉพาะเจาะจงลงไปที่ wtmp file ที่ได้จาก AIX แล้วกัน
ใน AIX wtmp file จะมี record size = 648 byte
(AIX version ใหม่ ของเก่าขนาดเล็กกว่านี้เยอะ)
วิธีการที่ผมใช้ก็คือ อ่าน file ทีละ 648 byte เป็น string จากนั้นก็ส่งไป
ให้
ตัว
method นี้จะ return Array ของ object โดยเราต้องกำหนด template
ที่จะใช้ในการ parse
ความหมายของ template ที่ใช้
Link
Document ของ unpack method ใน String class
และทำการ generate Image ที่แสดงลักษณะการใช้งานของ user ออกมา
(จริงๆแล้วมันไม่ต้องเวอร์ขนาดนี้ก็ได้ แต่อยากลอง feature ต่างๆดู)
เริ่มแรกสิ่งที่ต้อง solve ก็คือการ parse wtmp file
ขอยกตัวอย่างเฉพาะเจาะจงลงไปที่ wtmp file ที่ได้จาก AIX แล้วกัน
ใน AIX wtmp file จะมี record size = 648 byte
(AIX version ใหม่ ของเก่าขนาดเล็กกว่านี้เยอะ)
วิธีการที่ผมใช้ก็คือ อ่าน file ทีละ 648 byte เป็น string จากนั้นก็ส่งไป
ให้
parseRecord ทำงาน
def parseFile(fileName)
list = Array.new
begin
File.open(fileName, 'r') do |file|
while (! file.eof)
tmp = parseRecord(file.read(648))
wtmp = Wtmp.new(tmp)
list << wtmp
end
end
rescue
end
return list
end
ตัว
parseRecord ก็จะใช้ method unpack เข้ามาช่วยmethod นี้จะ return Array ของ object โดยเราต้องกำหนด template
ที่จะใช้ในการ parse
def parseRecord(str)
str.unpack("A256A14A64x2Nnx6Nnna256x36")
end
ความหมายของ template ที่ใช้
- A256 -> return String ที่ตัดเอา white space ออกแล้ว
ส่วนค่า 256 ก็คือ ความยาวของ data ที่ต้องการอ่านขึ้นมา ในที่นี้คือ 256 byte - x2 -> skip ข้ามไป 2 byte
- N -> integer ความยาว 4 byte
- n -> short (2 byte)
Link
Document ของ unpack method ใน String class
Related link from Roti
Wednesday, August 31, 2005
เล่าให้ฟัง (48-08-31)
วันก่อนอ่านเจอตารางตัวเลข โครงสร้างแรงงานนอกระบบ
ในหนังสือมติชนรายสัปดาห์ มีจุดสะดุดตาที่หนึ่งก็คือ
อืมม์ อาชีพเรานี่ใช้ฝีมือปานกลางเอง
กลับไปเป็นวิศวกรไฟฟ้าดีกว่าแฮะ
ช่วงนี้อ่านหนังสืออยู่เล่มหนึ่งชื่อ "ท้าวทองกีบม้า"
ของสำนักพิมพ์มติชน ขอบรรยายให้ฟังคร่าวๆ พอเป็นกระสัย
ชื่อ "ท้าวทองกีบม้า" เป็นชื่อบรรดาศักด์ิของข้าราชสำนักฝ่ายใน
มีหน้าที่ประกอบอาหารหวาน
ที่มาของชื่อนี้ก็มีคนสันนิษฐานไว้หลายแนว
แต่ที่หนังสือเล่มนี้ขยายความ ก็คือ ชื่อนี้เป็นชื่อที่ได้มาจากชื่อ
"แคทเทอริน เดอ ทอร์ควิมา" ซึ่งเป็นภรรยาของ
เจ้าพระยาวิชาเยนทร์ (คอนแสตนติน ฟอลคอน)
ในสมัยสมเด็จพระนารายณ์
เธอเป็นลูกครึ่งโปรตุเกส กับ ญี่ปุ่น
สืบเชื้อสายมาจาก ออกญาเสนาภิมุข (ยามาดา)
ที่รับราชการในสมัยพระเอกาทศรถ
หลังจากความวุ่นวายในช่วงเปลี่ยนราชวงศ์ในช่วงนั้น
ฟอลคอนถูกกำจัด (อาจรวมถึงพระนารายณ์ด้วย)
ตัวท้าวทองกีบม้า ถูกนำตัวไปเป็นทาสในวัง มีหน้าที่ทำอาหาร
ซึ่งช่วงนั้นแหล่ะที่เป็นที่มาของขนมหวาน
ที่มีไข่และน้ำตาลเป็นส่วนประกอบ (เป็นลักษณะของอาหารโปรตุเกส)
เช่น ทองหยิบ ทองหยอด ฝอยทอง ทองโปร่ง ทองพลุ ขนมหม้อแกง
สังขยา ....
เกริ่นแค่นี้พอแล้วกัน เล่มจริงอ่านแล้วเพลินดี
ใครสนใจลองหาอ่านดู
แถมกลอนให้อีกนิด
ในหนังสือมติชนรายสัปดาห์ มีจุดสะดุดตาที่หนึ่งก็คือ
ประกอบอาชีพอิสระ
- มีฝีมือสูง (วิศวกร/สถาปนิก/เภสัช)
- มีฝีมือปานกลาง (โปรแกรมเมอร์, สอนพิเศษ, นักร้อง, นักแสดง)
- มีฝึมือทั่วไป (ช่างเสริมสวย, แผงลอย, สาธิตสินค้า)
อืมม์ อาชีพเรานี่ใช้ฝีมือปานกลางเอง
กลับไปเป็นวิศวกรไฟฟ้าดีกว่าแฮะ
ช่วงนี้อ่านหนังสืออยู่เล่มหนึ่งชื่อ "ท้าวทองกีบม้า"
ของสำนักพิมพ์มติชน ขอบรรยายให้ฟังคร่าวๆ พอเป็นกระสัย
ชื่อ "ท้าวทองกีบม้า" เป็นชื่อบรรดาศักด์ิของข้าราชสำนักฝ่ายใน
มีหน้าที่ประกอบอาหารหวาน
ที่มาของชื่อนี้ก็มีคนสันนิษฐานไว้หลายแนว
แต่ที่หนังสือเล่มนี้ขยายความ ก็คือ ชื่อนี้เป็นชื่อที่ได้มาจากชื่อ
"แคทเทอริน เดอ ทอร์ควิมา" ซึ่งเป็นภรรยาของ
เจ้าพระยาวิชาเยนทร์ (คอนแสตนติน ฟอลคอน)
ในสมัยสมเด็จพระนารายณ์
เธอเป็นลูกครึ่งโปรตุเกส กับ ญี่ปุ่น
สืบเชื้อสายมาจาก ออกญาเสนาภิมุข (ยามาดา)
ที่รับราชการในสมัยพระเอกาทศรถ
หลังจากความวุ่นวายในช่วงเปลี่ยนราชวงศ์ในช่วงนั้น
ฟอลคอนถูกกำจัด (อาจรวมถึงพระนารายณ์ด้วย)
ตัวท้าวทองกีบม้า ถูกนำตัวไปเป็นทาสในวัง มีหน้าที่ทำอาหาร
ซึ่งช่วงนั้นแหล่ะที่เป็นที่มาของขนมหวาน
ที่มีไข่และน้ำตาลเป็นส่วนประกอบ (เป็นลักษณะของอาหารโปรตุเกส)
เช่น ทองหยิบ ทองหยอด ฝอยทอง ทองโปร่ง ทองพลุ ขนมหม้อแกง
สังขยา ....
เกริ่นแค่นี้พอแล้วกัน เล่มจริงอ่านแล้วเพลินดี
ใครสนใจลองหาอ่านดู
แถมกลอนให้อีกนิด
ทองหยิบทิพย์เทียมทัด สามหยิบชัดน่าเชยชม
หลงหยิบว่ายาดม ก้มหน้าเมินเขินขวนใจ
...
ทองหยอดทอดสนิท ทองม้วนมิดคิดความหลัง
สองปีสองปิดบัง แต่ลำพังสองต่อสอง
(กลอนของ ร.1)
Related link from Roti
ทดลอง Implement Business Rule ด้วย Drools
โดยปกติเวลาเรา implement business logic เรามัก
จะเขียน code ลงไปตรงๆ โดยใช้พวก control statement เป็นหลัก (พวก if statement)
ถ้าเป็นโปรแกรมที่มี bussiness logic ซับซ้อน ก็คงจะต้อง
เคยปวดหัวกับ if block อันมหาศาล โปรแกรมพวกนี้
ส่วนใหญ่จะเกี่ยวกับเงินๆทองๆ เช่น Module Doctor Fee (พวกหมอในโรงพยาบาลเอกชน
จะมีวิธีการคำนวณเงินที่ซับซ้อนมาก) หรือ Module การคำนวณเงินลงทะเบียนของนักศึกษา
ในโปรแกรมใหญ่ๆ ที่อยู่บน Mainframe มีอยู่จำนวนหนึ่งที่
นิยม implement business logic ด้วย Rule Engine
ถามว่าช่วยลดความซับซ้อนได้แค่ไหน
อันนี้ตอบไม่ได้เหมือนกัน เพราะยังไม่เคยลองเลย
แต่เท่าที่ดู ในกรณีที่มี Rule เยอะมาก ปัญหาที่เกิดก็คือ
Rule จะเกิด conflict กันได้ง่าย
ต้องมี Debugger หรือ Test Case ดีๆ ไว้ช่วย
ในส่วนของ Java ผมสนใจ Rule Engine อยู่หลายตัวเหมือนกัน
ช่วงนี้ได้ลองใช้ Drools ดู ก็พบว่าเข้าท่าดีเหมือนกัน
ตัว Drools เองใช้ Charles Forgy's Rete algorithm
ลองมาดูตัวอย่างการ Implement กันดู
สมมติว่าเราจะใช้ Rule Engine ในส่วนของการ คำนวณ Discount
ของ Sale Order
เริ่มด้วยการ define Domain ก่อน ทำง่ายๆดังนี้

ส่วน business rule ที่จะ implement มีดังนี้
Drools ให้เรากำหนด Rule ผ่านทาง xml file
โดยรูปแบบของ file เป็นดังนี้
กฎข้อที่ 1 "ลูกค้าทุกคนที่ซื้อของ ในเบื้องต้นจะได้ส่วนลด 5 %"
เขียนได้ดังนี้
กฎข้อที่ 2 "กรณีที่เป็นลูกค้าภาคเหนือ จะให้ส่วนลด 7 %"
กฎข้อที่ 3 "กรณีที่ซื้อสินค้าเกิน 1500 บาท และซื้อเป็นเงินสด จะให้ส่วนลด 10 %"
กฎข้อที่ 4 "สินค้า "a-1" จะไม่มีส่วนลด"
ในการ run จะเขียน code ดังนี้
จะเห็นว่าขั้นแรก เราต้อง load Rule จาก xml file ก่อน
จากนั้นก็ทำการ initialize Working Memory ขึ้นมา
ส่วน DebugListener ที่ใส่เข้าไป ก็เพื่อจะให้ Drools dump debug information
ออกมาให้เราดู
จากนั้นก็เป็นการ assert Fact เข้าไป
สุดท้ายก็สั่ง fireAllRules เพื่อให้ Rule Engine เริ่มคำนวณผลลัพท์
ข้อที่ต้องระวัง ก็คือ Conflict ของ Rule
อย่างในตัวอย่างของเรา สมมติให้ Data ที่จะทดสอบมี nature ดังนี้
ถ้าดูที่ Rule ที่เรากำหนดขึ้นมา จะเห็นว่า
Conflict จะเกิดได้ที่ rule ข้อ 2 กับ ข้อ 3
โดยข้อ 2 จะให้ส่วนลด 7 % ส่วนข้อ 3 จะให้ส่วนลด 10 %
การ solve สามารถทำได้หลายวิธี
ในกรณีของเรา เราใช้การกำหนด salience เข้ามาช่วย
โดยให้ rule ข้อ 2 มี saliance = 20 ส่วนข้อ 3 = 10
ดังนั้น Drools จะตัดสินให้ข้อ 3 ชนะ (ค่าน้อยๆมันทำทีหลัง เลยชนะ)
เท่าที่ลองทดสอบดูในประเด็นเรื่อง performance
พบว่าขั้นที่กินเวลามากสุดก็คือตอนที่เรา load xml file
ใน case ที่ทดสอบดู พบว่า
ไว้คราวหน้าจะลอง integrate เข้ากับ Spring ดู
อ่านเพิ่มเติม
จะเขียน code ลงไปตรงๆ โดยใช้พวก control statement เป็นหลัก (พวก if statement)
ถ้าเป็นโปรแกรมที่มี bussiness logic ซับซ้อน ก็คงจะต้อง
เคยปวดหัวกับ if block อันมหาศาล โปรแกรมพวกนี้
ส่วนใหญ่จะเกี่ยวกับเงินๆทองๆ เช่น Module Doctor Fee (พวกหมอในโรงพยาบาลเอกชน
จะมีวิธีการคำนวณเงินที่ซับซ้อนมาก) หรือ Module การคำนวณเงินลงทะเบียนของนักศึกษา
ในโปรแกรมใหญ่ๆ ที่อยู่บน Mainframe มีอยู่จำนวนหนึ่งที่
นิยม implement business logic ด้วย Rule Engine
ถามว่าช่วยลดความซับซ้อนได้แค่ไหน
อันนี้ตอบไม่ได้เหมือนกัน เพราะยังไม่เคยลองเลย
แต่เท่าที่ดู ในกรณีที่มี Rule เยอะมาก ปัญหาที่เกิดก็คือ
Rule จะเกิด conflict กันได้ง่าย
ต้องมี Debugger หรือ Test Case ดีๆ ไว้ช่วย
ในส่วนของ Java ผมสนใจ Rule Engine อยู่หลายตัวเหมือนกัน
ช่วงนี้ได้ลองใช้ Drools ดู ก็พบว่าเข้าท่าดีเหมือนกัน
ตัว Drools เองใช้ Charles Forgy's Rete algorithm
ลองมาดูตัวอย่างการ Implement กันดู
สมมติว่าเราจะใช้ Rule Engine ในส่วนของการ คำนวณ Discount
ของ Sale Order
เริ่มด้วยการ define Domain ก่อน ทำง่ายๆดังนี้

ส่วน business rule ที่จะ implement มีดังนี้
- ลูกค้าทุกคนที่ซื้อของ ในเบื้องต้นจะได้ส่วนลด 5 %
- กรณีที่เป็นลูกค้าภาคเหนือ จะให้ส่วนลด 7 % แทน
- กรณีที่ซื้อสินค้าเกิน 1500 บาท และซื้อเป็นเงินสด จะให้ส่วนลด 10 %
- สินค้า "a-1" จะไม่มีส่วนลด (ไม่เอายอดไปคำนวณในส่วนลดรวม)
Drools ให้เรากำหนด Rule ผ่านทาง xml file
โดยรูปแบบของ file เป็นดังนี้
<?xml version="1.0"?>
<rule-set name="BusinessRulesSample"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java"
xmlns:xs
="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation
="http://drools.org/rules rules.xsd
http://drools.org/semantics/java java.xsd">
<!-- Import the Java Objects that we refer
to in our rules -->
<java:import>
pok.test.Order
</java:import>
<!-- Rule -->
</rule-set>
กฎข้อที่ 1 "ลูกค้าทุกคนที่ซื้อของ ในเบื้องต้นจะได้ส่วนลด 5 %"
เขียนได้ดังนี้
<rule name="ในเบื้องต้น ลูกค้าทุกคนได้ส่วนลด 5 %" salience="100">
<parameter identifier="order">
<class>pok.test.Order</class>
</parameter>
<java:condition>
1 == 1
</java:condition>
<java:consequence>
order.setPercentDiscount(5.0);
</java:consequence>
</rule>
</rule-set>
กฎข้อที่ 2 "กรณีที่เป็นลูกค้าภาคเหนือ จะให้ส่วนลด 7 %"
<rule name="ลูกค้าภาคเหนือได้ส่วนลด 7 %" salience="20">
<parameter identifier="customer">
<class>pok.test.Customer</class>
</parameter>
<parameter identifier="order">
<class>pok.test.Order</class>
</parameter>
<java:condition>
customer.getRegion() == Customer.NORTH
</java:condition>
<java:condition>
order.getCustomer() == customer
</java:condition>
<java:consequence>
order.setPercentDiscount(7.0);
</java:consequence>
</rule>
กฎข้อที่ 3 "กรณีที่ซื้อสินค้าเกิน 1500 บาท และซื้อเป็นเงินสด จะให้ส่วนลด 10 %"
<rule name="สังซื้อมากกว่า 1500 บาท และเป็นเงินสด ได้ส่วนลด 10 %" salience="10
">
<parameter identifier="order">
<class>pok.test.Order</class>
</parameter>
<java:condition>
order.getAmount() > 1500.0
</java:condition>
<java:condition>
order.getPayType() == Order.CASH
</java:condition>
<java:consequence>
order.setPercentDiscount(10.0);
</java:consequence>
</rule>
กฎข้อที่ 4 "สินค้า "a-1" จะไม่มีส่วนลด"
<rule name="สินค้า p-1 จะไม่ให้ส่วนลด" salience="0">
<parameter identifier="item">
<class>pok.test.OrderItem</class>
</parameter>
<java:condition>
item.getProduct().getProductCode().equals("p-1")
</java:condition>
<java:consequence>
item.setIncludeInTotalDiscount(false);
</java:consequence>
</rule>
ในการ run จะเขียน code ดังนี้
RuleBase businessRules = RuleBaseLoader.loadFromUrl(BusinessLayer.class
.getResource("DiscountRules.xml"));
WorkingMemory workingMemory = businessRules.newWorkingMemory();
// Small ruleset, OK to add a debug listener
workingMemory.addEventListener(new DebugWorkingMemoryEventListener());
workingMemory.assertObject(order);
workingMemory.assertObject(order.getCustomer());
for (Iterator iter = order.getItems().iterator(); iter.hasNext();) {
OrderItem item = (OrderItem) iter.next();
workingMemory.assertObject(item);
}
workingMemory.fireAllRules();
// Test Result
System.out.println("Discount = " + order.getPercentDiscount());
for (Iterator iter = order.getItems().iterator(); iter.hasNext();) {
OrderItem item = (OrderItem) iter.next();
if (! item.isIncludeInTotalDiscount()) {
System.out.println("\titem:" +item.getProduct().getProductCode() + "
is no discount");
}
}
จะเห็นว่าขั้นแรก เราต้อง load Rule จาก xml file ก่อน
จากนั้นก็ทำการ initialize Working Memory ขึ้นมา
ส่วน DebugListener ที่ใส่เข้าไป ก็เพื่อจะให้ Drools dump debug information
ออกมาให้เราดู
จากนั้นก็เป็นการ assert Fact เข้าไป
สุดท้ายก็สั่ง fireAllRules เพื่อให้ Rule Engine เริ่มคำนวณผลลัพท์
ข้อที่ต้องระวัง ก็คือ Conflict ของ Rule
อย่างในตัวอย่างของเรา สมมติให้ Data ที่จะทดสอบมี nature ดังนี้
- customer อยู่ในภาคเหนือ
- ซื้อของ 7000 บาท เป็นเงินสด
ถ้าดูที่ Rule ที่เรากำหนดขึ้นมา จะเห็นว่า
Conflict จะเกิดได้ที่ rule ข้อ 2 กับ ข้อ 3
โดยข้อ 2 จะให้ส่วนลด 7 % ส่วนข้อ 3 จะให้ส่วนลด 10 %
การ solve สามารถทำได้หลายวิธี
ในกรณีของเรา เราใช้การกำหนด salience เข้ามาช่วย
โดยให้ rule ข้อ 2 มี saliance = 20 ส่วนข้อ 3 = 10
ดังนั้น Drools จะตัดสินให้ข้อ 3 ชนะ (ค่าน้อยๆมันทำทีหลัง เลยชนะ)
เท่าที่ลองทดสอบดูในประเด็นเรื่อง performance
พบว่าขั้นที่กินเวลามากสุดก็คือตอนที่เรา load xml file
ใน case ที่ทดสอบดู พบว่า
- load xml -> ~1800 millisec
- assert -> ~76 millisec
- fireRule -> ~5 millisec
ไว้คราวหน้าจะลอง integrate เข้ากับ Spring ดู
อ่านเพิ่มเติม
Related link from Roti
Tuesday, August 30, 2005
Link น่าสนใจ (2005-08-30)
- Raycoaster
Caustics are the optical effects that occur when light gets bent several times by some refractive material. You can observe them for example on the bottom of a swimming pool or when light shines through a glass bottle. I find the resulting shapes very attractive so I have tried to create a tool which allows me to create and explore caustics or rather pseudo-caustics.
- An Illusrated Guide to IPSec
Article นี้ใช้การอธิบายแบบ Bottom up
ดูแล้วเข้าใจมากขึ้นเยอะเลย
Related link from Roti
Link น่าสนใจ (2005-08-29)
- SteP 10 Limited
site นี้มีบทความเกี่ยวกับการ Modeling ที่น่าสนใจทีเดียว
(ในแง่ของ Bussiness Application) - Implementors plugin (Eclipse Plugin)
ช่วยในการ navigate จาก interface ไปยัง Implement Class
หรือจะกระโดดจาก implement class, method -> interface ก็ได้
Related link from Roti
Monday, August 29, 2005
Tagging with Rails
Y.o.m.b.a.r เขียน mixin ไว้ตัวหนึ่งที่ช่วยให้ ActiveRecord
handle เรื่อง tag ได้ง่ายขึ้น
ดูตัวอย่างการใช้งาน
วิธีการใช้ ก็คือให้เอา source ไปใส่ไว้ใต้ lib directory
แล้วก็แก้ config/environment.rb เพิ่มคำสั่ง
handle เรื่อง tag ได้ง่ายขึ้น
ดูตัวอย่างการใช้งาน
class Photo < ActiveRecord::Base
acts_as_taggable
end
elephant = Photo.find(4437)
elephant.tag 'zoo animals nature'
elephant.tagged_by?('urban') # => false
elephant.tags.size # => 3
elephant.tag_names # => [ 'zoo', 'animals', 'nature' ]
วิธีการใช้ ก็คือให้เอา source ไปใส่ไว้ใต้ lib directory
แล้วก็แก้ config/environment.rb เพิ่มคำสั่ง
require 'tagggale'
Related link from Roti
เรียนรู้ CLISP
สั่งซื้อหนังสือเกี่ยวกับ RUBY, RAILS จาก amazon มาอาทิตย์กว่าแล้ว
มันยังไม่ shpping เลย ก็เลยไปเดินร้านหนังสือที่ fortune มา
ได้หนังสือ Practical Common Lisp มาเล่มหนึ่ง
หนังสือเขียนดีใช้ได้เลย ชอบตรงที่เขาไม่เริ่มต้นด้วยการอธิบาย syntax
หรือ language feature แต่พยายามแสดงให้เห็นเลยว่า ถ้าเขียนกับโจทย์จริงๆแล้ว
จะต้องเขียนอย่างไรบ้าง
Environment ที่เขาแนะนำให้ใช้ ก็คือ ใช้ emacs + slime
ซึ่งเท่าที่ลองใช้ดูก็ work ดีมาก เสียอย่างเดียว
ยังจำคำสั่ง emacs ไม่ได้ (การกด meta key ก็วุ่นวายไปนิด
ใน OS X ที่ผมใช้ มันต้องกด ESC ทำให้ต้องง้างนิ้วไกลไปหน่อย
)
ส่วนตัว CLISP ผมใช้ GNU CLISP
สำหรับคนที่ใช้ java ก็มี Armed Bear Common List
ตัวนี้เป็น ANSI Common Lisp ที่ run บน Java Virtual Machine
มีข้อเสียในการ build นิดหน่อย ก็คือ ต้องใช้ Common LISP ในการ build
มันยังไม่ shpping เลย ก็เลยไปเดินร้านหนังสือที่ fortune มา
ได้หนังสือ Practical Common Lisp มาเล่มหนึ่ง
หนังสือเขียนดีใช้ได้เลย ชอบตรงที่เขาไม่เริ่มต้นด้วยการอธิบาย syntax
หรือ language feature แต่พยายามแสดงให้เห็นเลยว่า ถ้าเขียนกับโจทย์จริงๆแล้ว
จะต้องเขียนอย่างไรบ้าง
Environment ที่เขาแนะนำให้ใช้ ก็คือ ใช้ emacs + slime
ซึ่งเท่าที่ลองใช้ดูก็ work ดีมาก เสียอย่างเดียว
ยังจำคำสั่ง emacs ไม่ได้ (การกด meta key ก็วุ่นวายไปนิด
ใน OS X ที่ผมใช้ มันต้องกด ESC ทำให้ต้องง้างนิ้วไกลไปหน่อย
)
ส่วนตัว CLISP ผมใช้ GNU CLISP
สำหรับคนที่ใช้ java ก็มี Armed Bear Common List
ตัวนี้เป็น ANSI Common Lisp ที่ run บน Java Virtual Machine
มีข้อเสียในการ build นิดหน่อย ก็คือ ต้องใช้ Common LISP ในการ build
Related link from Roti
Sunday, August 28, 2005
Link น่าสนใจ (2005-08-27)
Roumen 's Weblog เขาเขียนเล่าเรื่อง Usability Studies of NetBeans
อ่านเพลินดีเหมือนกัน ที่ตลกก็คือเขาเล่าว่า เขาสามารถบอกได้ว่า ใคร(คนที่มาทดสอบ)
เป็นคนที่ใช้ eclipse อยู่แล้วบ้าง โดยดูจากพฤติกรรมที่คนคนนั้นเห็น error
เท้าความนิดหนึ่ง ใน eclipse เวลาเกิด error จะมีรูปกากะบาทเกิดขึ้นด้านซ้าย
ส่วนใน netbeans ก็เช่นเดียวกันเป็นรูปกากะบาทเหมือนกัน
ถ้าเป็นคนที่ใช้ eclipse อยู่ เวลาเกิด error ก็จะมีพฤติกรรมเหมือนกันหมด
ก็คือใช้ mouse ไป click ที่กากะบาท ซึ่งแน่นอนใน Netbeans การทำเช่นนั้น
จะกลายเป็นการ add Breakpoint แทน ที่น่าสนใจก็คือพฤติกรรมนั้นค่อนข้างจะติดเป็นนิสัย คือ ถึงจะเรียนรู้แล้ว
ว่า click กากะบาทใน netbeans ไม่ได้เป็นการแสดง error แต่พอเกิด error ใหม่ ก็จะยัง
คงพยายาม click กากะบาทอยู่ดี
ฟังเขาเล่าถึงเรื่องนี้แล้วก็นึกถึงตัวเองเหมือนกัน เมื่ออาทิตย์ก่อน ก็ใช้ Netbeans
paint หน้าจอ UI เหมือนกัน (ปกติใช้ eclipse แต่ชอบ visual editor
ของ netbeans มากกว่า) จะหงุดหงิดมากเวลาเกิด error แล้วพยายาม click
กากะบาทแล้วดันกลายเป็นการ add Breakpoint แทนแล้วก็ไม่จำด้วย เกิดใหม่ก็เผลอไป click ใหม่
Extremely Successful Software
มีอยู่ part หนึ่งที่เขาพูดถึง Technology ที่เขาใช้
อืมม์ผมนี่โลกแคบเหมือนกันแฮะ ไม่รู้เลยว่ามีคนใช้ Lisp ใน Business Application
สงสัยต้องเรียนรู้ภาษานี้บ้างแล้ว
Gigayachts lure the mega-rich
อันนี้สำหรับผู้ที่ใช้ Oracle
จะได้รู้ว่า license ที่จ่ายไป สุดท้ายไปอยู่ที่ไหน
Ellison สั่งต่อเรือ yacht ความยาว 137 metres
ภายในประกอบด้วย 5 ชั้น, 82 ห้อง, ห้องเก็บ wine ขนาดเท่ากับ beach bungalow
มีเครื่องปั่นไฟที่ใหญ่พอสำหรับเมืองเล็กๆ
ราคาแค่ 453 millon dollars เท่านั้นเอง
ส่วนลำของ Paul Allen (co-founder of Microsoft.)
ถูกหน่อย แค่ 300 millon dollars
(Note: อ่านเจอในมติชนก่อน แล้วค่อยไป search หาใน internet)
อ่านเพลินดีเหมือนกัน ที่ตลกก็คือเขาเล่าว่า เขาสามารถบอกได้ว่า ใคร(คนที่มาทดสอบ)
เป็นคนที่ใช้ eclipse อยู่แล้วบ้าง โดยดูจากพฤติกรรมที่คนคนนั้นเห็น error
เท้าความนิดหนึ่ง ใน eclipse เวลาเกิด error จะมีรูปกากะบาทเกิดขึ้นด้านซ้าย
ส่วนใน netbeans ก็เช่นเดียวกันเป็นรูปกากะบาทเหมือนกัน
ถ้าเป็นคนที่ใช้ eclipse อยู่ เวลาเกิด error ก็จะมีพฤติกรรมเหมือนกันหมด
ก็คือใช้ mouse ไป click ที่กากะบาท ซึ่งแน่นอนใน Netbeans การทำเช่นนั้น
จะกลายเป็นการ add Breakpoint แทน ที่น่าสนใจก็คือพฤติกรรมนั้นค่อนข้างจะติดเป็นนิสัย คือ ถึงจะเรียนรู้แล้ว
ว่า click กากะบาทใน netbeans ไม่ได้เป็นการแสดง error แต่พอเกิด error ใหม่ ก็จะยัง
คงพยายาม click กากะบาทอยู่ดี
ฟังเขาเล่าถึงเรื่องนี้แล้วก็นึกถึงตัวเองเหมือนกัน เมื่ออาทิตย์ก่อน ก็ใช้ Netbeans
paint หน้าจอ UI เหมือนกัน (ปกติใช้ eclipse แต่ชอบ visual editor
ของ netbeans มากกว่า) จะหงุดหงิดมากเวลาเกิด error แล้วพยายาม click
กากะบาทแล้วดันกลายเป็นการ add Breakpoint แทนแล้วก็ไม่จำด้วย เกิดใหม่ก็เผลอไป click ใหม่
Extremely Successful Software
มีอยู่ part หนึ่งที่เขาพูดถึง Technology ที่เขาใช้
Interestingly, when we checked we found that a number of Fortune 1000 companies were using Lisp in critical applications such as on-line banking and airline reservation systems.
อืมม์ผมนี่โลกแคบเหมือนกันแฮะ ไม่รู้เลยว่ามีคนใช้ Lisp ใน Business Application
สงสัยต้องเรียนรู้ภาษานี้บ้างแล้ว
Gigayachts lure the mega-rich
อันนี้สำหรับผู้ที่ใช้ Oracle
จะได้รู้ว่า license ที่จ่ายไป สุดท้ายไปอยู่ที่ไหน
Ellison สั่งต่อเรือ yacht ความยาว 137 metres
ภายในประกอบด้วย 5 ชั้น, 82 ห้อง, ห้องเก็บ wine ขนาดเท่ากับ beach bungalow
มีเครื่องปั่นไฟที่ใหญ่พอสำหรับเมืองเล็กๆ
ราคาแค่ 453 millon dollars เท่านั้นเอง
ส่วนลำของ Paul Allen (co-founder of Microsoft.)
ถูกหน่อย แค่ 300 millon dollars
(Note: อ่านเจอในมติชนก่อน แล้วค่อยไป search หาใน internet)
Related link from Roti
Subscribe to:
Posts (Atom)
