low level programmer

Main | Next page »
星期五 一月 29, 2010

counting sort

description

覺得如果不是寫程式的話, counting sort 還蠻難想像的.
照自己以為的 counting sort 寫出來, 如果有錯請指導了謝謝.

reference

這是 wiki 上的說明以及 C 的實作.

codes


package test;

public class TestCountingSort {

    /**
     * @param target is the unsorted number array
     */
    public static void countingSort(int[] target) {
        // find max and min
        int max = 0;
        int min = 0;
        for (int num : target) {
            if ( num > max ) {
                max = num;
            }
            if ( num < min ) {
                min = num;
            }
        }
        // add one into counter which index is the sorting number
        int zeroOffset = 0 - min;
        int counterSize = max + zeroOffset + 1;
        int[] counter = new int[counterSize];
        for (int num : target) {
            counter[num + zeroOffset]++;
        }
        // put result from counter into target array
        for ( int targetIdx = 0; targetIdx < target.length; targetIdx++ ) {
            for ( int counterIdx = min + zeroOffset; counterIdx < counterSize; counterIdx++ ) {
                if ( counter[counterIdx] > 0 ) {
                    target[targetIdx] = counterIdx - zeroOffset;
                    counter[counterIdx]--;
                    break;
                }
            }            
        }
        // show result
        for (int num : target) {
            System.out.print(num + ", ");
        }
    }

    public static void main(String[] args) {
        countingSort(new int[]{-1,84,13,0,0,73,26,32,19,91,38});
    }
    
}

星期日 一月 24, 2010

打包不同環境設定檔的 build.xml

description

上次做了可以部署不同環境的 build.xml 之後,
隨著產品的完整與使用的其他專案愈來愈多, 設定檔對應的環境也愈來愈多了.
不可能每次加個環境的設定檔就加個 target.
所以就改寫 build.xml 使能夠給定參數決定要複製哪一份設定檔.
另外原本的版本每次打包前都要跑測試, 不過這有點慢,
而且只有負責開發的人才需要測試, 給 user 自己部署的話是不用跑測試的,
所以就加了 target 使能夠直接打包不用跑 test.

usage

不知道會不會有人想用這份現成的 build.xml, 我自己是覺得蠻方便的. 還可用在 Hudson
要用這份 build.xml 的前提是專案資料夾結構並須符合以下格式
/ant/lib/jsp-api.jar
/ant/lib/junit-4.4.jar
/ant/lib/servlet-api.jar
/src
/test
/web
/config/yourconfigfolders1
/config/yourconfigfolders2
/config/yourconfigfolders3
...
/config/yourconfigfoldersN
只要確定專案資料夾結構符合規定就可以使用這份 build.xml
裡面的 target 不多, 主要會用到的大概如下.
clean : 清空 build 與 dist 資料夾
clean-dist-jarwar : clean 與 dist 後打包檔案到 dist 資料夾
clean-dist-test : clean 與 dist 後執行測試
clean-dist-test-jarwar : clean 與 dist 與 test 後打包檔案到 dist 資料夾
注意這是在使用 target 的時候要帶參數, 目的就是要指定想使用哪一個設定檔.
例如想打包 /config/yourconfigfolders1, 就輸入
ant clean-dist-jarwar -Dconfig=yourconfigfolders1
想打包 /config/yourconfigfolders2, 就輸入
ant clean-dist-jarwar -Dconfig=yourconfigfolders2
依此類推.

codes

