Posts Tagged ‘IOC’

Swiz Framework IoC สำหรับ Flex

January 15th, 2009

java มี spring , coldfusion มี coldspring แล้ว
แนวคิด IoC ก็ยังลามไปยัง Flex ด้วยครับ

Swiz เป็น IoC Framework สำหรับ Flex จากที่ดูโค้ดตัวอย่างคราวๆ
การ colfig beans นั้นจะใช้ mxml ไปเลย ส่วนการ ดึง Bean นั้นจะใช้
Metatag (เหมือน Annotation ในจาวานั่นเองครับ) ช่วยในการกำหนด
ตัวแปรที่จะให้ Swiz ทำการฉีด Bean เข้ามาให้นั่นเองครับ

blog Brian Kotek ตอนนี้ก็กำลังเขียน Tutorial อยู่ลองเข้าไปติดตามกันได้
Using Swiz Part 1: Initial Setup
Using Swiz Part 2: Dependency Injection
และนี่ก็เว็บของ project Swiz ครับ

http://code.google.com/p/swizframework/

ColdSpring, IOC สำหรับ ColdFusion ขยายความ

January 8th, 2009

ColdSpring ก็ได้แรงบัลดาลใจมาจาก Spring นั่นเองครับ วิธีการใช้ก็แถบเรียกได้ว่าลอกมาเช่นกัน คือใช้ xml ในการ config bean

เดี๋ยวผมจะเอาตัวอย่าง MovieLister ใน post นี้   Inversion Of Control และ Dependency Injection มาเขียนในแบบ coldfusion

ใน coldfusion ถ้าเราจะเขียนในลักษณะ oop คือมีการสร้างคลาส มีการสร้าง object นั้น จะสร้างได้โดยไปเขียน coldfusion component ซึ่งก็ concept เดียวกันกับ class นั่นเองครับ คือมี properties มี method (หรือ function) ประกาศไว้ ให้เราไป createObject ขึ้นมาเรียกใช้งานได้
ตัวอย่าง component MovieLister (MovieLister.cfc)


<cfcomponent>
    <cffunction name="setFinder">
        <cfargument name="finder" require="true" />
        <cfset variables.finder = arguments.finder />
    </cffunction>

    <cffunction name="getFinder" >
        <cfreturn variables.finder />
    </cffunction>

    <cffunction name="moviesDirectedBy" >
        <cfargument name="arg" require="true" />
        <cfset var allMovies = variables.finder.findAll() / >
        <cfset var findResult = ArrayNew(1) / >
        <cfset var movie = "" / >
        <cfset var i = "" / >
        <cfloop from="1" to="#ArrayLen(allMovies)#" index="i" >
            <cfset movie = allMovies[i] / >
            <cfif movie.getDirector() eq arguments.arg >
                <cfset arrayAppend(findResult,movie) / >
            </cfif>
        </cfloop>
        <cfreturn findResult />
    </cffunction>
</cfcomponent>

และ สมมุติมี component ColonMovieFinder ที่มีฟังก์ชัน findAll อยู่

<cfcomponent>
    <cffunction name="setFilename" >
        <cfargument name="fileName" require="true" >
        <cfset variables.fileName = arguments.fileName >
    </cffunction>
    <cffunction name="findAll">
        ...
    </cffunction>
</cfcomponent>

ส่วนการ config นั้นใช้ xml เหมือน spring (ลอกมาเลย)

<beans>
        <bean id="MovieLister" class="spring.MovieLister">
            <property name="finder">
                <ref bean="MovieFinder"/>
            </property>
        </bean>
        <bean id="MovieFinder" class="spring.ColonMovieFinder">
            <property name="filename">
                <value>movies1.txt</value>
            </property>
        </bean>
</beans>

เวลาเอาไปใช้ก็แบบนี้

<cfset coldspringConfig = '/coldspring/examples/quickstart/config/coldspring.xml' />
<cfset beanFactory = CreateObject('component', 'coldspring.beans.DefaultXmlBeanFactory').init() />
<cfset beanFactory.loadBeans(coldspringConfig) />

<cfset lister = beanFactory.getBean('MovieLister')>
<cfset movies = lister.moviesDirectedBy('Sergio Leone') >

ประมาณนี้ครับ นอกจากนั้น coldspring ยังช่วยให้เขียนแบบ AOP ด้วย coldfusion ก็ได้ด้วย แล้วก็ ใช้จัดการกับ Remote Component ได้ด้วยครับ

ColdSpring, IOC สำหรับ ColdFusion

