Monday, July 30, 2007

กลไก Dependency ใน Smalltalk

ช่วงนี้กำลังสนใจเรื่อง binding ของ Java GUI อยู่
หลังจากที่ค้นคว้า JGoodies binding, Spring RCP binding แล้ว
ก็เลยรู้ว่าทั้งคู่ได้ แนวคิด มาจาก ValueModel ของ VisualWork Smalltalk
ผมก็เลยอยากรู้ว่า smalltalk ตััวจริง เขามีแนวคิดในการ implement อย่างไรบ้าง

จากการแกะ code ของ ValueModel ใน smalltalk ก็พบว่ารากฐานของ ValueModel ก็คือกลไก Dependency
ซึ่งถ้าเปรียบเทียบกับฝั่ง java แล้วก็คือพวก กลไกพวก PropertyChangeSupport, PropertyChangeEvent ใน java bean
แต่ที่ต่างกันอย่างมาก ก็คือ ในฝั่ง Java กลไกพวกนี้ ถ้าอยากได้ เราต้อง implement มันเอง
แต่ในฝั่ง Smalltalk, กลไกพวกนี้มันฝังอยู่ใน Object class เลย
ดังนั้น object ทุกตัวใน virtual machine เรียกใช้กลไกนี้ได้ทั้งหมด

เริ่มต้นที่ Model Object ที่เราต้องการให้เป็นคน fire changed event ก่อน
ใน smalltalk ถ้าเราต้องการ fire change ก็ทำได้โดยเรียกใช้ method changed
(implement อยู่ใน Object class)
price: aPrice
| oldPrice |
oldPrice := price.
price := aPrice.
self changed: #price with: oldPrice.


ส่วน class ที่สนใจจะ track change ที่เกิดขึ้น ก็ทำได้โดย
1. register ตัวเองเข้ากับ Model object ที่ต้องการ track
โดยใช้ method addDependent
initialize: aStock
aStock addDependent: self

2. override method ที่ชื่อ update ซึ่งจะถูก call จาก model object เมื่อมีการเปลี่ยนแปลง
update: aSymbol with: oldPrice from: aStock
Transcript cr; print: thisContext; tab; print: aSymbol; tab.
aSymbol == #price ifTrue:
[Transcript
nextPutAll: 'Price changed from: '; print: oldPrice;
nextPutAll: ' to '; print: aStock price].
Transcript flush.

จากวิธีข้างบน เราจะเห็นว่า เรามี if statement ที่ใช้ตรวจสอบว่า message ที่ส่งมานั้นเป็น message ที่เราสนใจหรือเปล่า
ดูแล้วในทางปฏิบัติมันไม่น่าจะ work เท่าไร เนื่องจากถ้ามี message มากๆ
if statement หรือ switch statement ก็จะยาว และทำให้ code ดูเลอะเทอะ

เพื่อให้ code เรากระชับขึ้น
เราสามารถเลือก register เฉพาะ change ที่เราสนใจได้โดยใช้ method "expressInterestIn"
โดยเปลี่ยน code ข้างบนในข้อ 1 เป็นดังนี้แทน
initialize: aStock
aStock expressInterestIn: #price
for: self
sendBack: #priceChanged:by:

จากนั้นก็เปลี่ยน implement method update ในข้อ 2
เป็นชื่อที่เรา send เป็น parameter sendBack:
priceChanged: oldPrice by: aStock
Transcript
cr; print: thisContext; tab;
nextPutAll: 'Price changed from: '; print: oldPrice;
nextPutAll: ' to '; print: aStock price; flush


ปัญหาของ Dependency ใน Smalltalk ก็เป็นเช่นเดียวกับ Java
นั่นก็คือต้องระวังเรื่อง memory leak ที่เกิดจาก Garbage collector ไม่สามารถ remove object ได้
เนื่องจากมี dependency ค้างอยู่

กรณีตัวอย่างที่ยกมาข้างบน จะมีปัญหา memory leak ได้ง่ายได้
เนื่องจากกลไกการ implement ของ dependency list มันทำที่ระดับ class variable
เขาจึงแนะนำว่า ตัว Model Class ของเรา ควรจะ extends จาก Model class จะดีกว่า
เนื่องจาก Model Class มันจะ override กลไกการใช้ class variable มาใช้ instance variable
ในตัวเองแทน ทำให้ลดโอกาสเกิด memory leak ได้

Related link from Roti