build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="myproject" default="default" basedir=".">
    <description>Builds the project.</description>
    <property name="project.name" value="myproject" />
    <property name="dist" value="dist" />
    <property name="build" value="build" />
    <property name="build.version" value="1.0" />
    <property name="lib" value="web/WEB-INF/lib" />
    <property name="ant.lib" value="ant/lib" />
    <property name="build.reports.tests" value="build/reports/tests" />
    <property name="build.tests" value="build/tests" />
    <property name="build.tests.classes" value="build/tests/classes" />
    <property name="build.web" value="build/web" />
    <property name="build.web.classes" value="${build.web}/WEB-INF/classes" />
    <property name="build.web.lib" value="${build.web}/WEB-INF/lib" />
    <property name="src.java" value="src" />
    <property name="src.test" value="test" />
    <property name="src.config" value="config/${config}" />
    <property name="src.webapp" value="web" />
    <property name="dist.manifest" value="dist/MANIFEST.MF" />
    <property name="dist.mainclass" value="" />
    <path id="build.classpath">
        <fileset dir="${ant.lib}" includes="*.jar" />
        <fileset dir="${lib}" includes="*.jar" />
    </path>
    <target name="clean">
        <delete dir="${build}" />
        <delete dir="${dist}" />
    </target>
    <target depends="clean" name="clean-dist">
        <mkdir dir="${build.web.classes}" />
        <mkdir dir="${build.tests.classes}"/>
        <mkdir dir="${dist}" />
        <javac destdir="${build.web.classes}"
                srcdir="${src.java}"
                classpathref="build.classpath"
                source="1.5"
                target="1.5"
                encoding="UTF-8">
        </javac>
        <copy todir="${build.web.classes}">
            <fileset dir="${src.java}">
                <exclude name="**/*.java" />
                <include name="**/*"/>
            </fileset>
        </copy>
        <javac destdir="${build.tests.classes}"
                srcdir="${src.test}"
                classpathref="build.classpath"
                source="1.5"
                target="1.5"
                encoding="UTF-8">
            <classpath location="${build.web.classes}" />
        </javac>
        <copy todir="${build.tests.classes}">
            <fileset dir="${src.test}">
                <exclude name="**/*.java" />
                <include name="**/*"/>
            </fileset>
        </copy>
        <copy todir="${build.web.lib}">
            <fileset dir="${lib}" />
        </copy>
        <copy todir="${build.web}">
            <fileset dir="${src.webapp}" />
        </copy>
    </target>

    <target name="clean-dist-test" depends="clean-dist">
        <mkdir dir="${build.reports.tests}" />
        <copy todir="${build.web.classes}">
            <fileset dir="${src.config}" />
        </copy>
        <junit  haltonfailure="false" showoutput="true">
            <jvmarg value="-version:1.5.0" />
            <classpath>
                <pathelement path="${build.tests.classes}" />
                <pathelement path="${build.web.classes}"/>
                <path refid="build.classpath" />
            </classpath>
            <batchtest fork="yes" todir="${build.reports.tests}">
                <fileset dir="${src.test}">
                    <include name="**/*"/>
                </fileset>
                <formatter type="xml" />
            </batchtest>
        </junit>
    </target>


    <target name="init-dist-manifest">
        <copy todir="dist/lib">
            <fileset dir="${lib}" includes="*.jar" />
        </copy>
        <path id="jar.classpath">
            <fileset dir="dist/lib">
                <include name="*.jar" />
            </fileset>
        </path>
        <manifestclasspath property="jar.manifest.classpath" jarfile="dist/${project.name}.jar">
            <classpath refid="jar.classpath" />
        </manifestclasspath>
        <manifest file="${dist.manifest}">
            <attribute name="Class-Path" value="${jar.manifest.classpath}" />
            <attribute name="Main-Class" value="${dist.mainclass}"/>
            <attribute name="Implementation-Title" value="${project.name}"/>
            <attribute name="Implementation-Version" value="${build.version}"/>
            <attribute name="Implementation-Vendor" value="YourCompany Technology Corporation"/>
            <attribute name="Implementation-Build" value="myproject-${build.version}-M201001240200"/>
        </manifest>
    </target>

    <target depends="clean-dist-test,init-dist-manifest" name="clean-dist-test-jarwar">
        <jar destfile="dist/${project.name}.jar" manifest="${dist.manifest}" >
            <fileset  dir="${build.web.classes}"/>
        </jar>
        <copy todir="${build.web.classes}">
            <fileset dir="${src.config}" />
        </copy>
        <war destfile="${dist}/${project.name}.war">
            <fileset dir="${build.web}" />
        </war>
    </target>

    <target depends="clean-dist,init-dist-manifest" name="clean-dist-jarwar">
        <jar destfile="dist/${project.name}.jar" manifest="${dist.manifest}" >
            <fileset  dir="${build.web.classes}"/>
        </jar>
        <copy todir="${build.web.classes}">
            <fileset dir="${src.config}" />
        </copy>
        <war destfile="${dist}/${project.name}.war">
            <fileset dir="${build.web}" />
        </war>
    </target>

