Saturday, April 21, 2007

ตัวอย่าง aspect อย่างง่าย

ในโพสต์นี้ผมแนะนำตัวอย่าง aspect ที่น่าจะเข้าใจได้ไม่ยากนักสำหรับการเริ่มต้นหัดเขียน AOP
โจทย์คือ "สร้าง aspect สำหรับจับเวลาให้เมธอดในคลาส Target" ครับ

1. ให้สร้่าง aspect เป็นชื่อ TimeMeasurement ตั้ง package เป็นอะไรก็ได้ เช่น net.ckblog.aspect.TimeMeasurement จะได้ไฟล์ชื่อ TimeMeasurement.aj ซึ่งจะคอมไพล์ได้ด้วยตัวคอมไพเลอร์ของ AspectJ
2. ให้สร้างคลาส ชื่อ Target อาจจะเป็น net.ckblog.Target แล้วให้มี 2 เมธอด ชื่อ compute1000() กับ compute2000() แล้วลองเขียนโค้ดให้วนรอบใน 2 เมธอดนั้น 1000 รอบ และ 2000 รอบ ตามลำดับ
จากนั้นให้เตรียมคลาส Main เพื่อสร้าง object ของ Target แล้วทำการเรียกใช้ทั้งสองเมธอดดังกล่างใน main()
3. กลับมาที่ aspect TimeMeasurement ให้ประกาศ pointcut โดยเราจะใช้ pointcut designator (PCD) ชื่อ execution() ในการเลือก join point 2 จุด ณ ตำแหน่งการทำงานของเมธอด ของ compute1000() และ compute2000()
ซึ่งเหตุผลในการใช้ PCD มาจากวิธีคิดแบบนี้ครับ
- การทำงานของเมธอด คือ execution of method (ต่างจากการเรียกใช้ คือ call)
- นั่นคือความหมายของ PCD ชื่อ execution(...) (ถ้าเป็นการเรียกใช้ก็จะเป็น PCD ชื่อ call())
และเป้าหมายของเราคือเมธอด compute1000() และ compute2000
จะได้ pointcut ดังต่อไปนี้

execution(public Target.compute1000()) ||
execution(public Target.compute2000())

ความหมายคือ เราสร้าง pointcut จาก PCD มากกว่า 1 ตัว และในกรณีนี้ เราใช้ PCD 2 ตัวทำการ or กัน
ในการใช้งานจริง จะสามารถยุบรวมให้เป็น PCD เดียวโดยใช้ wildcard ซึ่งจะเป็นตามโค้ดต่อไปนี้ครับ

execution(public Target.compute*())

(หมายเหตุ: pointcut นี้จะเลือกทุก ๆเมธอดของคลาส Target ที่ชื่อเมธอด ขึ้นต้นด้วย compute นะครับ
นั่นคือถ้าเีราเพิ่ม compute3000() เข้าไป method ใหม่ก็จะโดนเลือกด้วย ซึ่งจะทำให้ความหมายไม่ตรงเป๊ะ ๆ กับ code แรก
แต่ผมต้องการแสดงให้เห็นว่าเราสามารถเลือกเชิงประมาณโดยใช้ wildcard ได้)

4. ต่อมาเราก็จะประกาศ advice เพื่อให้ทำงาน ณ ตำแหน่ง join point ที่เราเลือกมาด้วย pointcut ในข้อ 3 ครับ
ก่อนจะไปต่อ ผมเล่าให้ฟังนิดนึงว่า join point มีสมบัติสำคัญ ๆ คือ
1) เราเข้ามาที่จุด join point ได้
2) เมื่อมาถึงแล้ว เราทำงานที่จุด join point ได้
3) และเมื่อมาถึงแล้ว เราออกจากจุด join point ได้

ซึ่ง advice ก็คือคำแนะนำที่เราบอกกับโปรแกรมว่าควรจะทำอะไรที่จุด join point นี้

