CAB (Composite UI Application Block) จากค่าย Microsoft
กับ RCP (Rich Client Platform) จากค่าย Eclipse, IBM
มีอะไรที่เหมือนกัน?
ลองอ่าน Eclipse and Microsoft: parallel universes
Thursday, September 14, 2006
Tuesday, September 12, 2006
amb
amb หรือ Angelic Operator เป็น operator ที่มีเฉพาะใน
language ที่ support continuations เท่านั้น
(scheme, ruby, ...)
Angelic Operator มีชื่อเรียกอีกชื่อคือ "McCarthy's Ambiguous Operator
(John McCarthy คือคนที่สร้าง Lisp ขึ้นมา)
ลองดูความพิสดารของมันกัน
(โจทย์จาก ruby quiz #70)
ผลลัพท์ที่ได้ จะมีหน้าตาแบบนี้
โดยในตอนแรกที่เราสั่ง
a จะมีค่าเป็น 0
ส่วน b และ c ก็จะเริ่มต้นที่ 0 เช่นเดียวกัน
จากนั้นเมื่อถึงบรรทัดที่
ณ จุดที่ evaluate นี้ a กับ b มีค่า เท่ากับ 0
ดังนั้นมันจะเกิดการเรียกใช้ amb แบบไม่มี parameter
ซึ่งจะทำให้เกิดการ backtrack ย้อนกลับไปให้เลือกค่า a, b ใหม่
จนได้ค่า a = 0, b = 1 มา จึงจะผ่านบรรทัดนี้ไปได้
key สำคัญก็คือการ backtrack ซึ่งเขา implement โดยใช้ contiunation
ตัวอย่างการ implement amb โดย Jim Weirich
มีหน้าตาแบบนี้
เห็นแล้วตาลาย เพราะเขา port มาจากตัวอย่างใน Teach Yourself Scheme in Fixnum Days โดยตรง
มันมีกลิ่นของ funtional language เยอะไปหน่อย
แต่ลองดูตัวอย่างที่ Eric Kidd implement ไว้อีกแบบ
อันนี้มีกลิ่น imperative language เยอะหน่อย ทำให้ดูเข้าใจง่ายขึ้น
ดูง่ายกว่ากันเยอะเลย
ลอง implement โจทย์เดียวกันนี้ใน scheme บ้าง
(สบายหน่อย ตรง scheme มี library amb มาให้อยู่แล้ว)
หน้าตาโปรแกรมออกมาแบบนี้
language ที่ support continuations เท่านั้น
(scheme, ruby, ...)
Angelic Operator มีชื่อเรียกอีกชื่อคือ "McCarthy's Ambiguous Operator
(John McCarthy คือคนที่สร้าง Lisp ขึ้นมา)
ลองดูความพิสดารของมันกัน
(โจทย์จาก ruby quiz #70)
begin
a = amb 0,1,2,3,4
b = amb 0,1,2,3,4
c = amb 0,1,2,3,4
amb unless a < b
amb unless a + b == c
puts "a = #{a}, b = #{b}, c = #{c}"
amb
rescue
puts "no more solution."
end
ผลลัพท์ที่ได้ จะมีหน้าตาแบบนี้
irb(main):063:0>
a = 0, b = 1, c = 1
a = 0, b = 2, c = 2
a = 0, b = 3, c = 3
a = 0, b = 4, c = 4
a = 1, b = 2, c = 3
a = 1, b = 3, c = 4
no more solution.
nil
โดยในตอนแรกที่เราสั่ง
a = amb 0,1,2,3,4
a จะมีค่าเป็น 0
ส่วน b และ c ก็จะเริ่มต้นที่ 0 เช่นเดียวกัน
จากนั้นเมื่อถึงบรรทัดที่
amb unless a < b
ณ จุดที่ evaluate นี้ a กับ b มีค่า เท่ากับ 0
ดังนั้นมันจะเกิดการเรียกใช้ amb แบบไม่มี parameter
ซึ่งจะทำให้เกิดการ backtrack ย้อนกลับไปให้เลือกค่า a, b ใหม่
จนได้ค่า a = 0, b = 1 มา จึงจะผ่านบรรทัดนี้ไปได้
key สำคัญก็คือการ backtrack ซึ่งเขา implement โดยใช้ contiunation
ตัวอย่างการ implement amb โดย Jim Weirich
มีหน้าตาแบบนี้
class Amb
class ExhaustedError < RuntimeError; end
def initialize
@fail = proc { fail ExhaustedError, "amb tree exhausted" }
end
def choose(*choices)
prev_fail = @fail
callcc { |sk|
choices.each { |choice|
callcc { |fk|
@fail = proc {
@fail = prev_fail
fk.call(:fail)
}
if choice.respond_to? :call
sk.call(choice.call)
else
sk.call(choice)
end
}
}
@fail.call
}
end
def failure
choose
end
def assert(cond)
failure unless cond
end
end
เห็นแล้วตาลาย เพราะเขา port มาจากตัวอย่างใน Teach Yourself Scheme in Fixnum Days โดยตรง
มันมีกลิ่นของ funtional language เยอะไปหน่อย
แต่ลองดูตัวอย่างที่ Eric Kidd implement ไว้อีกแบบ
อันนี้มีกลิ่น imperative language เยอะหน่อย ทำให้ดูเข้าใจง่ายขึ้น
# A list of places we can "rewind" to
# if we encounter amb with no
# arguments.
$backtrack_points = []
# Rewind to our most recent backtrack
# point.
def backtrack
if $backtrack_points.empty?
raise "Can't backtrack"
else
$backtrack_points.pop.call
end
end
# Recursive implementation of the
# amb operator.
def amb *choices
# Fail if we have no arguments.
backtrack if choices.empty?
callcc {|cc|
# cc contains the "current
# continuation". When called,
# it will make the program
# rewind to the end of this block.
$backtrack_points.push cc
# Return our first argument.
return choices[0]
}
# We only get here if we backtrack
# using the stored value of cc,
# above. We call amb recursively
# with the arguments we didn't use.
amb *choices[1...choices.length]
end
# Backtracking beyond a call to cut
# is strictly forbidden.
def cut
$backtrack_points = []
end
ดูง่ายกว่ากันเยอะเลย
ลอง implement โจทย์เดียวกันนี้ใน scheme บ้าง
(สบายหน่อย ตรง scheme มี library amb มาให้อยู่แล้ว)
หน้าตาโปรแกรมออกมาแบบนี้
(require (lib "extra.ss" "swindle"))
(define assert
(lambda (pred)
(if (not pred) (amb))))
(define solve
(lambda ()
(let ((a (amb 0 1 2 3 4))
(b (amb 0 1 2 3 4))
(c (amb 0 1 2 3 4)))
(assert (< a b))
(assert (equal? (+ a b) c))
(printf "a=~a, b=~a, c=~a\n" a b c)
(amb))))
Related link from Roti
Saturday, September 09, 2006
กลุ่มคนที่สนใจ Erlang
ปกติในแวดวงของ erlang จะเงียบๆ ใครใช้ก็ใช้ไป
แต่หลังจากที่ Yariv Sadan เริ่มเขียน blog เกี่ยวกับ erlang
ก็ปรากฎว่ามีคนให้ความสนใจมากขึ้น
Yariv เขาเล่าว่า ก่อนเขียนเรื่อง erlang,
web เขามีคนมาเยี่ยมดูหลัก 10 คนต่อวัน
แต่พอหลังจากเขียนเรื่องนี้ จำนวนคนที่เข้ามาดูเพิ่มขึ้นเป็นหลักพัน
อันนี้้เป็นแผนที่ที่ได้จาก google analytic
แสดงว่า visitor มาจากที่ไหนบ้าง

ปล. ผมมั่นใจมากว่าจุดบน bangkok นี่ ของผมแน่นอน
แต่หลังจากที่ Yariv Sadan เริ่มเขียน blog เกี่ยวกับ erlang
ก็ปรากฎว่ามีคนให้ความสนใจมากขึ้น
Yariv เขาเล่าว่า ก่อนเขียนเรื่อง erlang,
web เขามีคนมาเยี่ยมดูหลัก 10 คนต่อวัน
แต่พอหลังจากเขียนเรื่องนี้ จำนวนคนที่เข้ามาดูเพิ่มขึ้นเป็นหลักพัน
อันนี้้เป็นแผนที่ที่ได้จาก google analytic
แสดงว่า visitor มาจากที่ไหนบ้าง
ปล. ผมมั่นใจมากว่าจุดบน bangkok นี่ ของผมแน่นอน
Related link from Roti
Friday, September 08, 2006
Law of Demeter
ช่วงนี้หากินกับน้องๆในบริษัทดีกว่า
ลองดู statement ที่น้องใหม่เขียน
วัตถุประสงค์ของบรรทัดข้างบน
ก็คือต้องการดึง current Line ขึ้นมา
ประเด็นก็คือ มัน(class ที่ implement method นี้)ล้วงลูกไปไกลเหลือเกิน
แถมยังรู้มากด้วย โดย method นี้รู้หมดว่า page เก็บอยู่ใน List Structure
ส่วน line ก็เก็บใน List เช่นเดียวกัน
ปัญหาของการล้วงลูก หรือรู้มาก ก็คือ
ในอนาคต ถ้ามีการปรับเปลี่ยน structure ของ page หรือ line เมื่อไร
จำนวน code ที่จะโดน effect ก็จะเยอะตามไปด้วย
ใน OO มันมีกฎอยู่ข้อหนึ่ง ก็คือ "Law of Demeter"
อธิบายสั้นๆว่า "Only talk to your friends"
ดังนั้นถ้าเขียนให้ถูกตามกฎของ Law of Demeter เราควรจะเขียนแค่นี้พอ
Good fences make good neighbors.
Robert Frost, "Mending Wall"
** ประโยคเด็ดข้างบน ลอกจาก "The Pragmatic Programmer, from journeyman to master"
บทที่ว่าด้วยเรื่อง "Decoupling and the Law of Demeter"
ลองดู statement ที่น้องใหม่เขียน
line = repOut.getPages().get(pageIndex-1).getLines().get(lineIndex-1);
วัตถุประสงค์ของบรรทัดข้างบน
ก็คือต้องการดึง current Line ขึ้นมา
ประเด็นก็คือ มัน(class ที่ implement method นี้)ล้วงลูกไปไกลเหลือเกิน
แถมยังรู้มากด้วย โดย method นี้รู้หมดว่า page เก็บอยู่ใน List Structure
ส่วน line ก็เก็บใน List เช่นเดียวกัน
ปัญหาของการล้วงลูก หรือรู้มาก ก็คือ
ในอนาคต ถ้ามีการปรับเปลี่ยน structure ของ page หรือ line เมื่อไร
จำนวน code ที่จะโดน effect ก็จะเยอะตามไปด้วย
ใน OO มันมีกฎอยู่ข้อหนึ่ง ก็คือ "Law of Demeter"
อธิบายสั้นๆว่า "Only talk to your friends"
ดังนั้นถ้าเขียนให้ถูกตามกฎของ Law of Demeter เราควรจะเขียนแค่นี้พอ
line = repOut.getCurrentLine()
Good fences make good neighbors.
Robert Frost, "Mending Wall"
** ประโยคเด็ดข้างบน ลอกจาก "The Pragmatic Programmer, from journeyman to master"
บทที่ว่าด้วยเรื่อง "Decoupling and the Law of Demeter"
Related link from Roti
Sun & JRuby
น่าสนใจมาก
Sun จ้างคนเขียน JRuby 2 คน เข้าทำงาน full time แล้ว
หน้าที่คือพัฒนา JRuby ให้แตะ 1.0 ให้ได้
และยังต้องคิดถึง developer tool ว่าควรจะเป็นอย่างไรด้วย
ส่วน licenses ของ JRuby ยังเป็น open source เหมือนเดิม
JRuby Steps Into the Sun
JRuby Shines in Sun
JRuby Guys
Sun จ้างคนเขียน JRuby 2 คน เข้าทำงาน full time แล้ว
หน้าที่คือพัฒนา JRuby ให้แตะ 1.0 ให้ได้
และยังต้องคิดถึง developer tool ว่าควรจะเป็นอย่างไรด้วย
ส่วน licenses ของ JRuby ยังเป็น open source เหมือนเดิม
JRuby Steps Into the Sun
JRuby Shines in Sun
JRuby Guys
Related link from Roti
Thursday, September 07, 2006
ประตูสามบาน
เมื่อวันก่อนตอนประชุมกัน มีการหยิบยกโจทย์ประตูสามบานขึ้นมาคุยกัน
จำได้ว่าเคยอ่านเจอใน blog ของ ฮุ้ย ก็เลยไปค้นเจอหัวข้อ ปริศนาเกมส์โชว์
ในนั้นมีการอ้างถึงหนังสือ "Information Theory, Inference and Learning Algorithms"
ก็เลย download มาดูเฉลย
[กรุณาอ่านโจทย์ใน link ก่อน แล้วค่อยอ่านข้างล่างนี้]
ใน commonsense ของเรา เราก็ต้องบอกว่า chance ของการได้ทอง
มันเหลือ 50-50 แล้ว การเปลี่ยนหรือการไม่เปลี่ยน ควรจะมีความน่าจะเป็นเท่าๆกัน
แต่หลังจากอ่านเฉลย ทำให้เรารู้ว่ามันมี event หนึ่งที่เราลืมคิดไป
นั่นก็คือ event ของ พิธีกรเกมส์โชว์ ที่เลือกเปิดประตูเบอร์ 3
event นั้นมีการกระจายตัวที่ไม่เท่ากัน เช่น
สมมติว่าถ้าทองอยู่ที่ประตู 1, พิธีกร จะเลือกเปิดประตู 2 หรือ 3 ก็ได้
ดังนั้นโอกาสที่จะเปิดประตู 3 มีค่าเท่ากับ 50%
แต่ถ้าทองอยู่ที่ประตู 2 , พิธีกร มีทางเลือกแค่ทางเดียวในการเปิดประตู
ดังนั้นโอกาสที่จะเปิดประตู 3 มีค่าเป็น 100%
ในเฉลย เขาเอา Bayes' theorem เข้ามาคำนวณ
ซึ่งสำหรับคนที่ไม่คุ้นกับ language ทางคณิตศาสตร์ (เช่นผม)
ก็ต้องใช้เวลา absorb นานหน่อย
แต่สำหรับคนที่ขี้เกียจอ่าน math language
ก็มีเฉลยอีกแบบ ที่เขาเรียกว่า "more intuitive"
ก็คือให้ลองคิดโจทย์นี้ใหม่ โดยจับโจทย์มาขยาย scale
เปลี่ยนเป็นว่า มีประตูอยู่ 1,000,000 ประตู
เราเลือกประตูเบอร์ 1, พิธีกรเปิดประตู 999,998 ประตูที่เหลือ
เพื่อโชว์ว่าไม่มีทองในประตูเหล่านี้
ถามว่าเราจะยังเลือกประตูเบอร์ 1 อยู่หรือไม่ หรือจะเปลี่ยนไปเลือกอีกประตูหนึ่งดี
พอเปลี่ยน scale โจทย์มาแบบนี้ ก็เลย Bingo เลย
ถ้าเปลี่ยนประตู โอกาสชนะนี้เกือบ 100% เลย
เพราะในการเลือกครั้งแรก เรามี chance อยู่แค่ 1 ใน 1,000,000 เท่านั้นเอง
ps. ใน link ของฮุ้ยมีโปรแกรม simulation ที่ otto เขียนไว้
แต่เนื่องจากทืม mm.co.th กลับจากญี่ปุ่นกันหมดแล้ว
link ก็เลยหายไปด้วย
ผมเลยเขียนโปรแกรม simulate ใหม่ขึ้นมา
จำได้ว่าเคยอ่านเจอใน blog ของ ฮุ้ย ก็เลยไปค้นเจอหัวข้อ ปริศนาเกมส์โชว์
ในนั้นมีการอ้างถึงหนังสือ "Information Theory, Inference and Learning Algorithms"
ก็เลย download มาดูเฉลย
[กรุณาอ่านโจทย์ใน link ก่อน แล้วค่อยอ่านข้างล่างนี้]
ใน commonsense ของเรา เราก็ต้องบอกว่า chance ของการได้ทอง
มันเหลือ 50-50 แล้ว การเปลี่ยนหรือการไม่เปลี่ยน ควรจะมีความน่าจะเป็นเท่าๆกัน
แต่หลังจากอ่านเฉลย ทำให้เรารู้ว่ามันมี event หนึ่งที่เราลืมคิดไป
นั่นก็คือ event ของ พิธีกรเกมส์โชว์ ที่เลือกเปิดประตูเบอร์ 3
event นั้นมีการกระจายตัวที่ไม่เท่ากัน เช่น
สมมติว่าถ้าทองอยู่ที่ประตู 1, พิธีกร จะเลือกเปิดประตู 2 หรือ 3 ก็ได้
ดังนั้นโอกาสที่จะเปิดประตู 3 มีค่าเท่ากับ 50%
แต่ถ้าทองอยู่ที่ประตู 2 , พิธีกร มีทางเลือกแค่ทางเดียวในการเปิดประตู
ดังนั้นโอกาสที่จะเปิดประตู 3 มีค่าเป็น 100%
ในเฉลย เขาเอา Bayes' theorem เข้ามาคำนวณ
ซึ่งสำหรับคนที่ไม่คุ้นกับ language ทางคณิตศาสตร์ (เช่นผม)
ก็ต้องใช้เวลา absorb นานหน่อย
แต่สำหรับคนที่ขี้เกียจอ่าน math language
ก็มีเฉลยอีกแบบ ที่เขาเรียกว่า "more intuitive"
ก็คือให้ลองคิดโจทย์นี้ใหม่ โดยจับโจทย์มาขยาย scale
เปลี่ยนเป็นว่า มีประตูอยู่ 1,000,000 ประตู
เราเลือกประตูเบอร์ 1, พิธีกรเปิดประตู 999,998 ประตูที่เหลือ
เพื่อโชว์ว่าไม่มีทองในประตูเหล่านี้
ถามว่าเราจะยังเลือกประตูเบอร์ 1 อยู่หรือไม่ หรือจะเปลี่ยนไปเลือกอีกประตูหนึ่งดี
พอเปลี่ยน scale โจทย์มาแบบนี้ ก็เลย Bingo เลย
ถ้าเปลี่ยนประตู โอกาสชนะนี้เกือบ 100% เลย
เพราะในการเลือกครั้งแรก เรามี chance อยู่แค่ 1 ใน 1,000,000 เท่านั้นเอง
ps. ใน link ของฮุ้ยมีโปรแกรม simulation ที่ otto เขียนไว้
แต่เนื่องจากทืม mm.co.th กลับจากญี่ปุ่นกันหมดแล้ว
link ก็เลยหายไปด้วย
ผมเลยเขียนโปรแกรม simulate ใหม่ขึ้นมา
def rand_door
rand(3) + 1
end
def open_second_door(gold_door, first_guess)
doors = [1,2,3]
doors.delete(gold_door)
doors.delete(first_guess)
doors[rand(doors.size)]
end
def choose_left_door(first_guess, opened_door)
(1..3).each {|door|
return door if (door != first_guess && door != opened_door)
}
end
def play
gold_door = rand_door
first_guess = rand_door
opened_door = open_second_door(gold_door, first_guess)
left_door = choose_left_door(first_guess, opened_door)
result = gold_door == left_door ? "win" : "loss"
puts <<-EOS
Game begin.
You don't see this. -> (Gold is at #{gold_door})
First. you choose door number #{first_guess}.
I open door number #{opened_door}.
So. You chnage your mind to door number #{left_door}.
== You #{result}. ==
EOS
gold_door == left_door ? 1 : 0
end
win = 0
loops = 30
loops.times do
win += play
end
puts "------------------"
puts "Play #{loops}, win #{win}"
Related link from Roti
Monday, September 04, 2006
Antlr
ช่วงนี้มีโจทย์งาน ที่ต้องเขียน interpreter เอง
โดยตัว language ที่เขียน จะใช้สำหรับ generate รายงาน แบบ text
(ที่ออกทาง line printer หรือ dot matrix)
ตัว syntax ลอกแบบมาจาก Ingres report writer
(ที่ลอก ก็เพราะมี report เก่าหลายร้อย report
ที่อยากจะ switch database ไปใช้ database ตัวอื่น)
หน้าตา language ก็ประมาณนี้
ตอนแรกก็ชั่งน้ำหนักก่อนเลย ว่าจะ implement ด้วยอะไรดี
ระหว่าง java กับ python
ตัว java นี่ของตายอยู่แล้ว
ส่วน python นี่ที่เลือก เพราะอยากเรียนรู้
สุดท้ายก็เลือกใช้ java เพราะว่ากรอบระยะเวลา
ที่อยากให้เสร็จภายใน 1 เดือน
tool ที่เลือกใช้ในส่วน parser ก็คือ Antlr
โดยข้อดีของการใช้ antlr ที่เห็นชัดๆ
ก็คือ meta syntax ของ antlr ที่ใช้เขียน lexer, parser, tree parser
มันเป็นตัวเดียวกัน ก็เลยง่ายในการเรียนรู้
เวลาเขียน parser ก็จะแยกเป็น 2 ขยัก
pass แรก ก็คือ compile language ให้เป็น AST (Abstract Syntax Tree) ก่อน
ขยักที่ 2 ก็คือตอน runtime
ซึ่งจะใช้ tree walker ท่องไปตาม AST ที่ได้จากข้อ 1
เริ่มแรกสุดผมก็ใช้วิธี top down
ทำ prototype ก่อน
define เฉพาะ gramma หลักๆที่อยากให้ prototype แสดงผลได้
หลังจากทำไปสัก 2 วัน ก็พบว่า
วิธีนี้มีข้อเสียมากกว่าข้อดี
ข้อดี ก็คือได้เห็นผลลัพท์ไว
ข้อเสีย ก็คือ gramma ของ feature ที่เพิ่มมาทีหลัง
มันจะ conflict กับ gramma ชุดแรก ได้ง่าย
ตอนนี้ก็เลยเปลี่ยนไปใช้วิธี bottom up
นั่ง define term ใล่จาก primitive datatype
ขึ้นมาเป็น expression ->ใล่ขึ้นมาเป็น statement
แล้วก็ใช้ junit เขียน test ประกบไปเรื่อยๆ
ทำให้ refactor ได้ง่ายขึ้น เพราะมี unit testing คอย proof ความถูกต้องอยู่
โดยตัว language ที่เขียน จะใช้สำหรับ generate รายงาน แบบ text
(ที่ออกทาง line printer หรือ dot matrix)
ตัว syntax ลอกแบบมาจาก Ingres report writer
(ที่ลอก ก็เพราะมี report เก่าหลายร้อย report
ที่อยากจะ switch database ไปใช้ database ตัวอื่น)
หน้าตา language ก็ประมาณนี้
.name xxxx
.query select name, salary from person
.header page
.pr '========================' .newline
.pr ' name salary .newline
.pr '========================' .newline
.header name
.tab 10
.print name
.newline
.detail
.tab 20
.print salary ("zzz,zzn.nn");
.newline
.footer page
.pr '========================' .newline
.pr ' total'
.tab 20
.pr sum(salary)
.newline
ตอนแรกก็ชั่งน้ำหนักก่อนเลย ว่าจะ implement ด้วยอะไรดี
ระหว่าง java กับ python
ตัว java นี่ของตายอยู่แล้ว
ส่วน python นี่ที่เลือก เพราะอยากเรียนรู้
สุดท้ายก็เลือกใช้ java เพราะว่ากรอบระยะเวลา
ที่อยากให้เสร็จภายใน 1 เดือน
tool ที่เลือกใช้ในส่วน parser ก็คือ Antlr
โดยข้อดีของการใช้ antlr ที่เห็นชัดๆ
ก็คือ meta syntax ของ antlr ที่ใช้เขียน lexer, parser, tree parser
มันเป็นตัวเดียวกัน ก็เลยง่ายในการเรียนรู้
เวลาเขียน parser ก็จะแยกเป็น 2 ขยัก
pass แรก ก็คือ compile language ให้เป็น AST (Abstract Syntax Tree) ก่อน
ขยักที่ 2 ก็คือตอน runtime
ซึ่งจะใช้ tree walker ท่องไปตาม AST ที่ได้จากข้อ 1
เริ่มแรกสุดผมก็ใช้วิธี top down
ทำ prototype ก่อน
define เฉพาะ gramma หลักๆที่อยากให้ prototype แสดงผลได้
หลังจากทำไปสัก 2 วัน ก็พบว่า
วิธีนี้มีข้อเสียมากกว่าข้อดี
ข้อดี ก็คือได้เห็นผลลัพท์ไว
ข้อเสีย ก็คือ gramma ของ feature ที่เพิ่มมาทีหลัง
มันจะ conflict กับ gramma ชุดแรก ได้ง่าย
ตอนนี้ก็เลยเปลี่ยนไปใช้วิธี bottom up
นั่ง define term ใล่จาก primitive datatype
ขึ้นมาเป็น expression ->ใล่ขึ้นมาเป็น statement
แล้วก็ใช้ junit เขียน test ประกบไปเรื่อยๆ
ทำให้ refactor ได้ง่ายขึ้น เพราะมี unit testing คอย proof ความถูกต้องอยู่
Related link from Roti
Form & Function
เห็น mk บ่นเรื่อง desinger ประเทศนี้
ก็เลยขอบ่นตามด้วย
เมื่อวานพาลูกนั่งรถเมล์ไปพิพิธภัณฑ์เด็ก
แต่เนื่องจากอากาศช่วงนี้เริ่มเข้าไกล้หน้าหนาวแล้ว
ฟ้าเปิดเต็มที่ ไม่มีเมฆมากวนใจ
เลยได้รับรู้ว่าหลังคาป้ายรถเมล์้เป็นพลาสติกใสสีฟ้านี่หว่า
สีสันไม่เลวเลย ฟ้าออกใสๆ ไม่เน่า หรือตุ่นเป็นพลาสติก recycle
สิ่งที่มาพร้อมกับความสวย ทันสมัย
ก็คือความร้อน
นั่งรอรถเมล์ตอนเกือบเที่ยงนี่มันจะสุกเอาได้
ผลงาน กทม. นี่รับประกันคุณภาพ
คราวก่อนก็มีประเด็นเรื่องป้ายโฆษณาบังสายตา
ทำให้มองไม่เห็นรถเมล์
มาคราวนี้ก็ใช้คนออกแบบที่พึ่งกลับจากอเมริกา
เลยยังไม่ชินกับสภาพอากาศเมืองไทย
(หรืออีกอย่าง ก็คือคนออกแบบที่ไม่เคยนั่งรถเมล์)
design ที่เป็น public แบบนี้
ไม่รู้ว่ามีกฎเกณฑ์การ approve design กันอย่างไรบ้าง
ก็เลยขอบ่นตามด้วย
เมื่อวานพาลูกนั่งรถเมล์ไปพิพิธภัณฑ์เด็ก
แต่เนื่องจากอากาศช่วงนี้เริ่มเข้าไกล้หน้าหนาวแล้ว
ฟ้าเปิดเต็มที่ ไม่มีเมฆมากวนใจ
เลยได้รับรู้ว่าหลังคาป้ายรถเมล์้เป็นพลาสติกใสสีฟ้านี่หว่า
สีสันไม่เลวเลย ฟ้าออกใสๆ ไม่เน่า หรือตุ่นเป็นพลาสติก recycle
สิ่งที่มาพร้อมกับความสวย ทันสมัย
ก็คือความร้อน
นั่งรอรถเมล์ตอนเกือบเที่ยงนี่มันจะสุกเอาได้
ผลงาน กทม. นี่รับประกันคุณภาพ
คราวก่อนก็มีประเด็นเรื่องป้ายโฆษณาบังสายตา
ทำให้มองไม่เห็นรถเมล์
มาคราวนี้ก็ใช้คนออกแบบที่พึ่งกลับจากอเมริกา
เลยยังไม่ชินกับสภาพอากาศเมืองไทย
(หรืออีกอย่าง ก็คือคนออกแบบที่ไม่เคยนั่งรถเมล์)
design ที่เป็น public แบบนี้
ไม่รู้ว่ามีกฎเกณฑ์การ approve design กันอย่างไรบ้าง
Related link from Roti
Tuesday, August 29, 2006
Time & Goal
Devid Seah เขาเขียนบทความ Roomba Productivity
มีอยู่ part หนึ่งที่เขาพูดถึง role of time
โดยเขาลองจับคู่ Time กับ Goal ดู
มีอยู่ part หนึ่งที่เขาพูดถึง role of time
โดยเขาลองจับคู่ Time กับ Goal ดู
- Time Specific/Specific Goal
อันนี้คือสิ่งที่เราพยายามจะเป็นกัน GTD (Get Things Done) - Time Specific/Unspecific Goal
อันนี้ด้านหนึ่งคือ model ที่ติดตัวมากับน้องๆที่พึ่งจบจากมหาวิทยาลัย
ในอีกด้าน Unspecific Goal ไม่ได้หมายความไม่ได้ทำอะไร
คนที่ทำงานจนหัวยุ่ง แต่ไม่มีเป้าชัดๆก็มี - Time Unspecific/Specific Goal
ชอบสำนวนที่เขาเปรียบเปรย "It's done when it's done" - Time Unspecific/Unspecific Goal
อันนี้สิที่น่าสนใจ
ในตอนแรกที่อ่าน ก็คิดว่าอันนี้มันต้องเป็นพวกไม่เอาไหนแน่ๆ
แต่มันมีอีกมุมแฮะ ลองไปอ่านตัวอย่างของเขาดูแล้วกัน
Related link from Roti
Monday, August 28, 2006
กีฬา, การจัดการ
วันนี้นั่งค้นข้อมูลงานกีฬายุวชนโลกที่จัดในช่วงนี้
ตั้งใจว่าจะพาลูกชายไปดูพี่ๆเขาเล่นกีฬาสักหน่อย
กว่าจะหาตารางเวลาการแข่งขันเจอนี่ ใช้เวลาอยู่นานเชียว
ส่วนใหญ่ข้อมูลที่ได้จากหนังสือพิมพ์ต่างๆ ก็คือ
งานนี้เป็นงานอะไร ใครเป็นเจ้าภาพ จัดช่วงวันที่นี้ถึงนี้นะ
ไม่ได้ให้ข้อมูลอะไรมากไปกว่านี้
พอเจอ web site หลักของงาน ก็เจอปัญหาว่า
คนออกแบบคงไม่ได้ตั้งใจออกแบบมาให้คนที่สนใจที่จะไปชมใช้
กว่าจะปะติดปะต่อข้อมูลได้ ก็ต้อง click เข้าไปหลายๆจุดหน่อย
(แถมยังต้องผ่านด่าน flash ที่น่ารำคาญในหน้าแรกเข้าไปด้วย)
เห็นความสะเปะสะปะของข้อมูล และหน้า web site แล้ว
ก็นึกถึงประสบการณ์ที่มีโอกาสได้ไปยุ่งเกี่ยวกับการจัดการแข่งขันไตรกีฬาที่นครพนม
ตอนนั้นรุ่นพี่คนหนึ่งเขามาชวนให้ผมไปเที่ยว
แล้วก็เป็น staff ในส่วนของการดูแลความปลอดภัยทางน้ำ
ก็เลยแบกถังดำน้ำ กับอุปกรณ์ดำน้ำไปจากกรุงเทพฯกัน
พอไปถึง รุ่นพี่ที่ไปด้วยเห็นคนจัดเขายุ่งๆ แล้วก็ไม่ค่อยพร้อม ก็เลยบอกไปว่า
ผมทำงานด้าน computer ,สามารถช่วยเหลือในด้านนี้ได้นะ
ไอ้เราไม่ทันตั้งเนื้อตั้งตัว ขี้เห็นใจ + ไม่ชอบปฎิเสธคน
ก็เลยได้แถมเป็นผู้ดูแลการรวมรวมและประมวลผลเวลา อีกตำแหน่ง
วันแข่งจริง พอเห็นเรือนี่เริ่มขมวดคิ้วเลย
เรือที่จัดให้เป็นเรือหางยาว ความคล่องตัวไม่ต้องพูดถึง
น้อย ถึงน้อยที่สุด
ยังดีที่มีทั้งทหารบกและทหารเรือมาช่วยเสริมก็เลยอุ่นใจขึ้น
ทหารบกที่มา เป็นกลุ่มที่ทำธุรกิจเรื่องเรือ canoe อยู่
มีเรือ canoe มาหลายลำเชียว พวกนี้ความคล่องตัวจะสูง
แถมยังบังเอิญเป็นพี่ที่เคยรู้จักกันสมัยผมไปเล่นเรือ canoe ที่นครนายกด้วย
(ผมยังเคยไปเป็น staff เรือแคนนูให้กลุ่ม ป๊อบ อารียา ด้วยนะ
หน้าที่ก็คือ ดูว่าเรือลำไหนล่ม ช่วยคนแล้วก็กู้เรือมาให้เขาเล่นต่อ)
ส่วนทหารเรือ ก็มีเรือยางซึ่งเหมาะกับการช่วยเหลือทางน้ำอย่างมาก
แค่จะเริ่มแข่งก็สนุกแล้ว
พวกการเมืองท้องถิ่น ก็เข้ามายุ่ง อยากให้ลาวเขามีเอี่ยวด้วย
ก็เลยไปเริ่ม start ที่ฝั่งลาวกัน
พอเสียงปืนดังปัง นักกีฬากระโดดน้ำ
ปัญหาแรกก็คือ แม่น้ำโขงมันไหลแรงมาก
นักกีฬา 200-300 คน ก็เลยแตกกระจายออกไปทั่วเลย
ผมนี่เครียดเลย มันจะดูทันได้ไงวะ กระจัดกระจายกันขนาดนี้
ผลของน้ำที่แรงมาก ทำให้นักกีฬาที่ว่ายน้ำกลางๆ,ว่ายน้ำไม่เก่ง
หรือว่ายเก่งแต่ไม่มีประสบการณ์ว่ายในน้ำเชี่ยว
ไหลเลยทุ่นลูกแรกไปเกือบหมด
พวกที่รู้ตัว ก็ไม่ฝืน ลัดเข้าทุ่นถัดไป
ส่วนพวกที่เคารพกฎก็เลยอยู่ในสถาพที่ว่ายทวนน้ำเข้าหาทุ่น
ด้วยความแรงของน้ำขนาดนั้น สภาพก็เลยเป็นการว่ายอยู่กับที่
หลังจากดูอาการแล้ว จะว่ายอีกกี่ชั่วโมง ก็ไม่มีทางไปถึงแน่
ผมก็ให้เรือเข้าเทียบแล้วก็สั่งให้ว่ายลัดทีละคน
มีฝรั่งบางคนจะดื้อไม่ยอมฟัง
ก็เลยต้องขึ้นเสียง บอกว่าผมเป็นกรรมการนะ
ผมขอสั่งให้คุณว่ายลัดไปที่ทุ่นนั่นเดี๋ยวนี้
(จริงๆแล้วเราไม่ใช่กรรมการหรอก)
พอจัดการกลุ่มที่มีปัญหาเสร็จ ดูแล้วผ่านหมดแน่ ก็ให้เรือเทียบฝั่ง
วิ่ง 300 เมตรปีนตลิ่งขึ้นมาดูเรื่อง computer ต่อ
(นึกแล้วตลก, ที่ไปช่วยนี่เงินก็ไม่ได้
แต่ตอนนั้นวิญญาญความรับผิดชอบมันสิงแล้ว)
ปัญหาของการรวมรวมเวลามีสองเรื่องก็คือ
1. format ของข้อมูลที่เข้ามาไม่เหมาะกับการประมวลผล
(จริงๆมันง่ายนะ ถ้ารู้ว่าต้องการ output อะไร
แต่ปัญหาก็คือ ก่อนหน้านี้ไม่มีคนดูแล)
2. ช้อมูลต้องถูกต้อง ห้ามป้อนผิด
นั่งทำจนบ่ายสองบ่ายสามแล้วก็ยังไม่เสร็จ
(เริ่มแข่งตอนเช้า)
ตอนนี้แหล่ะที่ทำให้ได้สัมผัสนักช่าวจากไทยรัฐ
เริ่มจากพวกเด็กๆมาก่อน
บอกจะรีบส่งข่าว เดี๋ยวไม่ทันปิดต้นฉบับ
เราก็บอกยังไม่เสร็จ ถ้าเสร็จแล้วจะให้
สักพัก ก็มีตัวใหญ่มา
เราก็พูดเหมือนเดิม เสร็จแล้วจะให้
พี่แกยืนไม่ยอมไปไหน
แต่ผมก็ไม่สนใจ นั่งตรวจข้อมูลต่อ
สักพักก็พูด
"คุณรู้เปล่าว่าผมเป็นใคร"
"ผมเขียนข่าวหน้า 4 ของไทยรัฐ"
"ขอรายงานผลเวลาด้วย"
ผมนี่ฉุนกึกเลย ก็เลยมองหน้ายักไหล่ แล้วก็บอกสั้นๆ
"แล้วไง, ผมไม่สนว่าคุณเป็นใครหรอก, ออกไปได้แล้ว"
วกเข้าเรื่องดีกว่า (ชักจะออกนอกเรื่องแล้ว)
ที่ตั้งใจจะบอกก็คือ การจัดการในบ้านเรานี่มันแย่จริงๆ
ไม่ว่าจะเรื่องกีฬาหรือเรื่อง IT (จริงๆแล้วทุกเรื่องนั่นแหล่ะ)
ตั้งใจว่าจะพาลูกชายไปดูพี่ๆเขาเล่นกีฬาสักหน่อย
กว่าจะหาตารางเวลาการแข่งขันเจอนี่ ใช้เวลาอยู่นานเชียว
ส่วนใหญ่ข้อมูลที่ได้จากหนังสือพิมพ์ต่างๆ ก็คือ
งานนี้เป็นงานอะไร ใครเป็นเจ้าภาพ จัดช่วงวันที่นี้ถึงนี้นะ
ไม่ได้ให้ข้อมูลอะไรมากไปกว่านี้
พอเจอ web site หลักของงาน ก็เจอปัญหาว่า
คนออกแบบคงไม่ได้ตั้งใจออกแบบมาให้คนที่สนใจที่จะไปชมใช้
กว่าจะปะติดปะต่อข้อมูลได้ ก็ต้อง click เข้าไปหลายๆจุดหน่อย
(แถมยังต้องผ่านด่าน flash ที่น่ารำคาญในหน้าแรกเข้าไปด้วย)
เห็นความสะเปะสะปะของข้อมูล และหน้า web site แล้ว
ก็นึกถึงประสบการณ์ที่มีโอกาสได้ไปยุ่งเกี่ยวกับการจัดการแข่งขันไตรกีฬาที่นครพนม
ตอนนั้นรุ่นพี่คนหนึ่งเขามาชวนให้ผมไปเที่ยว
แล้วก็เป็น staff ในส่วนของการดูแลความปลอดภัยทางน้ำ
ก็เลยแบกถังดำน้ำ กับอุปกรณ์ดำน้ำไปจากกรุงเทพฯกัน
พอไปถึง รุ่นพี่ที่ไปด้วยเห็นคนจัดเขายุ่งๆ แล้วก็ไม่ค่อยพร้อม ก็เลยบอกไปว่า
ผมทำงานด้าน computer ,สามารถช่วยเหลือในด้านนี้ได้นะ
ไอ้เราไม่ทันตั้งเนื้อตั้งตัว ขี้เห็นใจ + ไม่ชอบปฎิเสธคน
ก็เลยได้แถมเป็นผู้ดูแลการรวมรวมและประมวลผลเวลา อีกตำแหน่ง
วันแข่งจริง พอเห็นเรือนี่เริ่มขมวดคิ้วเลย
เรือที่จัดให้เป็นเรือหางยาว ความคล่องตัวไม่ต้องพูดถึง
น้อย ถึงน้อยที่สุด
ยังดีที่มีทั้งทหารบกและทหารเรือมาช่วยเสริมก็เลยอุ่นใจขึ้น
ทหารบกที่มา เป็นกลุ่มที่ทำธุรกิจเรื่องเรือ canoe อยู่
มีเรือ canoe มาหลายลำเชียว พวกนี้ความคล่องตัวจะสูง
แถมยังบังเอิญเป็นพี่ที่เคยรู้จักกันสมัยผมไปเล่นเรือ canoe ที่นครนายกด้วย
(ผมยังเคยไปเป็น staff เรือแคนนูให้กลุ่ม ป๊อบ อารียา ด้วยนะ
หน้าที่ก็คือ ดูว่าเรือลำไหนล่ม ช่วยคนแล้วก็กู้เรือมาให้เขาเล่นต่อ)
ส่วนทหารเรือ ก็มีเรือยางซึ่งเหมาะกับการช่วยเหลือทางน้ำอย่างมาก
แค่จะเริ่มแข่งก็สนุกแล้ว
พวกการเมืองท้องถิ่น ก็เข้ามายุ่ง อยากให้ลาวเขามีเอี่ยวด้วย
ก็เลยไปเริ่ม start ที่ฝั่งลาวกัน
พอเสียงปืนดังปัง นักกีฬากระโดดน้ำ
ปัญหาแรกก็คือ แม่น้ำโขงมันไหลแรงมาก
นักกีฬา 200-300 คน ก็เลยแตกกระจายออกไปทั่วเลย
ผมนี่เครียดเลย มันจะดูทันได้ไงวะ กระจัดกระจายกันขนาดนี้
ผลของน้ำที่แรงมาก ทำให้นักกีฬาที่ว่ายน้ำกลางๆ,ว่ายน้ำไม่เก่ง
หรือว่ายเก่งแต่ไม่มีประสบการณ์ว่ายในน้ำเชี่ยว
ไหลเลยทุ่นลูกแรกไปเกือบหมด
พวกที่รู้ตัว ก็ไม่ฝืน ลัดเข้าทุ่นถัดไป
ส่วนพวกที่เคารพกฎก็เลยอยู่ในสถาพที่ว่ายทวนน้ำเข้าหาทุ่น
ด้วยความแรงของน้ำขนาดนั้น สภาพก็เลยเป็นการว่ายอยู่กับที่
หลังจากดูอาการแล้ว จะว่ายอีกกี่ชั่วโมง ก็ไม่มีทางไปถึงแน่
ผมก็ให้เรือเข้าเทียบแล้วก็สั่งให้ว่ายลัดทีละคน
มีฝรั่งบางคนจะดื้อไม่ยอมฟัง
ก็เลยต้องขึ้นเสียง บอกว่าผมเป็นกรรมการนะ
ผมขอสั่งให้คุณว่ายลัดไปที่ทุ่นนั่นเดี๋ยวนี้
(จริงๆแล้วเราไม่ใช่กรรมการหรอก)
พอจัดการกลุ่มที่มีปัญหาเสร็จ ดูแล้วผ่านหมดแน่ ก็ให้เรือเทียบฝั่ง
วิ่ง 300 เมตรปีนตลิ่งขึ้นมาดูเรื่อง computer ต่อ
(นึกแล้วตลก, ที่ไปช่วยนี่เงินก็ไม่ได้
แต่ตอนนั้นวิญญาญความรับผิดชอบมันสิงแล้ว)
ปัญหาของการรวมรวมเวลามีสองเรื่องก็คือ
1. format ของข้อมูลที่เข้ามาไม่เหมาะกับการประมวลผล
(จริงๆมันง่ายนะ ถ้ารู้ว่าต้องการ output อะไร
แต่ปัญหาก็คือ ก่อนหน้านี้ไม่มีคนดูแล)
2. ช้อมูลต้องถูกต้อง ห้ามป้อนผิด
นั่งทำจนบ่ายสองบ่ายสามแล้วก็ยังไม่เสร็จ
(เริ่มแข่งตอนเช้า)
ตอนนี้แหล่ะที่ทำให้ได้สัมผัสนักช่าวจากไทยรัฐ
เริ่มจากพวกเด็กๆมาก่อน
บอกจะรีบส่งข่าว เดี๋ยวไม่ทันปิดต้นฉบับ
เราก็บอกยังไม่เสร็จ ถ้าเสร็จแล้วจะให้
สักพัก ก็มีตัวใหญ่มา
เราก็พูดเหมือนเดิม เสร็จแล้วจะให้
พี่แกยืนไม่ยอมไปไหน
แต่ผมก็ไม่สนใจ นั่งตรวจข้อมูลต่อ
สักพักก็พูด
"คุณรู้เปล่าว่าผมเป็นใคร"
"ผมเขียนข่าวหน้า 4 ของไทยรัฐ"
"ขอรายงานผลเวลาด้วย"
ผมนี่ฉุนกึกเลย ก็เลยมองหน้ายักไหล่ แล้วก็บอกสั้นๆ
"แล้วไง, ผมไม่สนว่าคุณเป็นใครหรอก, ออกไปได้แล้ว"
วกเข้าเรื่องดีกว่า (ชักจะออกนอกเรื่องแล้ว)
ที่ตั้งใจจะบอกก็คือ การจัดการในบ้านเรานี่มันแย่จริงๆ
ไม่ว่าจะเรื่องกีฬาหรือเรื่อง IT (จริงๆแล้วทุกเรื่องนั่นแหล่ะ)
Related link from Roti
Thursday, August 24, 2006
Amazon EC2
Amazon EC2 เป็นบริการใหม่ของ Amazon
ทำให้เราสามารถ สร้าง Amazon Machine Image (AMI) ที่ต้องการ
(จริงๆแล้ว มันก็คือ disk image ที่เอาไปใช้ run virtual machine)
นำไป deploy บน Amazon EC2
จากนั้นก็สามารถ start instance ตามจำนวนที่ต้องการได้
ลองดูตัวอย่างสถานะการณ์การใช้งาน
ค่าบริการคิดตามที่ใช้จริง
processing - $0.10 per instance-hour
data transfer (internet traffic) - $0.20 per GB
Storage - $0.15 per GB
(storage ใช้บริการจาก Amazon S3)
วิธีที่สร้าง AMI (Amazon Machine Image) ก็คล้ายๆกับที่เรา add guest-os ให้กับ xen
โดยต้องทำในเครื่องเราให้เรียบร้อยก่อน
จากนั้นจึง upload ขึ้นไป
ดูจากวิธีการสร้าง image แล้ว
ตัว infrastructure ของ EC คงใช้ fedora กับ xen
ข้อจำกัดอีกอย่างก็คือ
ตัว server ต้องใช้ dynamic IP เท่านั้น
ข้อดีที่เห็นชัดๆ ก็คือ เราสามารถเพิ่ม instance ได้ตามต้องการ
ทำให้การตอบสนองต่อพวก spike load ทำได้โดยง่าย
อย่างนี้ A-NET, O-NET ก็น่าไปใช้บริการนะ
เพราะปีหนึ่งมีประกาศผลแค่ไม่กี่ครั้ง แต่ต้องลงทุน server ไปกับ spike load
ทำให้เราสามารถ สร้าง Amazon Machine Image (AMI) ที่ต้องการ
(จริงๆแล้ว มันก็คือ disk image ที่เอาไปใช้ run virtual machine)
นำไป deploy บน Amazon EC2
จากนั้นก็สามารถ start instance ตามจำนวนที่ต้องการได้
ลองดูตัวอย่างสถานะการณ์การใช้งาน
Mary wishes to deploy her public, fault tolerant, three tier web service in Amazon EC2. Her grand plan is to have her web tier start off executing in seven instances of ami-fba54092, her application tier executing in twenty instances of ami-e3a5408a, and her multi-master database in two instances of ami-f1a54098.
ค่าบริการคิดตามที่ใช้จริง
processing - $0.10 per instance-hour
data transfer (internet traffic) - $0.20 per GB
Storage - $0.15 per GB
(storage ใช้บริการจาก Amazon S3)
วิธีที่สร้าง AMI (Amazon Machine Image) ก็คล้ายๆกับที่เรา add guest-os ให้กับ xen
โดยต้องทำในเครื่องเราให้เรียบร้อยก่อน
จากนั้นจึง upload ขึ้นไป
ดูจากวิธีการสร้าง image แล้ว
ตัว infrastructure ของ EC คงใช้ fedora กับ xen
ข้อจำกัดอีกอย่างก็คือ
ตัว server ต้องใช้ dynamic IP เท่านั้น
ข้อดีที่เห็นชัดๆ ก็คือ เราสามารถเพิ่ม instance ได้ตามต้องการ
ทำให้การตอบสนองต่อพวก spike load ทำได้โดยง่าย
อย่างนี้ A-NET, O-NET ก็น่าไปใช้บริการนะ
เพราะปีหนึ่งมีประกาศผลแค่ไม่กี่ครั้ง แต่ต้องลงทุน server ไปกับ spike load
Related link from Roti
Functional test with Selenium RC
ลองใช้ Selenium มาทั้ง 3 แบบแล้ว
core, firefox extension, remote-control
สุดท้ายก็ชอบแบบ remote-control มากที่สุด
โดยเฉพาะการใช้ร่วมกับ ruby
ภาพรวมการทำงานของ Selenium remote-control ก็คือ
เราจะ start selenium server ขึ้นมาตัวหนึ่ง
เวลาจะ test เราก็จะเขียน program เพื่อส่ง command ที่ต้องการ test
ไปยัง server, โดย server จะสื่อสารและสั่งงานให้ browser ทำตาม command ที่เราสั่ง

ที่นี้ลองมาดูการทำงานภายในของ Selenium ดูบ้าง
วิธีการที่ selenium ใช้ควบคุม browser ก็คือการใช้ javascript
เช่นสมมติว่าเราต้องการให้เกิดการ click ที่ปุ่มๆหนึ่ง
ในแง่ของ test script เราก็จะส่งคำสั่งต่อไปนี้
ทีนี้ selenium จะทำอย่างไร ปุ่มนี้จึงจะ click ได้
ขั้นที่ 1 ก็คือหา element ที่ต้องการ click ให้เจอก่อน
โดยวิธีการหา จะขึ้นอยู่กับ prefix ที่เรากำหนดไปใน parameter
เช่นถ้ากำหนด
แต่ถ้าเป็นพวก
หรือถ้านำหน้าด้วย
ขั้นที่ 2 ก็คือเมื่อเจอ element ที่ต้องการแล้ว ก็ต้องทำให้เกิดการ click ขึ้นมา
ลองดูตัวอย่างของการ simulate mouse click ใน Firefox ดู
จะเห็นว่า มันทำการ simulate event
สุดท้ายก็ส่ง event
ส่วนการทำงานภายในของ function
ถ้าเป็น mozilla ดูเหมือนจะใช้วิธีนี้
จะเห็นได้ว่า วิธีการทำงานนั้นน่าสนใจทีเดียว
ประเด็นที่ตามมาของการใช้ javascript control ก็คือ
javascript นั้นจะ run ใน domain name ที่เราต้องการ test อย่างไร
เนื่องจาก javascript จะทำงานได้กับ domain name ที่เรา load javascript นั้นมาเท่ากัน
Selenium เลยใช้วิธีการ start proxy server ขึ้นมา
เพื่อบังคับให้ request ของ browser วิ่งผ่านมันก่อน
เพื่อที่มันจะได้ inject javascript ที่ต้องการลงไปได้

หันไปดูด้านผู้ใช้ดูบ้าง
ลองดูตัวอย่าง ruby code ที่ผมเขียน test
เวลา run, Selenium ก็จะเปิด browser ทำงาน
โดย browser มีหน้าตาแบบนี้
core, firefox extension, remote-control
สุดท้ายก็ชอบแบบ remote-control มากที่สุด
โดยเฉพาะการใช้ร่วมกับ ruby
ภาพรวมการทำงานของ Selenium remote-control ก็คือ
เราจะ start selenium server ขึ้นมาตัวหนึ่ง
เวลาจะ test เราก็จะเขียน program เพื่อส่ง command ที่ต้องการ test
ไปยัง server, โดย server จะสื่อสารและสั่งงานให้ browser ทำตาม command ที่เราสั่ง

ที่นี้ลองมาดูการทำงานภายในของ Selenium ดูบ้าง
วิธีการที่ selenium ใช้ควบคุม browser ก็คือการใช้ javascript
เช่นสมมติว่าเราต้องการให้เกิดการ click ที่ปุ่มๆหนึ่ง
<a href="xxxx" id="myButton">click me</a>
ในแง่ของ test script เราก็จะส่งคำสั่งต่อไปนี้
cmd = "click"
param1 = "id=myButton"
ทีนี้ selenium จะทำอย่างไร ปุ่มนี้จึงจะ click ได้
ขั้นที่ 1 ก็คือหา element ที่ต้องการ click ให้เจอก่อน
โดยวิธีการหา จะขึ้นอยู่กับ prefix ที่เรากำหนดไปใน parameter
เช่นถ้ากำหนด
id= นำหน้า, selenium ก็จะใช้วิธี getElementByIdแต่ถ้าเป็นพวก
xpath=//img[@alt='The image alt text'] ก็จะใช้ xpathหรือถ้านำหน้าด้วย
css= ก็จะใช้ css selector ในการหาขั้นที่ 2 ก็คือเมื่อเจอ element ที่ต้องการแล้ว ก็ต้องทำให้เกิดการ click ขึ้นมา
ลองดูตัวอย่างของการ simulate mouse click ใน Firefox ดู
MozillaPageBot.prototype.clickElement = function(element, clientX, clientY) {
triggerEvent(element, 'focus', false);
// Add an event listener that detects if the default action has been prevented.
// (This is caused by a javascript onclick handler returning false)
var preventDefault = false;
...
// Trigger the click event.
triggerMouseEvent(element, 'click', true, clientX, clientY);
...
if (this.windowClosed()) {
return;
}
triggerEvent(element, 'blur', false);
};
จะเห็นว่า มันทำการ simulate event
focus จากนั้นก็ทำการสร้าง event mouse clickสุดท้ายก็ส่ง event
blurส่วนการทำงานภายในของ function
triggerMouseEventถ้าเป็น mozilla ดูเหมือนจะใช้วิธีนี้
var evt = document.createEvent('MouseEvents');
.. // init event
element.dispatchEvent(evt);
จะเห็นได้ว่า วิธีการทำงานนั้นน่าสนใจทีเดียว
ประเด็นที่ตามมาของการใช้ javascript control ก็คือ
javascript นั้นจะ run ใน domain name ที่เราต้องการ test อย่างไร
เนื่องจาก javascript จะทำงานได้กับ domain name ที่เรา load javascript นั้นมาเท่ากัน
Selenium เลยใช้วิธีการ start proxy server ขึ้นมา
เพื่อบังคับให้ request ของ browser วิ่งผ่านมันก่อน
เพื่อที่มันจะได้ inject javascript ที่ต้องการลงไปได้

หันไปดูด้านผู้ใช้ดูบ้าง
ลองดูตัวอย่าง ruby code ที่ผมเขียน test
def setup
@domain = 'http://wcfweb.test:8080'
@sel = Selenium::SeleneseInterpreter.new("selenium.server.test", 4444,
"*firefox", @domain, 15000)
@sel.start
@home = "#{@domain}/fin/app?page=wcf2000/Wcf2110_1&service=page"
end
def submit_cheque(no, date, bank, amount)
to_page2
@sel.click("link=0.00")
type_cheque(no, date, bank, amount)
calc_charge
@sel.click("save_btn")
@sel.wait_for_page_to_load(15000)
end
def t_cheque_amount_entry_but_no_other_info
submit_cheque("", "", "", 2000)
assert(@sel.is_text_present("กรุณาระบุเลขที่เช็ค"))
assert(@sel.is_text_present("กรุณาระบุวันที่เช็ค"))
assert(@sel.is_text_present("กรุณาระบุรหัสธนาคาร"))
end
def t_cheque_entry_wrong_date
submit_cheque("1234567", nextDay(2), "", 2000)
assert(@sel.is_text_present("วันที่เช็ครับลงล่วงหน้ามากกว่า 1 วัน"))
end
def t_cheque_entry_wrong_bank_code
submit_cheque("1234567", nextDay(1), 1234, 2000)
assert(! @sel.is_text_present("วันที่เช็ครับลงล่วงหน้ามากกว่า 1 วัน"))
assert(@sel.is_text_present("รหัสธนาคารไม่ถูกต้อง"))
end
เวลา run, Selenium ก็จะเปิด browser ทำงาน
โดย browser มีหน้าตาแบบนี้
Related link from Roti
Wednesday, August 23, 2006
Dojo Render Time
ช่วงแรกๆที่ใช้ Dojo จะยังไม่เจอปัญหาเรื่อง Render Time.
แต่พอในหน้าจอเริ่มมี element มากขึ้น
เวลาที่ใช้ในการ render หน้าจอ จะเริ่มช้าลงอย่างเห็นได้ชัด
ปัญหาก็คือ
เวลาเราใช้ dojo เรามักจะใช้ในรูปแบบนี้
ประเด็นก็คือ แล้ว dojo จะรู้ได้ไงว่าต้องเปลี่ยน dom element นี้ให้เป็น dojo component
Dojo ก็เลยต้อง scan document ทั้งอัน เพื่อที่จะหาว่ามี attribute dojotype อยู่ที่ไหนบ้าง
ยิ่งหน้าจอเรามี dom element เยอะขึ้นเท่าไร ก็ยิ่งเสียเวลามากขึ้นเท่านั้น
การแก้ไขก็คือ จำกัดวงไม่ให้ dojo ต้อง scan เอกสารทั้งหมด
เริ่มด้วยการ set djconfig
จากนั้นในแต่ละ element ที่ต้องการให้เป็น dojo component
ก็ให้ใส่ script นี้ลงไป
แต่พอในหน้าจอเริ่มมี element มากขึ้น
เวลาที่ใช้ในการ render หน้าจอ จะเริ่มช้าลงอย่างเห็นได้ชัด
ปัญหาก็คือ
เวลาเราใช้ dojo เรามักจะใช้ในรูปแบบนี้
<span dojoType="tooltip"
connectId="entryField"
caption="xxxxxx"/>
ประเด็นก็คือ แล้ว dojo จะรู้ได้ไงว่าต้องเปลี่ยน dom element นี้ให้เป็น dojo component
Dojo ก็เลยต้อง scan document ทั้งอัน เพื่อที่จะหาว่ามี attribute dojotype อยู่ที่ไหนบ้าง
ยิ่งหน้าจอเรามี dom element เยอะขึ้นเท่าไร ก็ยิ่งเสียเวลามากขึ้นเท่านั้น
การแก้ไขก็คือ จำกัดวงไม่ให้ dojo ต้อง scan เอกสารทั้งหมด
เริ่มด้วยการ set djconfig
<script>
djConfig = {
..
parseWidgets: false,
searchIds: [],
..
};
</script>
จากนั้นในแต่ละ element ที่ต้องการให้เป็น dojo component
ก็ให้ใส่ script นี้ลงไป
<span dojoType="tooltip"
connectId="entryField"
caption="xxxxxx"
id="this_is_my_dojo"/>
<script>djConfig.searchIds.push("this_is_my_dojo");</script>
Related link from Roti
Label กับ Form
Label Placement in Forms
บทความนี้เขา track ลูกตา กับ form ที่ align ในแบบต่างๆ
แล้วสรุปให้เราดูว่า ข้อดีข้อเสียเป็นอย่างไร
บทความนี้เขา track ลูกตา กับ form ที่ align ในแบบต่างๆ
แล้วสรุปให้เราดูว่า ข้อดีข้อเสียเป็นอย่างไร
Related link from Roti
Monday, August 21, 2006
Closures for Java
มี proposol แบบนี้ออกมา ค่อยรู้สึกมีความหวังกับภาษานี้หน่อย
(หลังจากปล่อยให้ C# นำหน้าไปนาน)
ดูตัวอย่างแรกสุดก่อน
ในที่สุด Kingdom of Nouns อย่าง java ก็มี verb citizen โผล่เข้ามาแล้ว
ในบรรทัดแรก
ส่วนบรรทัดที่ 2 ก็คือการกำหนดให้
บรรทัดที่ 1 และ 2 จากตัวอย่างข้างบน สามารถยุบเขียนในรูป anonymous function ได้แบบนี้
แถมด้วย syntactic sugar
แทนที่จะต้องเขียนแบบนี้
ก็สามารถเขียนแบบนี้ได้
(หลังจากปล่อยให้ C# นำหน้าไปนาน)
ดูตัวอย่างแรกสุดก่อน
public static void main(String[] args) {
int plus2(int x) { return x+2; }
int(int) plus2b = plus2;
System.out.println(plus2b(2));
}
ในที่สุด Kingdom of Nouns อย่าง java ก็มี verb citizen โผล่เข้ามาแล้ว
ในบรรทัดแรก
plus2 ก็คือ Type ชนิด functionส่วนบรรทัดที่ 2 ก็คือการกำหนดให้
plus2b เป็นตัวแปรที่ reference ไปยัง function plus2บรรทัดที่ 1 และ 2 จากตัวอย่างข้างบน สามารถยุบเขียนในรูป anonymous function ได้แบบนี้
int(int) plus2b = (int x) {return x+2; };
แถมด้วย syntactic sugar
แทนที่จะต้องเขียนแบบนี้
void sayHello(java.util.concurrent.Executor ex) {
ex.execute((){ System.out.println("hello"); });
}
ก็สามารถเขียนแบบนี้ได้
void sayHello(java.util.concurrent.Executor ex) {
ex.execute { System.out.println("hello"); }
}
Related link from Roti
Sunday, August 20, 2006
Emacs & xft
ตั้งแต่ใช้ Emacs มา มีเรื่องขัดใจอยู่อย่างเดียว
ก็คือ text ของ emacs ใน linux มันไม่เป็น anti-aliased
วันนี้ก็ลอง build emacs จาก branch ที่เขา implement feature xft ดู
ขั้นตอนก็คือ
checkout จาก trunk ก่อน
จากนั้นก็สั่ง update จาก branch
สั่ง configure
(ตรงนี้ distro แต่ละ distro คงไม่เหมือนกันนะ
ถ้าดูเอกสารที่คุณ poonlap แนะนำ จะไม่ต้องมี --x-includes กับ --x-libraries
กรณีของผมเป็น fedora core 5, ถ้าไม่มีจะ compile ไม่ผ่าน)
จากนั้นก็สั่ง
เท่านี้ก็ได้ emacs ฉบับสวยงาม
แต่ข้อเสียก็คือ มันแสดงภาษาไทยไม่ได้
เศร้า
(poonlap, ถ้าอ่านเจอ ก็ช่วยหาทางแก้ให้หน่อยนะ)
เครื่องผมก็เลยมี emacs มัน 2 แบบเลย
ถ้าดู code ภาษาอังกฤษก็ใช้ build with xft
ถ้าภาษาไทย ก็ใช้แบบธรรมดาไป
Update: มีปัญหา Segment fault เกิดขึ้นบ่อยมาก
ถ้า switch ไปใช้ workspace อื่น
ก็คือ text ของ emacs ใน linux มันไม่เป็น anti-aliased
วันนี้ก็ลอง build emacs จาก branch ที่เขา implement feature xft ดู
ขั้นตอนก็คือ
checkout จาก trunk ก่อน
cvs -z3 -d:pserver:anonymous@cvs.savannah.gnu.org:/cvsroot/emacs co emacs
จากนั้นก็สั่ง update จาก branch
cd emacs
cvs up -Pd -r XFT_JHD_BRANCH
สั่ง configure
(ตรงนี้ distro แต่ละ distro คงไม่เหมือนกันนะ
ถ้าดูเอกสารที่คุณ poonlap แนะนำ จะไม่ต้องมี --x-includes กับ --x-libraries
กรณีของผมเป็น fedora core 5, ถ้าไม่มีจะ compile ไม่ผ่าน)
./configure --with-xft --with-gtk --x-includes=/usr/include/X11 --x-libraries=/usr/lib/X11
จากนั้นก็สั่ง
make bootstrap
make install
เท่านี้ก็ได้ emacs ฉบับสวยงาม
แต่ข้อเสียก็คือ มันแสดงภาษาไทยไม่ได้
เศร้า
(poonlap, ถ้าอ่านเจอ ก็ช่วยหาทางแก้ให้หน่อยนะ)
เครื่องผมก็เลยมี emacs มัน 2 แบบเลย
ถ้าดู code ภาษาอังกฤษก็ใช้ build with xft
ถ้าภาษาไทย ก็ใช้แบบธรรมดาไป
Update: มีปัญหา Segment fault เกิดขึ้นบ่อยมาก
ถ้า switch ไปใช้ workspace อื่น
Related link from Roti
Friday, August 18, 2006
Ruby กับ Ingres
หลังจอเฝ้ารอว่าจะมีใครเขียน adapter ให้ ruby สามารถคุยกับ ingres ได้
ในที่สุด ก็รอไม่ไหว หันไปหาทางเลือกอื่น
นั่นคือ ช่องทาง ODBC
solution ก็คือ
ขั้นตอนถัดไป ระหว่างรอให้มี ODBC Adapter(สำหรับ rails) ดีๆออกมา
ก็จะทดลองเขียน rails adapter ที่ต่อเข้ากับ ruby dbi ไปพลางๆก่อน
(ใช้ตัวอย่างจาก sqlserver adapter ที่มี mode ที่ใช้ odbc ด้วย)
ในที่สุด ก็รอไม่ไหว หันไปหาทางเลือกอื่น
นั่นคือ ช่องทาง ODBC
solution ก็คือ
- unixODBC
อันนี้ไม่มีอะไรพิเศษ ติดตั้งได้จากพวก Package Manager ของ Distro ได้เลย
ประเด็นถัดมา ก็คือการสร้าง datasource
ถ้าสร้างด้วยมือ ดูจะโหดร้ายไปหน่อย
ingres ก็เลยเตรียม utility ที่ชื่อiiodbcadmn
- Ruby ODBC
ตัวนี้มีปัญหานิดหน่อย
ตรงวิธีที่เขาเขียน
เนื่องจากเขาเขียนโดยอิง mysql เป็นหลัก
พอมาใช้กับ database ตัวอื่น ก็อาจจะเป็นปัญหาบ้าง
ในกรณีของ ingres ถ้าลอง build แล้ว run testcase ดูจะพบ error นี้[pphetra@fedora ruby-odbc-0.9991]$ ruby test.rb sso
connect.............ok
create_table........ok
insert.............../test/20insert.rb:6:in `call': 37000 (2913)
[unixODBC][Ingres][Ingres ODBC Driver][Ingres]line 1, You cannot assign
a value of type 'varchar' to a column of type 'integer'.
Explicitly convert the value to the required type. (ODBC::Error)
from ./test/20insert.rb:6
from test.rb:20
from test.rb:16
ถ้าเข้าไปดูใน testcase จะเห็น code ที่มีปัญหา หน้าตาเป็นแบบนี้$p = $c.proc("insert into test (id, str) values (?, ?)") {}
$p.call(3, 'FOO')
ประเด็นที่เป็นปัญหา ก็คือวิธีที่ใช้ในการ binding parameter เข้ากับ statement
หลังจากไล่ source code ของ ruby-odbc ซักพัก ผมก็เจอจุด quickfix
ก็เลยแก้ง่ายๆลงไป@@ -6265,6 +6266,7 @@
if (coldef == 0) {
switch (ctype) {
case SQL_C_LONG:
+ stype = SQL_NUMERIC;
coldef = 10;
break;
case SQL_C_DOUBLE: - Ruby DBI
ตัวนี้ไม่มีอะไรพิเศษ
ผมเลือกลงแต่ dbd ที่ต่อเข้า ODBC อย่างเดียว
ขั้นตอนถัดไป ระหว่างรอให้มี ODBC Adapter(สำหรับ rails) ดีๆออกมา
ก็จะทดลองเขียน rails adapter ที่ต่อเข้ากับ ruby dbi ไปพลางๆก่อน
(ใช้ตัวอย่างจาก sqlserver adapter ที่มี mode ที่ใช้ odbc ด้วย)
Related link from Roti
Monday, August 14, 2006
เรื่องเล่าจากทางเหนือ
สมัยเรียนหนังสือ ถึงช่วงปิดเทอมเมื่อไร
ก็จะทำกิจกรรมค่ายอาสา
ขึ้นดอยไปใช้ชีวิตกับชาวเขา
กลางวันช่วยสอนหนังสือเด็ก
กลางคืนช่วยสอนหนังสือผู้ใหญ่
บางวันก็ไปช่วยทำไร่
จบมาแล้ว เวลาเห็นข่าวที่เกี่ยวกับชาวเขา
ก็รู้สึกว่าเป็นความผูกพันที่ต้องติดตามอ่าน
วันนี้อ่านเจอ blog ของ คุณจตุพร วิศิษฎ์โชติอังกูร
ที่ทำงานอยู่ในพื้นที่
เขามีเรื่องเล่าหลายเรื่องที่น่าสนใจทีเดียว
แต่มีอยู่เรื่องที่อ่านแล้วก็เกิดอาการสะดุดทางใจ
เรียนรู้ชีวิตกลางคืน.. ผ่านบาร์อะโกโก้
คือเมื่อก่อนผมก็เคยรวบรวมข้อมูลหญิงบริการที่เป็นชาวเขา
ได้มีโอกาสเห็นสมุดบัญชีของพวกซ่อง ที่จดจำนวนการใช้บริการ
เห็นตัวเลขแล้ว ก็รู้สึกช็อคเหมือนกัน
อธิบายไม่ถูก
คืออยากเห็นคนปฎิบัติต่อคนในฐานะเพื่อนมนุษย์ด้วยกัน
มากกว่าเป็นแค่วัตถุรองรับอารมณ์
ก็จะทำกิจกรรมค่ายอาสา
ขึ้นดอยไปใช้ชีวิตกับชาวเขา
กลางวันช่วยสอนหนังสือเด็ก
กลางคืนช่วยสอนหนังสือผู้ใหญ่
บางวันก็ไปช่วยทำไร่
จบมาแล้ว เวลาเห็นข่าวที่เกี่ยวกับชาวเขา
ก็รู้สึกว่าเป็นความผูกพันที่ต้องติดตามอ่าน
วันนี้อ่านเจอ blog ของ คุณจตุพร วิศิษฎ์โชติอังกูร
ที่ทำงานอยู่ในพื้นที่
เขามีเรื่องเล่าหลายเรื่องที่น่าสนใจทีเดียว
แต่มีอยู่เรื่องที่อ่านแล้วก็เกิดอาการสะดุดทางใจ
เรียนรู้ชีวิตกลางคืน.. ผ่านบาร์อะโกโก้
คือเมื่อก่อนผมก็เคยรวบรวมข้อมูลหญิงบริการที่เป็นชาวเขา
ได้มีโอกาสเห็นสมุดบัญชีของพวกซ่อง ที่จดจำนวนการใช้บริการ
เห็นตัวเลขแล้ว ก็รู้สึกช็อคเหมือนกัน
อธิบายไม่ถูก
คืออยากเห็นคนปฎิบัติต่อคนในฐานะเพื่อนมนุษย์ด้วยกัน
มากกว่าเป็นแค่วัตถุรองรับอารมณ์
Related link from Roti
Hivemind #2
ลองดูเรื่อง Configuration บ้าง
concept ของ Configuration ใน Hivemind ก็คือ
เวลาเรานำ service ไปใช้, service ย่อมต้องการ parameter บางอย่าง
เพื่อที่จะได้กำหนดพฤติกรรมให้เหมาะกับสภาพแวดล้อมที่ service run อยู่
ใน Hivemind เราแบ่งส่วนของ configuration ออกเป็น 2 ส่วนคือ
ดูตัวอย่างดีกว่า
สมมติเรามี service ตัวหนึ่งชื่อ FooService
ในตัวอย่างข้างบนนี้ serverInfos ก็คือ configuration point
ที่เราอยากให้สามารถปรับเปลี่ยนได้ตามบริบทที่ service นี้ไป run อยู่
ดังนั้นใน descriptor file ของเรา เราก็จะกำหนดดังนี้
เริ่มด้วยการกำหนด configuration point ก่อน
ให้สังเกตว่าเรามีการ define schema ของรูปแบบของข้อมูลด้วย
จากนั้นก็กำหนดบอกว่า FooService เราต้องการ information จาก configuration-point นี้นะ
สุดท้าย เวลาที่เราต้องการ customize กำหนดค่า serverInfo
เราจะทำผ่าน contribution
โดยรูปแบบข้อมูล ต้องตรงตามที่กำหนดเป็น schema ไว้ใน configuration-point
ลองเปรียบเทียบกับ spring บ้าง
ตามตัวอย่างข้างบนนี้ spring จะเขียนเป็นแบบนี้
ดูแค่นี้ ก็จะเห็นว่า spring มี configuration ที่ดูเข้าใจง่ายกว่า (จริงๆแล้วขึ้นกับว่า ผู้ดูเป็นใคร?)
แต่ hivemind มีข้อได้เปรียบกว่า spring ตรงที่
contribution ของ hivemind มัน auto-discovery ได้
คือเราสามารถแยก component ออกเป็น module ย่อยๆ
โดยแต่ละ module ก็มี descriptor file ของตัวเอง
ดังนั้นเราอาจจะ package service ไว้ใน module หนึ่ง
แล้วอาจจะ pack contribution ไว้ในอีก module หนึ่ง
เมื่อ registry ของ hivemind load ขึ้นมา มันจะมองหา
descriptor file ที่อยู่ใน classpath ทั้งหมด แล้วประกอบขึ้นมาเป็น registry ให้โดยอัติโนมัติ
concept ของ Configuration ใน Hivemind ก็คือ
เวลาเรานำ service ไปใช้, service ย่อมต้องการ parameter บางอย่าง
เพื่อที่จะได้กำหนดพฤติกรรมให้เหมาะกับสภาพแวดล้อมที่ service run อยู่
ใน Hivemind เราแบ่งส่วนของ configuration ออกเป็น 2 ส่วนคือ
- ส่วนที่ difine นิยาม และการนำไปใช้
เรียกตรงนี้ว่า Configuration Point - ส่วนที่ provide configuration data
มีศัพท์เรียกว่า Contribution
ดูตัวอย่างดีกว่า
สมมติเรามี service ตัวหนึ่งชื่อ FooService
public interface FooService {
public void connect();
}
public class FooServiceImpl implements FooService {
private List<ServerInfo> serverInfos;
/**
* @param serverInfo the serverInfo to set
*/
public void setServerInfos(List<ServerInfo> infos) {
this.serverInfos = infos;
}
/* (non-Javadoc)
* @see test.FooService#connect()
*/
public void connect() {
System.out.println("connect to " + serverInfos.get(0).getHost());
}
}
public class ServerInfo {
private String host;
private String port;
private String uri;
// setter & getter ..
}
ในตัวอย่างข้างบนนี้ serverInfos ก็คือ configuration point
ที่เราอยากให้สามารถปรับเปลี่ยนได้ตามบริบทที่ service นี้ไป run อยู่
ดังนั้นใน descriptor file ของเรา เราก็จะกำหนดดังนี้
เริ่มด้วยการกำหนด configuration point ก่อน
<configuration-point id="serverInfo" >
<schema>
<element name="serverInfo">
<attribute name="host" required="true"/>
<attribute name="port" required="false"/>
<attribute name="uri" required="true"/>
<conversion class="test.ServerInfo"/>
</element>
</schema>
</configuration-point>
ให้สังเกตว่าเรามีการ define schema ของรูปแบบของข้อมูลด้วย
จากนั้นก็กำหนดบอกว่า FooService เราต้องการ information จาก configuration-point นี้นะ
<service-point id="foo" interface="FooService">
<invoke-factory>
<construct class="impl.FooServiceImpl">
<set-configuration property="serverInfos" configuration-id="serverInfo"/>
</construct>
</invoke-factory>
</service-point>
สุดท้าย เวลาที่เราต้องการ customize กำหนดค่า serverInfo
เราจะทำผ่าน contribution
โดยรูปแบบข้อมูล ต้องตรงตามที่กำหนดเป็น schema ไว้ใน configuration-point
<contribution configuration-id="serverInfo">
<serverInfo host="128.1.1.10" uri="/app/service"/>
</contribution>
ลองเปรียบเทียบกับ spring บ้าง
ตามตัวอย่างข้างบนนี้ spring จะเขียนเป็นแบบนี้
<bean id="fooService" class="test.impl.FooServiceImpl">
<property name="serverInfos">
<list>
<bean class="test.ServerInfo">
<property name="host" value="128.1.1.10"/>
<property name="uri" value="/app/service"/>
</bean>
</list>
</property>
</bean>
ดูแค่นี้ ก็จะเห็นว่า spring มี configuration ที่ดูเข้าใจง่ายกว่า (จริงๆแล้วขึ้นกับว่า ผู้ดูเป็นใคร?)
แต่ hivemind มีข้อได้เปรียบกว่า spring ตรงที่
contribution ของ hivemind มัน auto-discovery ได้
คือเราสามารถแยก component ออกเป็น module ย่อยๆ
โดยแต่ละ module ก็มี descriptor file ของตัวเอง
ดังนั้นเราอาจจะ package service ไว้ใน module หนึ่ง
แล้วอาจจะ pack contribution ไว้ในอีก module หนึ่ง
เมื่อ registry ของ hivemind load ขึ้นมา มันจะมองหา
descriptor file ที่อยู่ใน classpath ทั้งหมด แล้วประกอบขึ้นมาเป็น registry ให้โดยอัติโนมัติ
Related link from Roti
Subscribe to:
Posts (Atom)