</project>

星期三 十一月 25, 2009

C pointer to a function

description

純粹拿來方便回憶

codes

#include <stdio.h>

void bubble( int numbers[], int length, int (*compare) (int, int) ); 
// 也可 void bubble( int numbers[], int length, int (*) (int, int) ); 
int ascending( int a, int b ) ;
int descending( int a, int b ); 
void swap( int *aPtr, int *bPtr ); 
void printAry( int ary[], int length ); 
void fillRandomNumbers( int numbers[], int length ); 
     
int main(void) {
    int numbers[10]; 
    fillRandomNumbers( numbers, 10 );
    printAry( numbers, 10 ); 
    printf("---1\n"); 
    
    bubble( numbers, 10, ascending ); 
    printAry( numbers, 10 ); 
    printf("---2\n");
    
    bubble( numbers, 10, descending ); 
    printAry( numbers, 10 ); 
    printf("---3\n");
        
    getche();
    return 0;    
} 

void printAry( int ary[], int length ) {
     int i = 0;
     for ( i = 0; i < length; i++ ) {
         printf( " %d ", ary[i] ); 
     } 
} 

void bubble( int numbers[], int length, int (*compare) (int, int) ) {
     int pass = 0;
     int count = 0;
     for ( pass = 0; pass < length; pass++ ) {
         for ( count = 0; count < length-1; count++ ) {
             if ( (*compare)( numbers[count], numbers[count+1]) ) {
                 swap( &numbers[count], &numbers[count+1] ); 
             } 
         } 
     } 
} 

void fillRandomNumbers( int numbers[], int length ) {
    int i = 0;
    for ( i = 0; i < length; i++ ) {
        numbers[i] = rand() % 10;  
    } 
} 

void swap( int *aPtr, int *bPtr ) {
    int tmp = *aPtr;
    *aPtr = *bPtr;
    *bPtr = tmp; 
} 

int ascending( int a, int b ) {
    return a > b;    
} 

int descending( int a, int b ) {
    return a < b;    
} 

星期三 十一月 18, 2009

codeline 的控制

專案通常會有三種環境: DEV / UAT / PROD.
為了讓每次的佈署與修改的 bug 都有好的版本管理方式, 我列一下一個需求進來該有怎樣的操作流程.
希望看官們能給點意見, 謝謝! :D
(這只是草稿, 希望以後可以適當的完整, 也不要太複雜好讓人容易記住).

    - create branch for development
        - create branch from trunk to /branches/DEV/
        - develop in branch
        - test in branch
        - fix bugs in branch
        - finish branch development
    
    - merge to trunk
        - create tag from trunk to /tags/DEV/
        - merge branch to trunk
        - test in trunk
        - fix bugs in trunk
        - finish trunk mergence
        
    - deploy to UAT
        - create tag from trunk to /tags/UAT/ before deploy to UAT
        - deploy to UAT
        - test in UAT
        - fix bugs in UAT
            - create branch from /tags/UAT/ to /branches/UAT
            - fix bugs in /branches/UAT/
            - test in /branches/UAT/
            - finish fix bugs in /branches/UAT/
            - create tag from trunk to /tags/UAT/
            - merge from branch to trunk
            - go to "deploy to UAT"
        - finish UAT deployment
        
    - deploy to PROD
        - create tag from trunk to /tags/PROD/ before deploy to PROD
        - deploy to PROD
        - test in PROD
        - fix bugs in PROD
            - create branch from /tags/PROD/ to /branches/PROD/
            - fix bugs in /branches/PROD/
            - test in /branches/PROD/
            - finish fix bugs in /branches/PROD
            - create tag from trunk to /tags/PROD/
            - merge from branch to trunk
            - go to "deploy to PROD"
        - finish PROD deployment

星期日 十一月 15, 2009

C + bubbleSort + linearSearch + binarySearch

description