ตาม aspect เราตั้งใจว่าจะ "จับเวลา" การทำงานของ 2 method
นั้นคือเราะจะมีการจับเวลา "ก่อน" และ "หลัง" ในแต่ละ join point ที่เลือกไว้ด้วย pointcut ในข้อ 3 ครับ
ตรงนี้เราจะใช้ before() เพื่อนิยามการแนะนำขณะเข้า join point
และ after() เพื่อนิยามการแนะนำขณะออกจาก join point
ดังนั้นเราก็จะได้

before(): <ชื่อ pointcut>() {
// จับเวลาใส่ตัวแปร
}

after(): <ชื่อ pointcut>() {
// จับเวลา แล้วเอาไปลบกับตัวแปรที่ตั้งค่าไว้ในโค้ดของ before
// แล้ว System.out.println(เวลา);
}


แล้วก็สั่งรันโปรแกรม ก็เป็นอันเสร็จพิธีครับ

ในตัวอย่างที่ผมยกมาให้ดูนี้ ผมใช้ join point model แบบ pointcut-advice เพื่่ออธิบาย aspect สำหรับ"จับเวลา" โดยสร้าง pointcut จากการใช้ PCD 2 ชุดมา or กัน เพื่อเลือก (quantify) join point 2 ตำแหน่งใน class Target แล้วก็สร้าง advice เพื่อแนะนำให้โปรแกรมทำการจับเวลา "ก่อน" และ "หลัง" จุดที่เลือกมาด้วย pointcut ผมตั้งใจละ code ไว้ให้ผู้อ่านลองนำไปเขียนเพิ่มเติมเองนะครับ

สำหรับ join point อีก model ที่มีใน AspectJ คือ inter-type declaration หรือเรียกว่า introduction จะกล่าวถึงในโพสต์อื่น ๆ ครับ

หมายเหตู ผมโพสต์ตอบแบบเดียวกันนี้ไว้ในกระดานข่าวของ narisa.com กระทู้นี้ครับ

10 comments:

rchatsiri said...

หากเคยเขียน java มาก่อนจะเขียนโปรแกรมเชิงลักษณะ ง่ายขึ้นไหมครับ..

chanwit said...

สวัสดีครับ

ถ้ามองในเชิงความคล้ายกันของภาษา ภาษาโปรแกรมเชิงลักษณะ (AOP) อย่าง AspectJ ก็ใช้ syntax ลักษณะเดียวกันกับ Java ครับ
ถ้ามองจาก concept จะต่างกันมาก นั่นคือ
Java เป็นภาษาโปรแกรมเชิงวัตถุ ซึ่ง มีแนวคิดของ class, object, interface, encapsulation, overloading, polymorphism
ในขณะที่ AspectJ เป็นภาษาโปรแกรมเชิงลักษณะ ที่มีแนวคิดเพิ่มเติมคือ aspect, join point, pointcut, advice และ intertype-declaration

แต่จะยังไง AspectJ ก็สร้างมาเพื่อให้คนเรียนรู้ Java สามารถเรียนรู้ AOP ผ่านทางมันอยู่แล้วหล่ะครับ

คำตอบคือ ง่ายขึ้นมากครับ :)

Anonymous said...

ไม่ทราบว่าเคยผ่านตางานวิจัยเกี่ยวกับการเขียนโปรแกรมเชิงลักษณะหรือ crosscutting concern ที่เอาไปใช้กับ design pattern บ้างไหมครับ แล้วเขาเอาไปใช้แนวไหนบ้าง ขอบคุณทุกๆ ความเห็นล่วงหน้าน่ะคราบ

chanwit said...

@Anonymous:

มีครับ เป็นลักษณะการเขียน OO pattern ด้วย AOP ครับ ลองดูใน paper นี้ครับ
http://tinyurl.com/yrl9hm

