Friday, April 08, 2005

Cocoon Control Flow

ใน Web-Application ทีมีลักษณะเป็น control-flow เข่น สมมติว่า user เริ่มต้นป้อนที่หน้าจอ A ถ้าเขาเลือก tick ถูกจะ flow ต่อไปหน้าจอ B แต่ถ้าเขา tick ผิด ก็จะ flow ต่อไปหน้าจอ C ...
ในการที่จะ solve ปํญหานี้ เราจะต้อง keep track state ของ userไว้ (ไม่ว่าจะไว้ใน cookie หรือใน session)
และเมื่อเกิด request ก็ต้องตัดสินว่าควรจะ flow ไป state ไหนต่อ โดยก่อนไปก็อาจจะมีการทำ action บางอย่างก่อน และหลังจากที่ไปถึง state นั้นแล้ว จะต้อง present view ที่เหมาะสมให้กับ user

ใครที่เคยเขียนโปรแกรมสำหรับปัญหาประเภทนี้ คงจะรู้ว่า ตัวโปรแกรมจะมีลักษณะกระจัดกระจาย ทำให้ยากต่อการมองดูภาพรวม เช่นจะดูว่าอะไรจะเกิดถัดไป หรือก่อนมาถึงจุดนี้มัน flow มาจากจุดไหน
ยิ่งโปรแกรมที่มี flow เปลี่ยนแปลงไปเรื่อยๆ (requirement changed) หรือมี flow ที่ซับซ้อน การที่จะมานั่งไล่ดูโปรแกรมถือว่าเรื่องสาหัสเอาการ

Cocoon เป็น Web-Application Framework ตัวหนึ่งของค่าย apache ซึ่งมี concept เป็นแบบ pipe line trasformation
มี feature หนึ่งของ Cocoon ที่น่าสนใจก็คือ Control Flow
ซึ่งเสนอทางเลือกในการแก้ไขปัญหาแบบนี้ โดยแทนที่จะ solve flow โดยใช้ finite state machine
ก็เปลี่ยนมาเป็นการเขียนโปรแกรม ซึ่ง apply เอา concept ของ Contiuations มาใช้

สมมติว่าเราจะเขียนโปรแกรมเครื่องคิดเลข โดยโปรแกรมแสดง page1 ให้ user เลือกเลขที่ต้องการ จากนั้นแสดง page ให้ user เลือกเลขถัดไป จากนั้นแสดง page3 เพื่อให้เลือก opertaor ที่ต้องการ เมื่อครบแล้วโปรแกรมก็แสดงผลลัพท์ให้ดูโดยผ่าน page4
ในการ solve ตัวอย่างนี้ Cocoon จะใช้วิธีเขียนดังนี้
function calculator()
{
var a, b, operator;

cocoon.sendPageAndWait("getA.html");
a = cocoon.request.get("a");

cocoon.sendPageAndWait("getB.html");
b = cocoon.request.get("b");

cocoon.sendPageAndWait("getOperator.html");
operator = cocoon.request.get("op");

try {
if (operator == "plus")
cocoon.sendPage("result.html", {result: a + b});
else if (operator == "minus")
cocoon.sendPage("result.html", {result: a - b});
else if (operator == "multiply")
cocoon.sendPage("result.html", {result: a * b});
else if (operator == "divide")
cocoon.sendPage("result.html", {result: a / b});
else
cocoon.sendPage("invalidOperator.html", {operator: operator});
}
catch (exception) {
cocoon.sendPage("error.html", {message: "Operation failed: " + exception.toString()});
}
}

จะเห็นว่าการเขียนโปรแกรมในลักษณะนี้ จะง่ายต่อการทำความเข้าใจกว่ามาก โดย Cocoon จะใช้ javascript เข้ามาเป็นตัว control flow (javascript นี้จะ run อยู่ในฝั่ง server) กรณีที่ user สั่ง cocoon.sendPageAndWait ตัว cocoon จะทำการ suspend process นี้ (เรียกว่า continuations object ) และทำการ put เก็บไว้ใน map โดยใช้ unique id ค่าหนึ่ง โดย id นี้จะถูกส่งกลับไปกับ response page ด้วย และเมื่อ request วิ่งกลับมาอีกครั้ง ก็จะทำการ get process (โดยใช้ id ที่อยู่ใน request page นั้น) ขึ้นมา run ต่อ

นอกจากนี้ วิธีการนี้ยังช่วยแก้ปัญหากรณี user กด back หรือ user clone browser window ใด้อีกด้วย เพราะในแต่ละขั้นของ sendPageAndWait จะเกิด new Continuations object เสมอ และตัว Id ของ object นี้จะถูกฝังไว้ใน page ดังนั้นการที่ user กด back และ submit เข้ามาใหม่ ก็เป็นแต่เพียงการย้อนกลับไปใช้ continuations object ตัวเก่าเท่านั้นเอง

Note: อ่านเพิ่มเติม feature นี้ได้จาก

Related link from Roti

No comments: