Saturday, June 02, 2007

ต้นไม้

ภาพถ่ายของ Jocke Berglund



links

Related link from Roti

Wednesday, May 30, 2007

จ่าย,ไม่จ่าย - ซื้อ, ไม่ซื้อ

Washington Post มีบทความน่าสนใจเรื่อง
Mental Accounting

เขาเกริ่นนำว่า
สมมติว่าเราไปซื้อตั๋วดูหนัง 170 บาท
แล้วเกิดทำตั๋วหนังหายไป
ถามว่า เราจะซื้อตั๋วหนังใบใหม่ไหม

กับอีกสถานการณ์หนึ่ง
สมมติว่าเราไปซื้อตั๋วหนังเหมือนกัน
แต่ก่อนจะซื้อ
เราเปิดกระเป๋าตังค์แล้วพบว่า
เงินเราหายไป 170 บาท
ถามว่า เราจะซื้อตั๋วดูหนังหรือไม่

ด้วยมูลค่าการสูญเสียที่เท่ากัน
(จากการทดลอง พบว่า)
46 % ของกลุ่มแรก ตกลงใจซื้อตั๋ว
88 % ของกลุ่มหลัง ตัดสินใจซื้อตั๋ว

Related link from Roti

Monday, May 28, 2007

Tapestry 5 Ioc

เมื่อรู้ว่า T5 เปลี่ยนวิธี config ไปแล้ว
ผมในฐานะที่มี learning style เป็นแบบที่ต้องทดลองทำด้วยตัวเองจึงจะเข้าใจ,
ก็เลยลองทดสอบ config tapestry5 IoC เปรียบเทียบกับ spring

เริ่มด้วยการสมมติสถานการณ์ก่อน
สมมติว่าเรามี service ที่ใช้ในการ upload content เข้า repository
public interface UploadService {

public void upload(InputStream input, String type);

}

การทำงานภายในของ service นี้ก็คือ
1. ทำการ parse content, ซึ่งวิธี parse ก็ขึ้นอยู่กับ type ของ content
2. delegate การจัดเก็บ content ให้กับ RepositoryService เป็นคนทำ
3. ทำ index ของ content นั้น โดย delegate ให้กับ IndexService เป็นคนจัดการ
public class UploadServiceImpl implements UploadService {

private IndexService indexService;
private RepositoryService repositoryService;
private Map<String, Parser> parserMap = new HashMap<String, Parser>();

public void upload(InputStream input, String type) {
Parser parser = parserMap.get(type);
Document document = parser.parse(input);
Long key = repositoryService.persistent(document);
indexService.index(document, key);
}

public void setIndexService(IndexService indexService) {
this.indexService = indexService;
}

public void setRepositoryService(RepositoryService repositoryService) {
this.repositoryService = repositoryService;
}

public void setParserMap(Map<String, Parser> parserMap) {
this.parserMap = parserMap;
}
}

ถ้าร้อย service ด้วย spring เราจะต้องเขียนดังนี้
<beans>
<bean id="indexService" class="test.service.impl.IndexServiceImpl"/>

<bean id="repositoryService" class="test.service.impl.RepositoryServiceImpl"/>

<bean id="uploadService" class="test.service.impl.UploadServiceImpl">
<property name="indexService" ref="indexService"/>
<property name="repositoryService" ref="repositoryService"/>
<property name="parserMap">
<map>
<entry>
<key><value>pdf</value></key>
<bean class="test.service.impl.PdfParser"/>
</entry>
<entry>
<key><value>word</value></key>
<bean class="test.service.impl.MsWordParser"/>
</entry>
</map>
</property>
</bean>
</beans>


พอมาเป็น Tapestry5 IoC
สิ่งที่เปลี่ยนไปอย่างมาก ก็คือ "ไม่มี xml อีกแล้ว"
เมื่อไม่มี xml, เราก็ต้อง define module descriptor เป็น java แทน
public class MyAppModule {

/**
* bind ใช้กับกรณีพวก simple bean, ไม่ต้องมีอะไรให้ set มากมาย
*/

public static void bind(ServiceBinder binder) {
binder.bind(RepositoryService.class, RepositoryServiceImpl.class);
binder.bind(IndexService.class, IndexServiceImpl.class);
}

/**
* กรณีพวก bean ที่ซับซ้อนมากขึ้น ก็ให้ตั้งชื่อ method ว่า build นำหน้า
* แล้วก็กำหนด parameter ตาม dependency ที่ต้องการ
* โดยลำดับจะเป็นอะไรก่อนหลังก็ได้
*/

public static UploadService buildUploadService(
IndexService indexer,
RepositoryService repositoryService,
Map<String, Parser> configuration) {

UploadServiceImpl impl = new UploadServiceImpl();
impl.setIndexService(indexer);
impl.setRepositoryService(repositoryService);
impl.setParserMap(configuration);
return impl;
}

/**
* ตรงนี้แหล่ะที่ tapestry5 IoC ยังเหนือกว่า Spring
* เราสามารถกำหนด contribute ที่ทำหน้าที่ provide configuration ให้กับ builder ข้างบน
* โดย contribute method สามารถ plugin เข้ามาจาก module อื่นๆได้
* ทำให้มันมีลักษณะเป็น modular มากกว่า spring
*/

public static void contributeUploadService(MappedConfiguration<String, Parser> configuration) {
configuration.add("pdf", new PdfParser());
configuration.add("word", new MsWordParser());
}
}

เวลา run ก็
        public void run() {
RegistryBuilder builder = new RegistryBuilder();
builder.add(MyAppModule.class, ParserModule.class);

Registry registry = builder.build();

UploadService service = registry.getService(UploadService.class);
service.upload(null, "pdf");
}

ขยายความเพิ่มเติมเรื่อง contribution ของ Tapestry5 อีกนิดหนึ่ง
contribution ใน T5 (จริงๆมันมีตั้งแต่ version 4 แล้ว)
มันมีลักษณะเป็น aggregate นั่นคือ มันจะ scan หา contribution ทั้งหมดที่มี
ไม่ว่าจะอยู่ใน jar file หรือใน classpath
จากนั้น จึง aggregate และส่งต่อให้ builder method
,feature ที่ไกล้เคียงสุดของ spring สำหรับกรณีนี้ ก็คือ collection merging (spring version 2)

Note: ทั้ง spring และ tapestry5 IoC ต่างมี feature พวก auto binding ทั้งคุ่
แต่ผมไม่นิยมใช้ เนื่องจากการประกาศว่าต้องใช้อะไรบ้าง มันชัดเจนกว่าในตอนที่ต้องไล่ code เพื่อแก้ไขโปรแกรม

Related link from Roti