ผมเคยคุยกับเพื่อนใน lab ถึงหัวข้อวิจัยเกี่ยวกับการพัฒนา design pattern ให้ aspect คือสร้าง pattern ของ aspect (แทนที่จะเป็นของ OO เหมือนด้านบน) ดูเหมือนพอจะทำได้ แต่ยังไม่ได้ข้อสรุปชันเจน หาสนใจเอาไปคิดต่อเป็นงานวิจัยก็ได้นะครับ

Anonymous said...

ขอถามต่อน่ะครับ
1. คือยังไม่ค่อยเข้าใจเท่าไร คือหมายถือสร้าง design pattern ของ aspect โดยมีลักษณะหรือรูปแบบหรือวัตถุประสงค์การใช้งานเป็นแบบเดียวกับ design pattern ของ OO หรือครับ เช่น command design pattern ของ OO ก็เขียน command design pattern ในรูปแบบของ aspect แทนใช่หรือป่าวครับ ถ้าเป็นแบบนี้ดูเหมือนจะทำคนทำแล้ว(หมายถึงตัว code นะครับ ไม่แน่ใจว่าเป็นงานวิจัยหรือป่าว)
2. หรือว่าเป็นการค้นหาความซ้ำซ้อนของ code ใน desing pattern ของ OO แล้วถ้าเจอก็นำมาเขียนแก้ไขความซ้ำซ้อนของ code นั้นด้วย aspect
3. paper ที่ให้มานี่เป็น paper ของเพื่อนใน lap ที่ได้พูดถึงหรือป่าวครับ

ขอขอบคุณทุกๆ ความคิดเห็นล่วงหน้าน่ะครับ

chanwit said...

1., 2.
ทีู่ post ให้ดูคือ oo pattern ในรูป aop แต่ที่คิดต่อออกมาคือ จะทำให้มี ao pattern แล้วหาวิธีบางอย่างมาอธิบายมัน ซึ่งตอนนี้ก็ยังไม่ตกผลึกกันครับ (แปลว่าแนวคิดนี้อาจจะมีหรือไม่มีอยู่จริงก็ได้ :) )

3. ปล่าวครับ

Anonymous said...

1. ถ้าเราคิดแค่จะจัดการเรื่องความซ้ำซ้อนของ code โดยไม่ได้คำนึกถือเรื่อง concern เลย เราจะเรียกว่าการทำแบบนี้จะเป็น AOP หรือป่าวครับ
2. ในการเขียนโปรแกรมเชิงลักษณะ(AOP) นี่้ต้องมองที่ concern ของโปรแกรมอย่างเดียวหรือป่าว
3. การที่เรามองที่ concern อย่างเดียวนี่จะครอบคลุมเรื่อง scattering และ tangling หรือป่าว

ต้องขอโทษด้วยถ้าเขียนไม่รู้เรื่อง แบบว่าพึ่งเริ่มต้นเรียนรู้เรื่อง AOP อ่ะครับ และขอบคุณทุกๆ ความเห็นล่วงหน้าน่ะคราบ

chanwit said...

1. เรียกว่า AOP ได้ครับ เพียงแต่เป็น for non-specific concern

2., 3. จริง ๆ แล้ว AOP นี่ออกแบบมาสำหรับ "cross-cutting" concerns ครับ
แล้ว crosscutting concerns ก็มีสมบัติ (ที่ไม่ดี) คือ code tangling

หามอง concern ในลักษณะอื่นที่ไม่ใช่ crosscutting (ซึ่่งมีอีกมากและต้องแก้ด้วยวิธีอื่น เช่น subject-oriented, agent-oriented) ก็จะไม่เกี่ยวข้องอะไรกับ scattering และ code tangling
เพราะเีราใช้ AOP solve ไม่ได้ครับ

นั่นคือ separation of concerns เป็นเรื่องนึง และ AOP จัดการกับ crosscutting concerns ซึ่งเป็น subset ของ concerns ทั้งหมดครับ

> ต้องขอโทษด้วยถ้าเขียนไม่รู้เรื่อง
> แบบว่าพึ่งเริ่มต้นเรียนรู้เรื่อง AOP อ่ะครับ
> และขอบคุณทุกๆ ความเห็นล่วงหน้าน่ะคราบ