January 8th, 2009

เพิ่งอ่านข่าวนี้จาก TheServerSide ครับ ต๊กก่ะใจเคยฮ่ะว่ามีคนเอาแนวคิดเรื่อง IOC หรือ DI ไปยัดใส่ ColdFusion ตัวผมเองไม่มีความรู้เรื่อง ColdFusion นะครับแต่ที่สำคัญคือขณะนี้แคินวคิดเดรื่อง IOC ได้รุกคืบไปทุกสารทิศแล้ว

รายละเอียดของเฟรมเวิร์คนี้สามารถอ่านได้ที่ ColdSpringFramework.org

Inversion Of Control และ Dependency Injection

January 6th, 2009

เอกสารนี้เขียนไว้ที่ thaidev.org ครับเมื่อประมาณสี่ปีที่แล้วเป็นจุดเริ่มต้นที่ผมเริ่มศึกษาสปริง เนื่องจาก IOC คือแก่นของสปริงดังนั้นใครจะใช้สปริงไม่รู้เรื่อง IOC เนี่ยโคตรบาป ขอเอามาแปะในนี้ต่อนะครับ
Inversion of control or Dependency Injection
เอกสารนี้มีต้นตอมาจาก http://martinfowler.com/articles/injection.html ครับ
เนื่องจากปัจจุบันมีการพูดถึงแนวคิดเรื่อง Lightweight Container กันมากขึ้นในกลุ่มผู้ใช้จาวาและมีการสร้าง container แบบนี้ขึ้นหลายโปรเจคแต่จริงๆแล้วโปรเจคเหล่านี้ได้ทำตามแนวคิดของ Design Pattern ที่ชื่อ Dependency Injection หรือ Inversion of Control
แนวคิดต่างๆนี้พรั่งพรูออกมาในกลุ่มคนชอบเปิดเผย (Open Source) เพื่อที่จะหาทางเลือกอะไรสักอย่างที่สามารถเข้ามาแทนผลิตภัณฑ์หลักที่แสนจะซับซ้อนของ J2EE แนวคิดหลักๆคือจะรวมเอาองค์ประกอบหลักๆของการทำเวบที่ต่างกันเข้าด้วยกันได้อย่างไรยกตัวอย่างเช่นจะรวมเอาเวบคอมโปเนนท์ กับเดต้าเบสคอมโปเนนท์ที่ทำมาจากเทคโนโลยีที่ต่างกันเข้าได้อย่างไร บางโปรเจคไม่สามารถแก้ไขปัญหานี้ได้แต่ในทางกลับกันบางโปรเจคสามารถแก้ไขปัญหานี้ได้ด้วยความสามารถในการรวมคอมโปเนนท์ต่างๆเข้าด้วยกันเป็นชั้นๆไปเช่น PicoContainer หรือ Spring
Component และ Service
ก่อนที่จะเข้าสู่เรื่องของ Inversion Of Control จะขอทำความเข้าใจความแตกต่างระหว่าง Component และ Service ในมุมมองของ Martin Fowler เสียก่อน จริงๆแล้วทั้งสองอย่างเหมือนกันตรงที่ต่างเป็นก้อนของซอฟท์แวร์ที่ทำหน้าที่ใดหน้าที่หนึ่งโดยเฉพาะและไม่สามารถหรือยากที่จะเปลี่ยนแปลง แต่สิ่งที่ทำให้สองสิ่งแตกต่างกันคือ Component จะถูกใช้แบบ Locally ส่วน Service จะถูกใช้แบบ Remotely ซึ่งในบทความนี้จะใช้คำว่า Service เป็นส่วนใหญ่
Native Example
การอธิบายแนวคิดต่างๆในบทความนี้จะใช้ตัวอย่างนี้เป็นหลักซึ่งเป็นตัวอย่างเล็กๆที่สามารถเห็นภาพได้ชัดเจนแต่อาจไม่มีจริงในโลกภายนอกได้
โดยตัวอย่างนี้จะใช้ ชุดรายชื่อของภาพยนต์กับชื่อของผู้กำกับโดยจะมีหน้าตาดังนี้

class MovieLister...
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}

หน้าที่หลักของ moviesDirectedBy(String arg) คือการตามล่าหารายชื่อภาพยนต์ที่กำกับโดยผู้กำกับที่ต้องการออกมาจุดนี้เองเป็นจุดเริ่มต้นของบทความเพราะปัญหาไม่ได้อยู่ที่เราจะหาภาพยนต์เหล่านั้นอย่างไรแต่เราจะสร้าง finder ขึ้นมาใช้งานได้อย่างต่างหาก ดังนั้นถ้าเรามี finder interface คลาสหน้าตาแบบนี้