最近在學 C, 書上講到 bubble sort 和 linear search 和 binary search.
自己練習寫, 練習完要往接下來的章節看就要刪掉練習程式了感覺有點可惜.
記錄一下...

codes

#include<stdio.h>
#define ARY_SIZE 20 
#define RANDOM_RANGE 20 

void bubbleSort(int ary[], int length); 
void fillRandomNumber(int ary[], int length); 
void printAry(int ary[], int length); 
int linearSearch( int search, int ary[], int length ); 
int binarySearchRecursion( int searchNumber, int sortedAry[], int lowIdx, int highIdx ); 
int binarySearchLoop( int searchNumber, int sortedAry[], int lowIdx, int highIdx ); 

int main(void) {
    int ary[ARY_SIZE];
    
    fillRandomNumber( ary, ARY_SIZE );
    printAry( ary, ARY_SIZE ); 
    bubbleSort( ary, ARY_SIZE ); 
    printAry( ary, ARY_SIZE ); 
    int searchNumber = 5; 
    int searchIndex = linearSearch(searchNumber, ary, ARY_SIZE); 
    printf( "  searched %d index = %d\n\n", searchNumber, searchIndex ); 
        
    
    fillRandomNumber( ary, ARY_SIZE );
    printAry( ary, ARY_SIZE ); 
    bubbleSort( ary, ARY_SIZE ); 
    printAry( ary, ARY_SIZE ); 
    searchNumber = 1;
    searchIndex = binarySearchRecursion(searchNumber, ary, 0, ARY_SIZE ); 
    printf( "  searched %d index = %d\n\n", searchNumber, searchIndex ); 
            
    
    fillRandomNumber( ary, ARY_SIZE );
    printAry( ary, ARY_SIZE ); 
    bubbleSort( ary, ARY_SIZE ); 
    printAry( ary, ARY_SIZE ); 
    searchNumber = 7;
    searchIndex = binarySearchLoop(searchNumber, ary, 0, ARY_SIZE ); 
    printf( "  searched %d index = %d\n\n", searchNumber, searchIndex ); 
            
    getche();     
    return 0;    
} 

int binarySearchLoop( int searchNumber, int sortedAry[], int lowIdx, int highIdx ) {
    while ( lowIdx <= highIdx ) {
        printf( "  ." ); 
        int centerIdx = ( lowIdx + highIdx ) / 2;
        if ( sortedAry[centerIdx] == searchNumber ) {
            return centerIdx; 
        } 
        if ( (highIdx - lowIdx <= 1 ) 
                     && searchNumber != sortedAry[highIdx]
                     && searchNumber != sortedAry[lowIdx] ) { 
            return -1;  
        } 
        if ( searchNumber > sortedAry[centerIdx] ) {
            lowIdx = centerIdx; 
        } else {
            highIdx = centerIdx; 
        } 
    }    
    return -1; 
} 

int binarySearchRecursion( int searchNumber, int sortedAry[], int lowIdx, int highIdx ) {
    printf("  ."); 
    int centerIdx = ( lowIdx + highIdx ) / 2;
    if ( sortedAry[centerIdx] == searchNumber ) {
        return centerIdx; 
    } 
    if ( sortedAry[centerIdx] != searchNumber 
                              && (centerIdx == lowIdx || centerIdx == highIdx) ) {
        return -1; 
    } 
    if ( sortedAry[centerIdx] > searchNumber ) {
        return binarySearchRecursion( searchNumber, sortedAry, lowIdx, centerIdx ); 
    } else if ( sortedAry[centerIdx] < searchNumber ) {
        return binarySearchRecursion( searchNumber, sortedAry, centerIdx, highIdx );  
    } 
    return -1; 
} 


int linearSearch( int search, int ary[], int length ) {
    int i = 0;
    for ( i = 0; i < length; i++ ) {
        printf( "  ." ); 
        if ( search == ary[i] ) {
              return i; 
        } 
    } 
    return -1; 
} 

void printAry(int ary[], int length) {
     int i = 0;
     for ( i = 0; i < length; i++ ) {
         printf( "%3d", i ); 
     } 
     printf( "\n" ); 
     for ( i = 0; i < length; i++ ) {
         printf( "%3d", ary[i] ); 
     }     
     printf( "\n" ); 
     printf( "\n" );      
} 

void fillRandomNumber(int ary[], int length) {
     int i = 0;
     for ( i = 0; i < length; i++ ) {
         ary[i] = rand() % RANDOM_RANGE; 
     }     
} 

void bubbleSort(int ary[], int length) {
     int roundCnt = 0;
     for ( roundCnt = 0; roundCnt < length; roundCnt++ ) {
         int compareIdx = 0;
         for ( compareIdx = 0; compareIdx < length; compareIdx++ ) {
             if ( ary[compareIdx] > ary[compareIdx+1] ) {
                  int temp = ary[compareIdx];
                  ary[compareIdx] = ary[compareIdx+1];
                  ary[compareIdx+1] = temp; 
             } 
         }               
     } 
} 

adapter

description

adapter pattern 有很多正式的講解方式,
不過為了快速回憶大概是什麼東西,
所以寫一點小小的程式來記.
不一定正確, 如果觀念不對請多質疑討論謝謝.

對我來說, adapter 就是拿來隱藏第三方程式,
例如第三方程式提供了數百個 method 或功能可用,
為了 user code 不依賴在那數百個功能上,
所以寫一個 adapter 只提供幾個願意開放的功能給 user code.

另外 adapter 也常拿來使用沒 source code 的 library,
目的是以後要換比較方便, 只要換 adapter 的實作就好了.

codes

UnknownSourcePrinter.java
public class UnknownSourcePrinter {

    public void print(String text) {
        System.out.println("print with printer without source code, print text = " + text);
    }

}
MyPrintService.java
public class MyPrintService {

    private UnknownSourcePrinter printer = new UnknownSourcePrinter();

    public void print(String text) {
        printer.print( text );
    }

}
UserCode.java
public class UserCode {

    public static void main(String[] args) {
        MyPrintService service = new MyPrintService();
        service.print( "my text" );
    }
    
}

星期五 十一月 06, 2009

[分享] 節錄版本控制與平行開發文章重點的投影片

今天在公司分享介紹一篇文章

Streamed Lines: Branching Patterns for Parallel Software Development

有做投影片節錄重點.
http://docs.google.com/present/view?id=dcsswcvv_5f65vrwfp

不過這篇文章好像蠻久的了, 而且也只是大概看一下, 如果有誤解或是該抓的重點沒抓到請提出來討論喔.

星期二 十月 06, 2009

簡單的推文到 twitter, plurk, facebook

因為客戶想要, 所以看一下客戶口中的"推文到 twitter, plurk, facebook"是怎麼回事.
根據客戶的描述文字, 目前猜測可能是以下的簡單功能.
facebook 的部份跟 twitter/plurk 不太一樣的地方是不知道怎麼顯示字串訊息
而且給的是網頁的位址, facebook 會自動去找那個網頁有什麼可以顯示的..

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
        <script type="text/javascript">
            function sendToPlurk() {
                var msg = document.getElementById('msg').value;
                window.open('http://www.plurk.com/?qualifier=shares&status=' + msg );
            }
            function sendToTwitter() {
                var msg = document.getElementById('msg').value;
                window.open('http://twitter.com/home/?status=' + msg );
            }
            function sendToFacebook() {
                var url = document.getElementById('url').value;
                window.open('http://www.facebook.com/sharer.php?u=' + encodeURIComponent(url) );
            }
       </script>
    </head>
    <body>
        <input type="text" size="100" id="msg" value="測試" /><br />
        <a href="javascript:sendToPlurk()">
            發訊息上 plurk
        </a><br />
        <a href="javascript:sendToTwitter()">
            發訊息上 twitter
        </a><br /><br /><br />
        URL : <input type="text" size="100" id="url" value="http://www.youtube.com/watch?v=x6QA3m58DQw" /><br />
        <a href="javascript:sendToFacebook()">
            發訊息上 facebook
        </a>
    </body>
</html>

星期六 九月 19, 2009

指定 package iterate 包含 jar 檔內所有 sub package 的 class

以前就好奇現在很多工具只要 annotation 就可以紀錄你有哪些 class 是註冊要做某事的行為怎麼實做.
也就是好奇要怎麼 iterate 所有的 class 進行操作呢?
今天看到 stripes 的 ResolverUtil.java 就有這種行為.
感覺像是這樣.

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class TestIterateClasses {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        TestIterateClasses test = new TestIterateClasses();
        // 這裡指定的 package 不管是 source code 或 library 下的都可以把所有 class 顯示出來
        test.test("org.apache.commons.pool.impl");
    }
    
    private void test(String javaPackageName) throws IOException {
        String packageName = javaPackageName.replaceAll("\\.", "/");
        // 這裡可以找到有指定 package 的 library 有哪些
        Enumeration urls = Thread.currentThread().getContextClassLoader().getResources(packageName);
        while (urls.hasMoreElements()) {
            String urlPath = urls.nextElement().getFile();
            if ( urlPath.startsWith("file:") ) {
                urlPath = urlPath.substring(5);
            }
            if ( urlPath.indexOf("!") > 0 ) {
                urlPath = urlPath.substring(0, urlPath.indexOf("!"));
            }
            File file = new File(urlPath);
            if ( file.isDirectory() ) {
                iterateDirectory( packageName, file );
            } else {
                showClassInJarFile( file, packageName );
            }

        }        
    }

    private void showClassInJarFile(File file, String packageName) throws IOException {
        JarInputStream jarIs = new JarInputStream(new FileInputStream(file));
        JarEntry entry = null;
        while ( (entry = jarIs.getNextJarEntry()) != null ) {
            // 還不確定這有沒有更快的方式, 不然其實是 iterate 所有的 package
            if ( entry.getName().endsWith(".class") 
                    && entry.getName().indexOf(packageName) > -1 ) {
                System.out.println(entry.getName());
            }
        }
    }

    private void iterateDirectory(String packageName, File directory) throws IOException {
        File[] files = directory.listFiles();
        for (File file : files) {
            System.out.println("iterate directory:" + packageName + "/" + file.getName());
            if ( file.isDirectory() ) {
                iterateDirectory( packageName, file );
            } else {
                showClassInFileDirectory( file );
            }
        }
    }

    private void showClassInFileDirectory(File file) {
        if ( file.getName().endsWith(".class") ) {
            System.out.println(file);
        }
    }
}

星期日 九月 13, 2009

做 immutable class 時讓 List 真的不能被改變

上次看到一篇文章寫得很不錯, 但是忘記網址了,
反正就是在說實做 immutable 要注意的地方.
今天剛好需要 immutable class, 有個 List 的變數, 我想讓它 immutable
這時候就是用 Collections#unmodifiableList,
要注意雖然 unmodifiableList 會產生一個不能 modify 的 List.
但傳進去的 List instance 卻還是可以被編輯, 所以仍然可以透過操作傳進去的物件改變內容, 這樣就不是 immutable 了.
所以要做 immutable 的 List 除了呼叫 unmodifiableList 以外還要用 ArrayList 包起來


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TestCollections {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<String>();
        stringList.add("A");
        stringList.add("B");
        stringList.add("C");

        List<String> notReallyUnmodifiedList = Collections.unmodifiableList( stringList );
        stringList.add("D");
        
        try {
            notReallyUnmodifiedList.add("Trigger exception");
        } catch (Exception ex) {
            System.out.println("Yes! You can't modify notReallyUnmodifiedList directly");
        }

        // will print ABCD
        System.out.print("notReallyUnmodifiedList=");
        for (String str : notReallyUnmodifiedList) {
            System.out.print(str);
        }
        System.out.println();

        List<String> reallyUnmodifiedList = Collections.unmodifiableList( new ArrayList<String>(stringList) );
        stringList.add("E");

        try {
            reallyUnmodifiedList.add("Trigger exception");
        } catch (Exception ex) {
            System.out.println("Yes! You can't modify reallyUnmodifiedList directly");
        }

        // will print ABCD, not ABCDE
        System.out.print("reallyUnmodifiedList=");
        for (String str : reallyUnmodifiedList) {
            System.out.print(str);
        }
        System.out.println();
    }
}

星期日 七月 26, 2009

decorator 適合自己設計的 class

如果用 adapter, 就比較適合用來處理 3rd party 的 library method.
只要我呼叫的是不會變動的 interface, 即使以後換 library 也不會有太大問題.
不過如果是 decorator, 因為要 override 所有的 method 然後用 delegate 到 class member 中.
那我就無法掌握我 decorate 的 method 以後不會變動.
比方說我要 decorate log4j 的 Logger 使能夠不用

private static final Logger logger = Logger.getLogger(ABC.class);
public void methodA() {
    if ( logger.isInfoEnabled() ) {
        logger.info("This is method a");
    }
}
而只要
private static final Logger logger = new LoggerDecorator(Logger.getLogger(ABC.class));
public void methodA() {
    logger.info("This is method a");
} 
就可以有 isInfoEnabled 的效果.
那我需要在 LoggerDecorator 裡 override 所有 Logger 的 method.
...
那我需要 override 的是 Logger 還是 Logger 的 super class: Category 呢?
我覺得應該是 Logger.
可是我實在無法確定下一版的 Logger 會不會有新的 method.
以後換新的版本, 儘管可能不會 compile error.
但很可能 LoggerDecorator 並沒有 override 到新的 Logger 的 method.
導致 user code 呼叫的時候, LoggerDecorator 不會把行為 delegate 到新版的 Logger 導致錯誤發生.
...
所以現在有種感覺就是: decorator 適合用在自己設計的 classes 上.
自己設計的 classes 所以知道有哪些行為要被 decorate, 並且設計成好的 decorate 方式.
...
這樣想對嗎?
目前我是這麼想的...
所以如果要用到前面 LoggerDecorator 的例子, 可能我需要的反而是 adapter.
就是準備一個自己系統的 LoggerAdapter, 然後和 3rd party 的 Logger 橋接了.

星期五 七月 24, 2009

Study effective java 2nd note - 如果建構子的參數很多怎麼辦

如果有一個建構子的參數超過四個, 而且很多是 optional 的.
可用 Builder 建立參數物件.
參考作法是一個 immutable 的參數物件加上一個 mutable 的 inner builder class.
透過對 inner class 的呼叫設定想要的參數值後呼叫 build 建立一個 immutable 的參數物件.


package test;

import org.apache.commons.lang.builder.ToStringBuilder;
import test.PersonalInfo.PersonalInfoBuilder;


public class TestBuilder {

    public static void main(String[] args) {
        PersonalInfo info = new PersonalInfoBuilder()
                                            .address("myaddress")
                                            .age(5)
                                            .cellphone("1234567890")
                                            .firstName("my first name")
                                            .lastName("my last name")
                                            .phone("myphone")
                                            .build();
        System.out.println(info);
    }
    
}



class PersonalInfo {

    private final String firstName;
    private final String lastName;
    private final int age;
    private final String address;
    private final String phone;
    private final String cellphone;

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

    public String getPhone() {
        return phone;
    }

    public String getCellphone() {
        return cellphone;
    }

    public static class PersonalInfoBuilder implements Builder<PersonalInfo> {
        private String firstName = "";
        private String lastName = "";
        private int age = 0;
        private String address = "";
        private String phone = "";
        private String cellphone = "";

        public PersonalInfoBuilder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public PersonalInfoBuilder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public PersonalInfoBuilder age(int age) {
            this.age = age;
            return this;
        }

        public PersonalInfoBuilder address(String address) {
            this.address = address;
            return this;
        }

        public PersonalInfoBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public PersonalInfoBuilder cellphone(String cellphone) {
            this.cellphone = cellphone;
            return this;
        }

        @Override
        public PersonalInfo build() {
            return new PersonalInfo( this );
        }
    }

    private PersonalInfo(PersonalInfoBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.address = builder.address;
        this.phone = builder.phone;
        this.cellphone = builder.cellphone;
    }

}
interface Builder<T> {
    T build();
}

星期日 七月 12, 2009

double-checked locking 在JDK5後搭配 volatile 可實做 singleton

最近才知道原來 double-checked locking 其實是不能實作 singleton 的.
一開始看不太懂為什麼, 後來上網查之後才知道原因.
不過以前看書的時候有說到 double-checked locking 在 JDK5 之後能用.
那時候還不知道為什麼, 現在才知道...原來如此...

The "Double-Checked Locking is Broken" Declaration

星期五 七月 10, 2009

factory

description

有種 factory pattern 的實作是一個 product interface,
一個 factory interface, 然後兩個實作的 class.
今天看 Thinking in Java 4/e 的時候, 看到可以把 factory 用成 inner class.
除此之外, inner class 可以把實做完全隱藏, 只暴露 interface 就好,

reference

Thinking in Java 4/e

codes

package test;

public class TestFactory {

    public static void main(String[] args) {
        new ProductUser(AProduct.FACTORY);
        new ProductUser(BProduct.FACTORY);
    }
}

class ProductUser {

    private ProductFactory productFactory = null;

    public ProductUser(ProductFactory productFactory) {
        this.productFactory = productFactory;
    }

    public void doUse() {
        productFactory.create().use();
    }
}

class BProduct implements Product {

    public static final ProductFactory FACTORY = new ProductFactory() {
        public Product create() {
            return new BProduct();
        }
    };

    private BProduct() {
    }

    public void use() {
        System.out.println("use BProduct");
    }

}

class AProduct implements Product {

    public static final ProductFactory FACTORY = new ProductFactory() {
        public Product create() {
            return new AProduct();
        }
    };

    private AProduct() {
    }

    public void use() {
        System.out.println("use AProduct");
    }

}

interface ProductFactory {

    public Product create();
}

interface Product {

    public void use();
}

星期二 七月 07, 2009

YUI 做 Yahoo suggest.

description

遇到一個需求, 就是要像 yahoo 搜尋那樣有橘色的字.

reference

Yahoo! UI Library: AutoComplete

codes

TestAutoCompleteServlet.java: 網頁會呼叫這個 servlet, 每次都回傳一樣的東西
public class TestAutoCompleteServlet extends HttpServlet {
   
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("誰誰誰");
            out.println("神神神");
            out.println("一一一");
            out.println("二二二");
            out.println("aaa");
            out.println("bbb");
            out.println("ccc");
            out.println("ddd");
            out.println("eee");
            out.println("fff");
            out.println("ggg");
            out.println("hhh");
        } finally { 
            out.close();
        }
    } 

    // doGet, doPost call processRequest
}
TestAutoComplete.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/fonts/fonts-min.css" />
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/autocomplete/assets/skins/sam/autocomplete.css" />
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>

<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/connection/connection-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/animation/animation-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/datasource/datasource-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/autocomplete/autocomplete-min.js"></script>


<style type="text/css">
    #myAutoComplete {
        width:25em; /* set width here or else widget will expand to fit its container */
        padding-bottom:2em;
    }
</style>

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body class="yui-skin-sam">
        <div id="myAutoComplete">
            <input id="myInput" type="text">
            <div id="myContainer"></div>
        </div>


        <script type="text/javascript">
            var test = function() {
                // Use an XHRDataSource
                var oDS = new YAHOO.util.XHRDataSource("${pageContext.request.contextPath}/TestAutoCompleteServlet");
                // Set the responseType
                oDS.responseType = YAHOO.util.XHRDataSource.TYPE_TEXT;
                // Define the schema of the delimited results
                oDS.responseSchema = {
                    recordDelim: "\n",
                    fieldDelim: "\t"
                };
                // Enable caching
                oDS.maxCacheEntries = 5;

                // Instantiate the AutoComplete
                var oAC = new YAHOO.widget.AutoComplete("myInput", "myContainer", oDS);
                oAC.formatResult = function(oResultData, sQuery, sResultMatch) {
                    var resultDataString = new String(oResultData);
                    var queryString = new String(sQuery);
                    var minusString = resultDataString.substr(queryString.length, resultDataString.length);
                    var hasMatch = resultDataString.indexOf(queryString) > -1;
                    if ( hasMatch ) {
                        return '<font color=orange>' + queryString + '</font>' + minusString;
                    } else {
                        return resultDataString;
                    }
                };
                return {
                    oDS: oDS,
                    oAC: oAC
                };
            }();
            
        </script>



    </body>
</html>