ยินดีครับ

Anonymous said...

ใน AspectJ นี้สามารถ dynamically เลือก aspect หรือ pointcut ที่จะใช้ได้หรือไม่ (upon behavior) เช่น
1. ถ้า user เป็น role นี้ จะใช้ aspect/pointcut นี้ซึ่งมี privilege สูงกว่าหรือต่ำกว่าอีก role หนึ่ง เช่น อาจต้องการ second-level authentication หรือต้องการ log การกระทำของ user ที่ละเอียดขึ้นหรือซับซ้อนขึ้น
2. ถ้าเป็นช่วงเวลานี้หรือสถานการณ์นี้ (temporal event) จะใช้ aspect/pointcut นี้ซึ่งต่างจากอีก aspect/pointcut หนึ่ง เช่น ถ้าเป็นกลางวันหรือ connection ปรกติ จะติดต่อกับ remote database แต่ถ้าเป็นกลางคืนหรือ connection lost จะติดต่อกับ local database แทนไปก่อน
3. ถ้า user เลือกทำงานในโมดหนึ่ง จะใช้ aspect/poincut หนึ่ง แล้วเมื่อ user switch การทำงานไปอีกโมดหนึ่ง จะเลือกอีก aspect/pointcut หนึ่งทำงาน เช่น ในโมดหนึ่งอาจมี data encryption แต่ในอีกโมด ไม่มี data encryption หรือมีต่าง encryption scheme กัน
4. ถ้าคำตอบของคำถามเหล่านี้อยู่ที่การใช้ if then else ใน aspect/pointcut เพื่อเลือกว่าจะใส่ behavior ไหน ก็คงดูไม่จืดนัก จะเลือก instantiate sub-aspect ได้เหมือน OOP ที่เลือก instantiate subclass ได้หรือไม่ ถ้าได้ จะสามารถ switch aspects ได้กลางอากาศ (at runtime upon user behavior) หรือไม่
5. จากที่ได้ศึกษาอย่างหยาบ ๆ ในเบื้องต้น ดูเหมือนว่า aspect ไม่ถูก treat อยู่ใน level เดียวกับ class/object ดังนั้น จะส่ง aspect ไปมาเป็น parameter ก็คงไม่ได้ หรือว่าในระดับ advanced ของ AspectJ นั้น จะสามารถ treat aspect ได้เหมือน first-class item ใน program อย่างตัวแปร (เหมือน ๆ กับที่ functional programming languages treat functions as first-class items and can be sent as parameters and etc.) ความเข้าใจนี้ผิดถูกแค่ไหนครับ

ขอบคุณล่วงหน้าที่ช่วยไขข้อข้องใจครับ
simple@narisa.com
(ไม่ใช่ email นะ)

chanwit said...

@simple

1., 2., 3., 4. ได้ครับ กลไกการ weaving มี 2 กลุ่มใหญ่ dynamic weaving (ตอน runtime) และ static weaving (ตอน compile-time) สำหรับที่คุณ simple ถาม เป็นการเปลี่ยนแปลง/นิยาม aspect ตอน runtime ก็จะใช้ dynamic weaving ครับ
ผม implement AOP ชนิดนี้ให้กับ Groovy อยู่ แต่สำหรับ AspectJ ยังไม่ support แบบนี้ 100%
แต่จะพบได้ใน JBoss AOP และ research AOP system บางตัวครับ

5. ถูกครับ แต่ต้องพูดว่า property นึงของ AOP คือ object และ method (และทุก ๆ join point) จะต้องไม่ aware การมีตัวตนอยู่ของ aspect ครับ อย่างดีที่สุดในการ reasoning aspect ก็คือ annotation ครับ เรื่องนี้มีเหตุผลอยู่ แต่ผมจะไม่เฉลยให้หมดนะครับ ลองไปค้นต่อดู ;-)