public interface MovieFinder {
List findAll();
}

ดังนั้นถ้าเราต้องการใช้ finder นี้เราจะต้องทำการสร้างมันขึ้นมาซึ่งวิธีการด้านล่างถือเป็นหนึ่งในหลายๆวิธี

class MovieLister...
private MovieFinder finder;
public MovieLister() {
finder = new ColonDelimitedMovieFinder("movies1.txt");
}

คือการสร้างไว้ใน constructor ของ MovieLister ถึงจุดนี้จะเป็นได้ว่าทุกๆอย่างน่าจะทำงานได้ดีแล้วปัญหามันอยู่ตรงไหน เรื่องจะเริ่มจากถ้าคลาส finder นี้ถูกใช้งานด้วยคนๆเดียวและข้อมูลทั้งหมดถูกเก็บไว้ในไฟล์ movies1.txt และข้อมูลถูกแบ่งด้วย colon ก็จะไม่มีอะไรแต่ถ้ามีเพื่อนโปรแกรมเมอร์มีความต้องการจะใช้ finder นี้ด้วยแต่เกิดไม่ชอบใจเรื่องการเก็บรายชื่อเพราะต้องการเก็บแบบอื่นหรือเก็บใน Database หรือเรียกจาก web-services เมื่อนั้นแหละครับที่เราต้องมานั่งเปลี่ยนการสร้าง instance ของ finder ให้ตรงกับสิ่งที่เราต้องการใช้ อันนี้คือปัญหา

จากรูปด้านบนจะเห็นได้ว่า MovieLister ขึ้นกับทั้ง MovieFinder และ MovieFinderImpl ซึ่งจริงๆแล้วไม่เป็นการดีแต่ก็หลีกเลี่ยงไม่ได้เพราะไม่รู้ว่าจะสร้าง Instance ได้อย่างไรจะทำยังไงดีให้ MovieLister ขึ้นกับ MovieFinder อย่างเดียว
วิธีการคือแทนที่เราจะต้องมานั่งจัดการเรื่อง Instance เองเรามอง MovieFinderImpl เป็น Plugin แทนโดยที่แนวคิดเรื่อง Plugin นี้จะช่วยในการแยก Implement คลาสออกมาและ Implement คลาสจะถูกเสียบหรือกำหนดค่าให้เมื่อต้องการใช้งาน และนี่คือจุกเริ่มต้นของ Inversion of control

Inversion of Control
แต่คำถามถัดมาคือแล้ว contianer ที่ใช้ Inversion of control มีดีอะไรหรือ? ข้อดีของการทำ Inversion of control คือสามารถแยกโปรแกรมของเราออกจาก service ได้อย่างเต็มที่โดยที่จะมี Assembler เป็นผู้ช่วยในการกำหนดหรือฉีดค่า service ที่เราต้องการใช้เข้ามาในเวลาที่เราต้องการ
ดังนั้นจากพฤติกรรมของการทำงานผู้เขียนจึงขอตั้งชื่อให้ Inversion of control ใหม่ว่า Dependency Injection และเนื่องจากการทำ dependency injection สามารถทำได้หลายแบบจากนี้จะเป็นการแสดงรายละเอียดของแต่ละแบบ และเสนอวีอื่นที่สามารถกำจัดการขึ้นต่อกันของ คลาสกับ service เช่นการใช้ service locator

Forms of Dependency Injection
แนวคิดหลักของการทำ dependency injection คือการแยก เอา assembler คลาสที่ทำการจัดหา service ที่เหมาะสมให้กับผู้ใช้ หรือยกตัวอย่างเช่นการหา finder implement ที่เหมาะสมให้กับ Lister คลาสดังรูป

การทำ Dependency Injection สามารถทำได้แบบดังนี้คือ Constructor Injection (Ioc1), Setter Injection (Ioc2) และ Interface Injection.(Ioc3)
Constructor Injection with PicoContainer
การทำ injection แบบแรกคือการทำงานของ PicoContainer โดยที่ PicoContainer จะใช้ constructor เมธอดในการจับ finder ใส่ใน lister ดังนั้นด้วยวิธีนี้ lister คลาสต้องกำหนด constructor ที่รวมทุกๆอย่างที่ต้องการใช้ดังนี้

class MovieLister...
public MovieLister(MovieFinder finder) {
this.finder = finder;
}

และตัว finder เองก็ต้องถูกบริหารจัดการโดย pico จะต้องใช้ชื่อไฟล์ที่ใช้เก็บรายชื่อภาพยนต์โดยชื่อนี้จะถูกจับใส่โดย container อีกเช่นกัน

class ColonMovieFinder...
public ColonMovieFinder(String filename) {
this.filename = filename;
}

และตัว container เองก็ต้องได้รับข้อมูลเกี่ยวกับ implementation คลาสและชื่อไฟล์ที่ต้องการใช้โดยสามารถทำได้ดังนี้

private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer();
Parameter[] finderParams =  {new ConstantParameter("movies1.txt")};
pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);
pico.registerComponentImplementation(MovieLister.class);
return pico;
}

โดยที่ configuration นี้โดยปกติจะถูกเขียนแยกกันไปตามแล้วแต่คนใช้นอกจากนี้ยังสามารถเขียนโปรแกรมให้อ่านค่าจาก ไฟล์ได้อีกด้วยแต่ PicoContainer ไม่มีความสามารถนี้แต่มีในโปรเจคที่มีความเกี่ยวเนื่องกันคือ NanoContainer ที่สามารถอ่าน configuration file ที่เป็น XML ได้
การใช้งาน container สามารถทำได้ดังนี้

public void testWithPico() {
MutablePicoContainer pico = configureContainer();
MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class);
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}

Setter Injection with Spring
ตัวอย่างนี้จะใช้ Spring ที่เป็น Framework ที่มีชื่อเสียงมากทางด้านนี้โดย Spring เองใช้ stter injection เป็นหลักในการทำงานดังนั้นการทำงานจะต้องสร้าง set เมธอดที่ Lister คลาสดังนี้

class MovieLister...
private MovieFinder finder;
public void setFinder(MovieFinder finder) {
this.finder = finder;
}

ในขณะเดียวกันเราก็ต้องสร้างใน FinderImpl ดังนี้

class ColonMovieFinder...
public void setFilename(String filename) {
this.filename = filename;
}

ข้อดีอีกอย่างของ Spring คือใช้ XML Configuration

<beans>
<bean id="MovieLister" class="spring.MovieLister">
<property name="finder">
<ref local="MovieFinder"/>
</property>
</bean>
<bean id="MovieFinder" class="spring.ColonMovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>

และสามารถใช้งานได้ดังนี้

public void testWithSpring() throws Exception {
ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");
MovieLister lister = (MovieLister) ctx.getBean("MovieLister");
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}

Interface Injection
แบบที่สามซึ่งเป็นแบบสุดท้ายคือ Interface Injection นี้ถูกใช้โดย Avalon Project ซึ่งค่อนข้างยุ่งยากเล็กน้อยแต่ก็สามารถใช้งานตามตัวอย่างได้ดังนี้โดยเริ่มจากการสร้าง

public interface InjectFinder {
void injectFinder(MovieFinder finder);
}

คลาสนี้จะต้องถูก Implement โดยใครก็จามที่ต้องการใช้ service นี้เช่น Lister

class MovieLister implements InjectFinder...
public void injectFinder(MovieFinder finder) {
this.finder = finder;
}

ในขณะเดียวกันก็สามารถทำได้กับชื่อไฟล์ใน Finder

public interface InjectFinderFilename {
void injectFilename (String filename);
}
class ColonMovieFinder implements MovieFinder, InjectFinderFilename......
public void injectFilename(String filename) {
this.filename = filename;
}

และเราก็ต้องการใช้ค่า configuration ต่างๆโดยสามารถเสกขึ้นมาเรียบๆได้ดังนี้

class Tester...
private Container container;

private void configureContainer() {
container = new Container();
registerComponents();
registerInjectors();
container.start();
}

ขั้นที่สองของการทำงานคือการ register component สามารถทำได้ดังนี้

class Tester...
private void registerComponents() {
container.registerComponent("MovieLister", MovieLister.class);
container.registerComponent("MovieFinder", ColonMovieFinder.class);
}

ขั้นต่อมาคือการ register injector

class Tester...
private void registerInjectors() {
container.registerInjector(InjectFinder.class, container.lookup("MovieFinder"));
container.registerInjector(InjectFinderFilename.class, new FinderFilenameInjector());
}
public interface Injector {
public void inject(Object target);
}

หมดแรงครับ พรุ่งนี้จะเขียน Service Locator ต่อครับ