<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    1.安裝
    首先,我是使用mysql進行測試的,你的機器上需要安裝mysql數(shù)據(jù)庫。
    然后執(zhí)行:
    gem install mysql

    到rubyforge下載ruby-DBI,解壓后cd到目錄運行如下命令:
    ruby setup.rb config --with=dbi,dbd_mysql
    ruby setup.rb setup
    ruby setup.rb install

    完整的setup命令參數(shù)參考DBI的doc

    2.完整例子
    DBI是一類似于ODBC的開發(fā)式的統(tǒng)一的數(shù)據(jù)庫編程接口,結(jié)構(gòu)層次上可以分為兩層:
    1.Database Interface——數(shù)據(jù)庫接口層,與數(shù)據(jù)庫無關,提供與數(shù)據(jù)庫無關的標準接口
    2.Database Driver——數(shù)據(jù)庫驅(qū)動,與數(shù)據(jù)庫相關

    DBI也是很簡單易用的,一個完整的使用例子,對于初學者可能有點幫助:
    require 'dbi'
    begin
      
    #連接數(shù)據(jù)庫
      dbh=DBI.connect("DBI:Mysql:dbi_test:localhost","root","")
      
      dbh.columns(
    "simple").each do |h|
        p h
      end
      
    #示范3種事務處理方式
      #手動commit
      dbh["AutoCommit"]=false
      
    1.upto(10) do |i|
        sql 
    = "insert into simple (name, author) VALUES (?, ?)"
        dbh.do(sql, 
    "Song #{i}""#{i}")
      end
      dbh.commit
      
      
    #使用transaction方法
      dbh.transaction do |dbh|
        
    1.upto(10) do |i|
          sql 
    = "insert into simple (name, author) VALUES (?, ?)"
          dbh.do(sql, 
    "Song #{i}""#{i}")
        end
      end
      
      
    #使用SQL語句
      dbh.do("SET AUTOCOMMIT=0")
      dbh.do(
    "BEGIN")
      dbh[
    "AutoCommit"]=false
      dbh.do(
    "UPDATE simple set name='test' where id='1'")
      dbh.do(
    "COMMIT")
      
      
    #查詢
      sth=dbh.execute("select count(id) from simple")
      puts 
    "bookCount:#{sth.fetch[0]}"
      sth.finish
      begin
        sth
    =dbh.prepare("select * from simple")
        sth.execute
        
    while row=sth.fetch do
          p row
        end
        sth.finish
      rescue
      end
      
      
    #上面這段查詢可以改寫為:
      #dbh.select_all("select * from simple") do |row|
      #   p row
      #end   
      
      
      
    #使用工具類輸出xml格式結(jié)果集以及測量查詢時間
      sql="select * from simple"
      mesuretime
    =DBI::Utils::measure do
        sth
    =dbh.execute(sql)
      end 
      puts 
    "SQL:#{sql}"
      puts 
    "Time:#{mesuretime}"
      rows
    =sth.fetch_all
      col_names
    =sth.column_names
      sth.finish
      puts DBI::Utils::XMLFormatter.table(rows)
      
      dbh.do(
    "delete from simple")
    rescue  DBI::DatabaseError
    =>e
      puts 
    "error code:#{e.err}"
      puts 
    "Error message:#{e.errstr}"
    ensure
      dbh.disconnect 
    if dbh
    end   


    posted @ 2007-04-10 20:21 dennis 閱讀(4654) | 評論 (1)編輯 收藏

        這個包的類主要用于spring框架的異常處理和一些核心的助手類(與框架具體部分無關的)。
        這個包中主要應用到了簡單工廠模式,用于判斷jdk版本,根據(jù)jdk版本不同提供不同的集合類、當前方法棧信息等。我們來看看是如何判斷當前用戶的jdk版本的:

    package org.springframework.core;

    public class JdkVersion {
        
        
    public static final int JAVA_13 = 0;
        
        
    public static final int JAVA_14 = 1;
        
        
    public static final int JAVA_15 = 2;

        
    private static String javaVersion;

        
    private static int majorJavaVersion = JAVA_13;
        
        
    static {
            javaVersion 
    = System.getProperty("java.version");
            
    // should look like "1.4.1_02"
            if (javaVersion.indexOf("1.4."!= -1) {
                majorJavaVersion 
    = JAVA_14;
            }
            
    else if (javaVersion.indexOf("1.5."!= -1) {
                majorJavaVersion 
    = JAVA_15;
            }
            
    // else leave as 1.3 default
        }

        
    /**
         * Return the full Java version string, as returned by
         * <code>System.getProperty("java.version")</code>.
         
    */
        
    public static String getJavaVersion() {
            
    return javaVersion;
        }

        
    /**
         * Get the major version code. This means we can do things like
         * <code>if (getMajorJavaVersion() < JAVA_14)</code>.
         * 
    @return a code comparable to the JAVA_XX codes in this class
         * 
    @see #JAVA_13
         * 
    @see #JAVA_14
         * 
    @see #JAVA_15
         
    */
        
    public static int getMajorJavaVersion() {
            
    return majorJavaVersion;
        }

    }

    直接獲取系統(tǒng)的java.version屬性來進行jdk版本的判斷。而CollectionFactory依據(jù)這個類來創(chuàng)建不同的集合類型,如果是jdk1.4就優(yōu)先使用jdk1.4的集合框架,再次選擇Commons Collections,最后才不得已就使用jdk1.3的集合框架,這里比較有趣的是判斷Commons Collections的方法就是嘗試Class.forName一個Commons集合框架中的對象,如果成功,當然證明classpath有commons-collections.jar包:
    static {
            
    // Check whether JDK 1.4+ collections and/or
            
    // Commons Collections 3.x are available.
            if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_14) {
                logger.info(
    "JDK 1.4+ collections available");
            }
            
    try {
                Class.forName(COMMONS_COLLECTIONS_CLASS_NAME);
                commonsCollections3xAvailable 
    = true;
                logger.info(
    "Commons Collections 3.x available");
            }
            
    catch (ClassNotFoundException ex) {
                commonsCollections3xAvailable 
    = false;
            }
        }

    然后就是一系列的getXXXIfPossible()方法用以獲取最優(yōu)版本的集合類型,比如getLinkedHashMapIfPossible():
    public static Map createLinkedMapIfPossible(int initialCapacity) {
            
    if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_14) {
                logger.debug(
    "Creating [java.util.LinkedHashMap]");
                
    return Jdk14CollectionFactory.createLinkedHashMap(initialCapacity);
            }
            
    else if (commonsCollections3xAvailable) {
                logger.debug(
    "Creating [org.apache.commons.collections.map.LinkedMap]");
                
    return CommonsCollectionFactory.createLinkedMap(initialCapacity);
            }
            
    else {
                logger.debug(
    "Falling back to [java.util.HashMap] for linked map");
                
    return new HashMap(initialCapacity);
            }
        }
    其中的Jdk14CollectionFactory 和CommonsCollectionFactory 也都是工廠類。可以看到,一個優(yōu)秀的通用框架對于版本的兼容性非常重視。

        這個包中另外一個需要注意的就是用于spring AOP功能實現(xiàn)的輔助類——ControlFlow。ControlFlow按照rod johnson的說法就是用于獲取當前調(diào)用的方法棧的具體信息。ControlFlow是一個接口,擁有3個方法用于判斷當前方法棧的位置:
    public interface ControlFlow {

        
    /**
          查找當前方法調(diào)用是否則在某類中
         * 
    @param clazz the clazz to look for
         
    */
        
    boolean under(Class clazz);

        
    /**
         * 查找當前方法調(diào)用是否則在某類的某個方法中
         * according to the current stack trace.
         * 
    @param clazz the clazz to look for
         * 
    @param methodName the name of the method to look for
         
    */
        
    boolean under(Class clazz, String methodName);

        
    /**
         * 當前棧幀是否包含傳入的記號
         * 
    @param token the token to look for
         
    */
        
    boolean underToken(String token);

    }

    然后根據(jù)jdk版本的不同采用不同的方式實現(xiàn)這個接口:Jdk14ControlFlow和Jdk13ControlFlow。這是典型的策略模式的應用。需要注意的是,這兩個具體類的是放在工廠類ControlFlowFactory中作為內(nèi)部類實現(xiàn)的:
    public abstract class ControlFlowFactory {
       
       
    static class Jdk13ControlFlow implements ControlFlow {
      
       

        
    static class Jdk14ControlFlow implements ControlFlow {
       
    }

    在這里,我們可以學到的東西就如何去判斷當前方法棧的信息?jdk1.4之前只能通過對StackTrace的字符串進行分析,而jdk1.4引入了java.lang.StackTraceElement用于獲取當前方法調(diào)用所處的棧幀的信息,看看spring的使用方法,相當簡單:
    static class Jdk14ControlFlow implements ControlFlow {

            
    private StackTraceElement[] stack;

            
    public Jdk14ControlFlow() {
                
    this.stack = new Throwable().getStackTrace();
            }

            
    /**
             * Searches for class name match in a StackTraceElement.
             
    */
            
    public boolean under(Class clazz) {
                Assert.notNull(clazz, 
    "Class must not be null");
                String className 
    = clazz.getName();
                
    for (int i = 0; i < stack.length; i++) {
                    
    if (this.stack[i].getClassName().equals(className)) {
                        
    return true;
                    }
                }
                
    return false;
            }

            
    /**
             * Searches for class name match plus method name match
             * in a StackTraceElement.
             
    */
            
    public boolean under(Class clazz, String methodName) {
                Assert.notNull(clazz, 
    "Class must not be null");
                Assert.notNull(methodName, 
    "Method name must not be null");
                String className 
    = clazz.getName();
                
    for (int i = 0; i < this.stack.length; i++) {
                    
    if (this.stack[i].getClassName().equals(className) &&
                            
    this.stack[i].getMethodName().equals(methodName)) {
                        
    return true;
                    }
                }
                
    return false;
            }

            
    /**
             * Leave it up to the caller to decide what matches.
             * Caller must understand stack trace format, so there's less abstraction.
             
    */
            
    public boolean underToken(String token) {
                
    if (token == null) {
                    
    return false;
                }
                StringWriter sw 
    = new StringWriter();
                
    new Throwable().printStackTrace(new PrintWriter(sw));
                String stackTrace 
    = sw.toString();
                
    return stackTrace.indexOf(token) != -1;
            }
    }

    獲取當前棧幀的信息,對于一般的java開發(fā)者沒有什么意義,對于AOP的實現(xiàn)和框架開發(fā)者可能有比較重要的作用,我還未研讀spring的aop部分,不敢妄言,留待以后解答,如果您已經(jīng)研讀過這部分代碼,不吝賜教。

    這個包另外的一個特點就是將java的反射API演示了一遍,特別是Constant.java(用于提取某個類public static final定義的常量)和ReflectiveVisitorHelper (反射助手類),對于學習java反射技術也有不小的幫助。

    posted @ 2007-04-10 16:56 dennis 閱讀(4554) | 評論 (2)編輯 收藏

        網(wǎng)上其實有一大堆這樣的資料了,我再寫也沒多大價值,談下幾個注意點吧。
    1.在windows上安裝Rmagic,如果你是通過gem安裝的,
    require 'Rmagic'
    要修改為:
    require 'rubygems'
    require 
    'Rmagick'
    才能正確引入。

    2.網(wǎng)上那個例子,畫布是使用Rmagic內(nèi)置的圖像格式,Rmagic內(nèi)置的圖像格式還有:

    gradient*

    梯度,比如gradient:red-blue

    granite

    花崗石,比如: "granite:".

    logo

    logo型的圖像. 如: "logo:"后面會多顯示一個五角星^_^

    netscape

    非常漂亮的彩條。如: "netscape:"

    null*

    空白 使用方式: "null:"

    rose


    玫瑰 使用方式 : "rose:"

    xc*

    設置一個背景色,比如"xc:green"


    一個修改的例子,在rails的models下存為noisy_image.rb,在Controller就可以這樣調(diào)用NoisyImage.new(6) :


    require 'rubygems'
    require 
    'Rmagick'
    class NoisyImage
      include Magick
      attr_reader :code, :code_image
      Jiggle 
    = 15
      Wobble 
    = 15
      
      
    def initialize(len)
        chars 
    = ('a'..'z').to_a - ['a','e','i','o','u']
        code_array
    =[]
        
    1.upto(len) {code_array << chars[rand(chars.length)]}
        granite 
    = Magick::ImageList.new('xc:#EDF7E7')
        canvas 
    = Magick::ImageList.new
        canvas.new_image(
    32*len, 50, Magick::TextureFill.new(granite))
        text 
    = Magick::Draw.new
        text.font_family 
    = 'times'
        text.pointsize 
    = 40
        cur 
    = 10
        
        code_array.each{
    |c|
          rand(
    10> 5 ? rot=rand(Wobble):rot= -rand(Wobble)
          rand(
    10> 5 ? weight = NormalWeight : weight = BoldWeight
          text.annotate(canvas,0,0,cur,
    30+rand(Jiggle),c){
            self.rotation
    =rot
            self.font_weight 
    = weight
            self.fill 
    = 'green'
          }
          cur 
    += 30
        }
        @code 
    = code_array.to_s
        @code_image 
    = canvas.to_blob{
          self.format
    ="JPG" 
        }
      end
      
    end

    3.與rails應用的結(jié)合,和一般的驗證碼原理一樣,將產(chǎn)生的隨機數(shù)存儲在session或者request范圍內(nèi),提交的時候進行比較驗證即可。比如產(chǎn)生圖片的時候?qū)㈦S機字母存儲在session[:code]中:

     session[:noisy_image] = NoisyImage.new(6)

     session[:code] = session[:noisy_image].code

    驗證的時候,比較提交的type_code與session[:code]即可,為了安全性考慮,最好還是不考慮使用客戶端驗證。

     unless session[:code]==params[:type_code]
          flash[:notice]
    ='驗證碼填寫錯誤,請重新注冊,謝謝!'
          
    return redirect_to :action=>:new  
     end

    在頁面顯示圖片,類似servlet一樣直接調(diào)用Controller的action:

     def code_image
        image 
    = session[:noisy_image].code_image
        send_data image, :type 
    => 'image/jpeg', :disposition => 'inline'
      end

    <img height='30' src="/test/code_image">




    posted @ 2007-04-09 17:13 dennis 閱讀(3428) | 評論 (3)編輯 收藏

        開源工具已經(jīng)有NUnit,微軟這次給VS增加的單元測試功能不知道是不是直接借鑒NUnit?不過總也是好事,還集成了靜態(tài)分析和代碼覆蓋工具等,值的小小期待下。從這點上看,單元測試已經(jīng)非常深入人心咯。消息來自infoq中文站

    posted @ 2007-04-09 08:37 dennis 閱讀(1913) | 評論 (1)編輯 收藏

        《Programming Ruby中文版》前3部分我并不準備細看,畢竟我接觸ruby也有一段時間了,只準備快速地掠過一遍,查缺補漏;重點放在第3部分的核心內(nèi)容上,至于第四部分的參考手冊更多作為工具書了。僅在此記錄下一些值的注意的東西。

    1.全局變量$_,默認當gets方法返回輸入的行時,同時保存在全局變量$_,并且正則表達式如果作為條件語句(if或者while)時默認是跟這個全局變量進行匹配,而print參數(shù)為空時也是打印這個全局變量。這是早期ruby向perl語言學習的結(jié)果。可以看看這個例子:
    while gets
      
    if /Ruby/
          print
      end
    end

    這樣的風格不值的提倡,全局變量的使用應該盡力減少,ruby也在逐漸脫離perl主義的風格

    2.ruby中的單例模式:
    class Logger
      private_class_method:new
      @@logger
    =nil
      
    def Logger.create
        @@logger
    =new unless @@logger
        @@logger
      end
    end
    log1
    =Logger.create
    log2
    =Logger.create

    puts log1.object_id
    puts log2.object_id

    3.ruby中的block作用:
    1)迭代器,通常是內(nèi)部迭代器
    2)事務Blocks,c#的using語句倒是跟這個有點像,其實就是讓對象自身負責資源的打開和關閉,這是通過Kernel.block_given?實現(xiàn)的,比如File.open方法,當后面跟著一個block的時候,就會自動關閉打開的文件資源,如果不是,就需要自己處理。
    3)作為閉包,與javascript和其他語言中的閉包概念一致,一個例子:
    def n_times(thing)
      
    return lambda {|n| thing*n}
    end
    p1
    =n_times(23)
    puts p1.call(
    3)
    puts p1.call(
    2)
    通過lambda方法將一個block轉(zhuǎn)為Proc對象,盡管參數(shù)thing在block被真正調(diào)用時已經(jīng)離開了作用范圍,但是仍然可以使用

    4.ruby中數(shù)字的最大長度取決于系統(tǒng),這跟java,C#通過虛擬機規(guī)范的不同,數(shù)字類型的幾個常用迭代器:times,upto,downto,step,如:
    2.step(10,2){|i| print i,' '}  =>2,4,6,8,10

    5.ruby中的字符串是8字節(jié)的序列,可以存儲可打印的字符和二進制數(shù)據(jù)。比較有趣3種構(gòu)建字符串常量方式:%q(對應于單引號定義的字符串),%Q(雙引號)以及here documents,比如:
    s=<<END_OF_STRING
       測試測試啦
    END_OF_STRING

    6.Range,書中翻譯為區(qū)間,我倒更喜歡范圍這個詞。區(qū)間的3個用途:
    1)用作序列,最常見的,如1..2,a..z等,可以定義自己的區(qū)間,只要實現(xiàn)succ和<=>比較方法
    2)作為條件,書中的例子很經(jīng)典:
    while line=gets
       puts line 
    if line=~/start/..line=~/end/
    end

    #利用全局變量簡化為,不建議這樣寫
    while gets
       
    print if /start/../end/
    end

    3)作為間隔,看看某個值是否落入?yún)^(qū)間范圍內(nèi),使用===操作符比較

    7.正則表達式,這是重頭戲。ruby中的perl風格的正則表達式,其實也是內(nèi)建在ruby中的正則表達式對象的外部包裝,關鍵的就是兩個類Regexp類和MatchData類。一些peri程序員熟悉的記號:
    $&    匹配的字符串
    $`    匹配前的字符串
    $'    匹配后的字符串
    $1    第一個分組,$2,$3...類似
    詳細的就不抄書了,正則表達式我在學習javascript的時候已經(jīng)系統(tǒng)地學過,倒是不感覺吃力。

    8.在方法中定義可變長度參數(shù),只要參數(shù)前加*號即可,java1.5也已經(jīng)支持可變參數(shù),比如Object...obj。
    另外,在方法中,將數(shù)組展開為參數(shù),可以在數(shù)組前加一個*號,比如:
    def three(a,b,c)
       
    print "this is #{a},#{b},#{c}"
    end

    three([
    1,2,3)]
    #上面這樣調(diào)用報參數(shù)數(shù)目錯誤,正確的用法如下:
    three(*[1,2,3)] =>this is 1,2,3
    將hash列表直接做為參數(shù),可能在2.0支持,目前采用的要求散列數(shù)組在正常的參數(shù)之后,并位于任何的block或者數(shù)組之前

    9.ruby中的多線程:
    1)ruby創(chuàng)建線程,見下面這個例子,開3個線程分別訪問3個站點,并且對3個線程通過調(diào)用join方法,直到3個線程都結(jié)束,主線程才結(jié)束,來自書中例子:
    require 'net/http'
    pages
    =%w(www.javaeye.com www.sina.com.cn www.tkk7.com)
    $proxy_addr 
    = 'x.x.x.x'
    $proxy_port 
    = 80
    threads
    =[]
    for page_to_fetch in pages
      threads
    <<Thread.new(page_to_fetch) do |url|
        h
    =Net::HTTP.Proxy($proxy_addr, $proxy_port).new(url,80)
        puts 
    "Fetcing:#{url}"
        resp
    =h.get('/',nil)
        puts 
    "Got #{url}:#{resp.message}"
      end
    end    
    threads.each{
    |thr| thr.join}

    2)線程中如何共享變量?可以通過[]=簡單地把當前線程看成一個散列表,這里沒有考慮同步問題:
    count=0
    threads
    =[]
    10.times do |i|
      threads[i]
    =Thread.new do 
        sleep(rand(
    0.1))
        Thread.current[
    "mycount"]=count
        count
    +=1
      end
    end
    threads.each{
    |t| t.join;print t["mycount"],""}
    puts 
    "count =#{count}"

    3)通過設置abort_on_exception,如果是true,未處理的線程異常將殺死所有正在運行的線程,如果是false,則殺死當前運行的線程,其他線程繼續(xù)運行。修改上面的例子查看下:
    count=0
    threads
    =[]
    10.times do |i|
      threads[i]
    =Thread.new(i) do |j|
        
    raise "boom!" if j==4 
        sleep(rand(
    0.1))
        Thread.current[
    "mycount"]=count
        count
    +=1
      end
    end
    threads.each do 
    |t|
      begin
        t.join
        
    print t["mycount"],""
      rescue RuntimeError
    =>e
        puts 
    "Failed:#{e.message}"
      end
    end
    puts 
    "count =#{count}"
    輸出(隨機的):
    8, 1, 6, 3, Failed:boom!
    2, 4, 7, 0, 5, count =9

    在開頭加上:
    Thread.abort_on_exception=true
    殺死所有的運行進程,報出異常,而不會產(chǎn)生輸出。

    4)通過線程的一系列方法:pass,join,value,stop來進行線程的調(diào)度
    5)互斥的實現(xiàn),與其他語言一樣,不外乎加鎖、信號量、隊列的方式。看看加鎖是如何做的,通過monitor庫的關鍵字synchronize實現(xiàn),如下面這個例子,兩個線程遞增同一個變量,似乎結(jié)果應該是20000:
    #require 'monitor'
    class Counter#<Monitor
      attr_reader:count
      
    def initialize
        @count
    =0
      
    #  super
      end
      
    def tick
      
    #  synchronize do
          @count+=1
      
    #  end
      end
    end
    c
    =Counter.new
    t1
    =Thread.new{10000.times{c.tick}}
    t2
    =Thread.new{10000.times{c.tick}}

    t1.join;t2.join

    print c.count
    很遺憾,結(jié)果不會是20000,而是比它小的一個數(shù)值,這里的問題就是因為訪問共享資源沒有進行同步的緣故,使用monitor庫,請將上面代碼中的注釋去掉,可以得到正確的結(jié)果
    使用monitor,不一定要使用繼承,也可以使用mixin,甚至:
    lock=Monitor.new
    t1
    =Thread.new{10000.times{lock.synchronize{c.tick}}}
    還可以把特定的對象放入monitor,比如:
    c=Counter.new
    c.extend(MonitorMixin)
    t1
    =Thread.new{10000.times{c.synchronize{c.tick}}}
    .

    6)條件變量和隊列的方式不準備抄書了,ruby中對線程的操作都是直接調(diào)用操作系統(tǒng)的命令,特別是*nix支持的非常好,可惜我對linux也是個初哥。

    10.ruby中表達式很重要的一個特點是:任何表達式都有返回值,包括賦值語句、條件語句、循環(huán)語句之類。
    1)ruby中對布爾表達式的規(guī)定是:任何不是nil或者常量false的值都為真
    2)注意,在方法中調(diào)用訪問屬性的函數(shù),需要寫上調(diào)用者self,否則將處理為局部變量
    3)defined?方法用于返回參數(shù)的描述,如果未定義,返回nil
    4)邏輯表達式中,and和or的優(yōu)先級低于&&,||
    5)ruby沒有for語句,因為ruby通過內(nèi)建在對象中的迭代器提供了循環(huán)訪問的能力,最簡單的內(nèi)建迭代器:loop do ....end
    6)只要你的類支持each方法,你就可以使用for ... in ..語句循環(huán)它
    7)對循環(huán)可以使用break(打斷跳出),redo(從頭重新循環(huán),當前迭代),next進行調(diào)度。另外,還有retry,用于完全重新開始循環(huán)
    8)while,until和for循環(huán)內(nèi)建到了ruby語言中,但沒有引入新的作用域:前面存在的局部變量可以在循環(huán)中使用,而循環(huán)中新創(chuàng)建的局部變量也可以在循環(huán)后使用。而被迭代器使用的block則不同,在block中創(chuàng)建的局部變量無法在block外訪問。

    11.ruby的異常處理
    類似于java的try...catch...finnaly,ruby對應的是begin...rescue...ensure...end,將產(chǎn)生異常的代碼放在這個塊中進行處理。可以通過$!得到異常信息,或者提供局部變量名,我改寫了一下我的google在線翻譯機,增加異常處理,并用exit代替break:
    require 'net/http'
    def translate
      txt
    =STDIN.gets
      exit 
    if txt.strip=='e' or txt.strip=='exit'
      temp
    =txt.split(' ')
      
    if temp[1]=='1' or temp.size==1
        langpair
    ='en|zh-CN'
      
    else
        langpair
    ='zh-CN|en'
      end
      
    #使用代理  
      begin 
        $proxy_addr 
    = 'localhost'
        $proxy_port 
    = 80
        response 
    = Net::HTTP.Proxy($proxy_addr,$proxy_port).post_form(URI.parse("http://translate.google.com/translate_t"),{'text'=>temp[0],'langpair'=>langpair})
        response.body 
    =~ /<div id=result_box dir=ltr>(.*)<\/div>/
      rescue  StandardError 
    =>e
        $stderr.
    print "網(wǎng)絡錯誤:"+e
      
    else
        result 
    = $1 
        puts 
    '翻譯內(nèi)容:'+temp[0]
        puts 
    'google返回:'+result
        puts 
    '-------------------退出請打e或者exit---------------'
        translate
      end
    end
    translate
    引發(fā)一個異常使用raise語句,重新引發(fā)當前異常,如果沒有,就引發(fā)一個RuntimeError,常見使用方式:
    raise InterfaceException,"keyboard failure",caller
    其中的caller生成了棧的信息。另外,catch...throw語句用于在異常發(fā)生時從深度嵌套的結(jié)構(gòu)中跳轉(zhuǎn)出來。

    12。關于模塊,作用有二:作為命名空間和Mixin機制。模塊的Mixin機制可以說是ruby的一個精華所在,通過Mixin,可以變相地實現(xiàn)了多重繼承,并且可以動態(tài)地為類添加和刪除功能。這一部分注意兩點:
    1)模塊中定義的實例變量可能與包含模塊的類的實例變量產(chǎn)生名稱沖突。可以使用模塊一級的散列表,以當前對象的ID做索引,來保存特定于當前模塊的實例變量解決這個問題。比如:
    module Test
      State
    ={}
      
    def state=(value)
        State[object_id]
    =value
      end
      
    def state
        State[object_id]
      end
    end
    class Client
      include Test
    end
    c1
    =Client.new
    c2
    =Client.new
    c1.state
    ='A'
    c2.state
    ='B'

    puts c1.state
    puts c2.state
    2)是關于方法的查找路徑,順序是:當前類-》類的mixin模塊-》超類-》超類的mixin,另外mixin的模塊,最后混入的同名方法將覆蓋前面混入的。

    13.irb的配置和命令,今天發(fā)現(xiàn)irb原來也是可以玩出很多花樣的。記錄些有趣的:
    1)可以使用按tab鍵兩次來自動補全,要求加載irb/completaion庫。比如這樣啟動irb:
     
    irb -r irb/completion

    或者進入irb后手工require:
    require 'irb/completation'

    當然,還有更好的方法,呆會介紹
    2)子會話,在irb中使用irb可以創(chuàng)建子會話,通過命令jobs可以查看所有的子會話。創(chuàng)建子會話的時候指定一個對象,子會話的self將綁定該對象,比如:
    irb 'test'
    reverse
    =>"tset"
    length
    =>4
    self
    =>"test"
    irb_quit

    3)在linux下可以通過配置.irbrc配置文件來進行初始化定制,在windows環(huán)境你可以在ruby安裝目錄下的bin看到一個irb.bat文件,通過配置文件來定制irb,比如我們?yōu)閕rb增加ri和tab自動補齊功能:
    @echo off
    goto endofruby
    #!/bin/ruby
    #
    #
       irb.rb - intaractive ruby
    #
           $Release Version: 0.9.5 $
    #
           $Revision: 1.2.2.1 $
    #
           $Date: 2005/04/19 19:24:56 $
    #
           by Keiju ISHITSUKA(keiju@ruby-lang.org)
    #

    require 
    "irb"
    require 
    'irb/completion'
    def ri(*names)
      system(
    %{ri.bat #{names.map{ |name| name.to_s}.join(" ")}})
    end
    if __FILE__ == $0
      IRB.start(
    __FILE__)
    else
      
    # check -e option
      if /^-e$/ =~ $0
        IRB.start(
    __FILE__)
      
    else
        IRB.setup(
    __FILE__)
      end
    end
    __END__
    :endofruby
    "%~d0%~p0ruby" -"%~f0" %*

    4)常用命令:
    exit,quit,irb_exit,irb_quit——退出
    conf,context,irb_context——查看配置信息
    irb <obj>——創(chuàng)建子會話,如果提供obj,作為self
    jobs,irb_jobs——列出irb的子會話
    irb_fg,fg n——切換子會話
    kill n,irb_kill n——殺死一個irb子會話

    14.類的實例變量,類除了類變量、實例變量外,還有一個類的實例變量的概念:
    class Test
      #類的實例變量
      @cls_var 
    = 123
      def Test.inc
        @cls_var 
    += 1
      end
      
    class<<self
        attr_accessor:cls_var
      end
    end
    Test.inc
    Test.inc


    15.子類竟然可以改變父類定義方法的訪問級別:
    class Base
      
    def aMethod
        puts 
    "Got here"
      end
      private :aMethod
    end

    class Derived1 < Base
      public :aMethod
    end

    class Derived2 < Base
      
    def aMethod(*args)
        super
      end
      public:aMethod  
    end

    d1
    =Derived1.new
    d2
    =Derived2.new
    d1.aMethod
    d2.aMethod

    不知道ruby是基于什么樣的考慮允許這樣的行為。













    posted @ 2007-04-07 10:30 dennis 閱讀(2335) | 評論 (0)編輯 收藏

        這個包的說明是:Support for styling values as Strings, with ToStringCreator as central class.
    這個包簡單來說就是提供一個pretty-printing功能的輔助類,而ToStringCreator就是用于產(chǎn)生一個可以輸出經(jīng)過美化的value信息的toString()方法。使用方法參照spring的Test可以看到是這樣:   
            int[] integers = new int[] { 01234 };
            String str 
    = new ToStringCreator(integers).toString();
            assertEquals(
    "[@" + ObjectUtils.getIdentityHexString(integers) + " array<Integer>[0, 1, 2, 3, 4]]", str);
    或者寫個簡單例子感受下:
    int [] a={1,2,3,4,5,6,7,8,9};
    System.out.println(
    new ToStringCreator(a).toString());
        
    輸出:
    [@18558d2 array<Integer>[123456789]]

        如果你接觸過ruby,你應該很熟悉Object.inpsect這個功能,這里通過ToStringCreator包裝的toString()方法也是產(chǎn)生類似的能夠清晰顯示對象內(nèi)部結(jié)構(gòu)信息的方法。spring應該是使用這些輔助類來報告清晰的錯誤信息或者提示信息。
        看看這個包的UML類圖:

        首先,你需要理解ToStringStyler和ValueStyle兩個接口,ToStringStyler定義了描述一個輸入的Value信息的基本模板方法:
    public interface ToStringStyler {

        
    /**
         * Style a <code>toString()</code>'ed object before its fields are styled.
         * 
    @param buffer the buffer to print to
         * 
    @param obj the object to style
         
    */
        
    void styleStart(StringBuffer buffer, Object obj);

        
    /**
         * Style a <code>toString()</code>'ed object after it's fields are styled.
         * 
    @param buffer the buffer to print to
         * 
    @param obj the object to style
         
    */
        
    void styleEnd(StringBuffer buffer, Object obj);

        
    /**
         * Style a field value as a string.
         * 
    @param buffer the buffer to print to
         * 
    @param fieldName the he name of the field
         * 
    @param value the field value
         
    */
        
    void styleField(StringBuffer buffer, String fieldName, Object value);

        
    /**
         * Style the given value.
         * 
    @param buffer the buffer to print to
         * 
    @param value the field value
         
    */
        
    void styleValue(StringBuffer buffer, Object value);

        
    /**
         * Style the field separator.
         * 
    @param buffer buffer to print to
         
    */
        
    void styleFieldSeparator(StringBuffer buffer);

    }
        這是典型的Template Method模式,而兩個接口ToStringStyler、ValueStyler和它們的相應實現(xiàn)DefaultToStringStyler、DefaultValueStyler又是策略模式(Strategy)的應用體現(xiàn)。ValueStyler和DefaultValueStyler之間不僅僅是策略模式,同時也是visitor模式,請看DefaultValueStyler中一系列重載的visit方法,這些visit方法訪問不同類型Value的內(nèi)部結(jié)構(gòu)并構(gòu)造pretty格式的String返回,提供給ToStringStyler使用。
        ToStringCreator是ToStringStyler的客戶,它使用ToStringStyler調(diào)用產(chǎn)生優(yōu)美格式打印,而ToStringStyler 其實又是使用ValueStyler是訪問每個不同類型的子元素并返回優(yōu)美格式的String。實現(xiàn)的相當精巧和靈活:
       
    public ToStringCreator(Object obj, ToStringStyler styler) {
            Assert.notNull(obj, 
    "The object to be styled is required");
            
    this.object = obj;
            
    this.styler = (styler != null ? styler : DEFAULT_TO_STRING_STYLER);
    //開始        this.styler.styleStart(this.buffer, this.object);
        }

    public ToStringCreator append(String fieldName, byte value) {
            
    return append(fieldName, new Byte(value));
        }
       一系列不同類型的append方法
       
    public String toString() {
    //結(jié)束,并返回優(yōu)美格式的String        this.styler.styleEnd(this.buffer, this.object);
            return this.buffer.toString();
        }

    posted @ 2007-04-06 15:18 dennis 閱讀(1132) | 評論 (0)編輯 收藏

        其實我這系列小文,名為源碼分析,其實是自己讀《設計模式》的讀書筆記。Decorator模式在java的IO庫中得到應用,java的IO庫看起來復雜,其實理解了Decorator模式再回頭看可以很好理解并使用。
        Decorator模式,也就是裝飾器模式,是對象結(jié)構(gòu)型模式之一。

    1.意圖:動態(tài)地給一個對象添加一些額外的職責。給對象添加功能,我們首先想到的是繼承,但是如果每增一個功能都需要繼承,類的繼承體系將無可避免地變的龐大和難以理解。面向?qū)ο笤O計的原則:優(yōu)先使用組合,而非繼承,繼承的層次深度最好不過三。

    2.適用場景:
    1)在不影響其他對象的情況下,以動態(tài)、透明的方式給單個對象添加額外的責任
    2)處理可以撤銷的職責
    3)為了避免類的數(shù)目爆炸,或者不能采用生成子類的方法進行擴展時

    3.UML圖和協(xié)作:



    Component——定義一個對象接口,可以給這些對象動態(tài)地添加職責

    ConcreteComponent——定義一個對象,可以給這個對象添加職責

    Decorator——維持一個指向Component的引用,并定義一個與Component一致的接口,作為裝飾類的父類

    ConcreteDecorator——具體裝飾類

    4.效果:
    1)與靜態(tài)繼承相比,Decorator可以動態(tài)添加職責,更為靈活
    2)避免產(chǎn)生復雜的類,通過動態(tài)添加職責,而不是一次性提供一個萬能的接口
    3)缺點是將產(chǎn)生比較多的小對象,對學習上有難度,顯然,java.io就是這個問題

    我們以一個例子來實現(xiàn)Decorator模式,假設這樣一個場景:在某個應用中需要打印票據(jù),我們寫了一個PrintTicket接口,然后提供一個實現(xiàn)類(DefaultPrintTicket)實現(xiàn)打印的功能:

    package com.rubyeye.design_pattern.decorator;
    //抽象component接口
    public interface PrintTicket {
        
    public void print();
    }


    //默認實現(xiàn)類,打印票據(jù)
    package com.rubyeye.design_pattern.decorator;

    public class DefaultPrintTicket implements PrintTicket {

        
    public void print() {
            System.out.println(
    "ticket body");
        }

    }

    OK,我們的功能已經(jīng)實現(xiàn),我們還體現(xiàn)了針對接口編程的原則,替換一個新的打印方式很靈活,但是客戶開始提需求了——人生無法避免的三件事:交稅、死亡和需求變更。客戶要求打印頁眉,你首先想到的是繼承:
    package com.rubyeye.design_pattern.decorator;

    public class AnotherPrintTicket implements PrintTicket {

        
    public void print() {
            System.out.println(
    "ticket header");
            System.out.println(
    "ticket body");
        }

    }

    請注意,我們這里只是簡單的示例,在實際項目中也許意味著添加一大段代碼,并且需要修改打印票據(jù)本體的功能。需求接踵而至,客戶要求添加打印頁碼,要求增加打印花紋,要求可以聯(lián)打......你的類越來越龐大,直到你看見這個類都想吐的地步!-_-。讓我們看看另一個方案,使用Decorator模式來動態(tài)地給打印增加一些功能,首先是實現(xiàn)一個Decorator,它需要保持一個到PrintTicket接口的引用:

    package com.rubyeye.design_pattern.decorator;

    public class PrintTicketDecorator implements PrintTicket {

        
    protected PrintTicket printTicket;

        
    public PrintTicketDecorator(PrintTicket printTicket) {
            
    this.printTicket = printTicket;
        }

        
    //默認調(diào)用PrintTicket的print
        public void print() {
            printTicket.print();
        }

    }

    然后,我們實現(xiàn)兩個具體的裝飾類——打印頁眉和頁腳:
    package com.rubyeye.design_pattern.decorator;

    public class HeaderPrintTicket extends PrintTicketDecorator {

        
    public HeaderPrintTicket(PrintTicket printTicket){
            
    super(printTicket);
        }
        
        
    public void print() {
            System.out.println(
    "ticket header");
            
    super.print();
        }
    }

    package com.rubyeye.design_pattern.decorator;

    public class FooterPrintTicket extends PrintTicketDecorator {
        
    public FooterPrintTicket(PrintTicket printTicket) {
            
    super(printTicket);
        }

        
    public void print() {
            
    super.print();
            System.out.println(
    "ticket footer");
        }
    }

        使用起來也很容易:
       
    package com.rubyeye.design_pattern.decorator;

    public class DecoratorTest {

        
    /**
         * 
    @param args
         
    */
        
    public static void main(String[] args) {
            PrintTicket print
    =new HeaderPrintTicket(new FooterPrintTicket(new DefaultPrintTicket()));
            print.print();
        }

    }

    輸出:
    ticket header
    ticket body
    ticket footer

        了解了Decorator模式,我們聯(lián)系了下JUnit里面的應用。作為一個測試框架,應該方便地支持二次開發(fā),也許用戶開發(fā)自己的TestCase,添加自定義的功能,比如執(zhí)行重復測試、多線程測試等等。動態(tài)添加職責,而又不想使用靜態(tài)繼承,這正是Decorator使用的地方。在junit.extensions包中有一個TestDecorator,正是所有裝飾類的父類,也是作為二次開發(fā)的基礎,它實現(xiàn)了Test接口,而Test接口就是我們定義的抽象接口:
    public class TestDecorator implements  Test {
        
    //保有一個指向Test的引用
            protected Test fTest;
           
        
    public TestDecorator(Test test) {
            fTest
    = test;
        }
            
            
    public void basicRun(TestResult result) {
              fTest.run(result);
            }
            
    public void run(TestResult result) {
               basicRun(result);
            }
            
    }

    Junit已經(jīng)提供了兩個裝飾類:junit.extensions.ActiveTest用于處理多線程,junit.extensions.RepeatedTest用于執(zhí)行重復測試,看看RepeatedTest是怎么實現(xiàn)的:
    public class RepeatedTest extends  TestDecorator {
            
    //重復次數(shù)
        private int fTimesRepeat;

        
    public RepeatedTest(Test test, int repeat) {
            
    super(test);
            fTimesRepeat
    = repeat;
        }
            
    public void run(TestResult result) {
            
    //重復執(zhí)行
                    for (int i= 0; i < fTimesRepeat; i++) {
                
    if (result.shouldStop())
                    
    break;
                
    super.run(result);
            }
        }
            
    }

        RepeatedTest繼承TestDecorator ,覆寫run(TestReult result)方法,重復執(zhí)行,super.run(result)將調(diào)用傳入的TestCase的run(TestResult result)方法,這已經(jīng)在TestDecorator默認實現(xiàn)。看看使用方式,使用裝飾模式的好處不言而喻。

    TestSuite suite = new TestSuite();
    suite.addTest(
    new TestSetup(new RepeatedTest(new Testmath("testAdd"),12)));

    posted @ 2007-04-06 12:39 dennis 閱讀(2559) | 評論 (3)編輯 收藏

        在JUnit執(zhí)行測試時,我們經(jīng)常需要初始化一些環(huán)境供測試代碼使用,比如數(shù)據(jù)庫連接、mock對象等等,這些初始化代碼應當在每一個測試之前執(zhí)行并在測試方法運行后清理。在JUnit里面就是相應的setUp和tearDown方法。如果沒有這兩個方法,那么我們要在每個測試方法的代碼內(nèi)寫上一大堆重復的初始化和清理代碼,這是多么愚蠢的做法。那么JUnit是怎么讓setUp和tearDown在測試執(zhí)行前后被調(diào)用的呢?
        如果你查看下TestCase方法,你會發(fā)現(xiàn)TestCase和TestSuite的run()方法都是將執(zhí)行測試的任務委托給了TestResult,由TestResult去執(zhí)行測試代碼并收集測試過程中的信息(這里用到了Collecting Parameter模式)。
       
        public TestResult run() {
            TestResult result
    = createResult();
            run(result);
            
    return result;
        }
        
    /**
         * Runs the test case and collects the results in TestResult.
         * This is the template method that defines the control flow
         * for running a test case.
         
    */
        
    public void run(TestResult result) {
            result.run(
    this);
        }
       
        我們直接找到TestResult,看看它的run方法:
    /**
         * Runs a TestCase.
         
    */
        
    protected void run(final TestCase test) {
            startTest(test);
            Protectable p 
    = new Protectable() {
                
    public void protect() throws Throwable {
                    test.runBare();
                }
            };
            runProtected(test, p);
            endTest(test);
        }

        這里實例化了一個內(nèi)部類,內(nèi)部類實現(xiàn)了Protectable接口的 protect()方法,并執(zhí)行傳入的TestCase的runBare()方法,顯然,真正的測試代碼在TestCase的runBare()方法中,讓我們來看下:


            //將被子類實現(xiàn)
        protected void setUp() throws Throwable {
        }
        
    //同上,將被具體的TestCase實現(xiàn)
        protected void tearDown() throws Throwable {
        }
         /**
         * 模板方法
         * Runs the bare test sequence.
         * 
    @exception Throwable if any exception is thrown
         
    */
        
    public void runBare() throws Throwable {
            setUp();
            
    try {
                runTest();
            }
            
    finally {
                tearDown();
            }
        }

    真相水落石出,對于每一個測試方法,都遵循這樣的模板:setUp->執(zhí)行測試 runTest()->tearDown。這正是模板方式模式的一個應用例子。什么是template method模式呢?

    Template Method模式

    類行為模式的一種
    1.意圖:定義一個操作中的算法的骨架,而將一些延遲步驟到子類中。Template Method使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些步驟。
    2.適用場景:
    1)一次性實現(xiàn)算法的不變部分(基本骨架),將可變的行為留給子類來完成
    2)子類中的公共部分(比如JUnit中的初始化和清理)被抽取到一個公共父類中以避免代碼重復。
    3)控制了子類的擴展,這里其實也有類似回調(diào)函數(shù)的性質(zhì),具體步驟先在骨架中注冊,在具體執(zhí)行時被回調(diào)。

    3.UML圖和結(jié)構(gòu)
       
      抽象父類定義了算法的基本骨架(模板方法),而不同的子類實現(xiàn)具體的算法步驟,客戶端由此可以與算法的更改隔離。

    4.效果:
    1)模板方法是代碼復用的基本技術,在類庫中經(jīng)常使用,可以減少大量的代碼重復
    2)通過隔離算法的不變和可變部分,增加了系統(tǒng)的靈活性,擴展算法的某些步驟將變的很容易。

        了解了Template Method模式之后,讓我們回到JUnit的源碼,看看runTest()方法,這里主要應用的是java的反射技術,對于學習反射技術的有參考價值:
    protected void runTest() throws Throwable {
            Method runMethod
    = null;
            
    try {
                runMethod
    = getClass().getDeclaredMethod(fName, new Class[0]);
            } 
    catch (NoSuchMethodException e) {
                fail(
    "Method \""+fName+"\" not found");
            }
            
    if (runMethod != null && !Modifier.isPublic(runMethod.getModifiers())) {
                fail(
    "Method \""+fName+"\" should be public");
            }

            
    try {
                runMethod.invoke(
    thisnew Class[0]);
            }
            
    catch (InvocationTargetException e) {
                e.fillInStackTrace();
                
    throw e.getTargetException();
            }
            
    catch (IllegalAccessException e) {
                e.fillInStackTrace();
                
    throw e;
            }
        }

       

    posted @ 2007-04-06 09:54 dennis 閱讀(2663) | 評論 (0)編輯 收藏

        今天在javaeye論壇上看到有人有這個需求,順手寫了下。原理就是通過遍歷Controllers目錄,并用正則表達式取出Controller名和它所有的action。

        @controllers=Hash.new
        path
    ="#{RAILS_ROOT}/app/controllers/"
        Dir
    .new(path).entries.each do |f|
          
    if !f.index('.rb').nil? and f.index('.rb')>0
            
            controller
    =File.open(path+f.to_s)
            s
    =controller.read
             
    /class\s(.*)\s\</.match(s)
            controller_name
    =$1.to_s
            actions
    =[]
            s
    .scan(/def\s(.*)\s/).each|action| actions<<(action[0]) }
            
    @controllers[controller_name]=actions
            controller
    .close
          end
        end
        
        
    @controllers.each_pair do |name, actions|
          actions
    .each do |action| 
            puts 
    |name<<" "<<action
          end
        end


    posted @ 2007-04-05 20:21 dennis 閱讀(969) | 評論 (0)編輯 收藏


        我們知道JUnit支持不同的使用方式:swt、swing的UI方式,甚至控制臺方式,那么對于這些不同的UI我們?nèi)绾翁峁┙y(tǒng)一的接口供它們獲取測試過程的信息(比如出現(xiàn)的異常信息,測試成功,測試失敗的代碼行數(shù)等等)?我們試想一下這個場景,當一個error或者exception產(chǎn)生的時候,測試能夠馬上通知這些UI客戶端:發(fā)生錯誤了,發(fā)生了什么錯誤,錯誤是什么等等。顯而易見,這是一個訂閱-發(fā)布機制應用的場景,應當使用觀察者模式。那么什么是觀察者模式呢?

    觀察者模式(Observer)

    Observer是對象行為型模式之一

    1.意圖:定義對象間的一種一對多的依賴關系,當一個對象的狀態(tài)發(fā)現(xiàn)改變時,所有依賴于它的對象都得到通知并被自動更新

    2.適用場景:
    1)當一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,通過觀察者模式將這兩者封裝在不同的獨立對象當中,以使它們可以獨立的變化和復用
    2)當一個對象改變時,需要同時改變其他對象,并且不知道其他對象的具體數(shù)目
    3)當一個對象需要引用其他對象,但是你又不想讓這個對象與其他對象產(chǎn)生緊耦合的時候

    3.UML圖:
                     
       Subject及其子類維護一個觀察者列表,當需要通知所有的Observer對象時調(diào)用Nitify方法遍歷Observer集合,并調(diào)用它們的update方法更新。而具體的觀察者實現(xiàn)Observer接口(或者抽象類),提供具體的更新行為。其實看這張圖,與Bridge有幾分相似,當然兩者的意圖和適用場景不同。

    4.效果:
    1)目標和觀察者的抽象耦合,目標僅僅與抽象層次的簡單接口Observer松耦合,而沒有與具體的觀察者緊耦合
    2)支持廣播通信
    3)缺點是可能導致意外的更新,因為一個觀察者并不知道其他觀察者,它的更新行為也許將導致一連串不可預測的更新的行為

    5.對于觀察者實現(xiàn)需要注意的幾個問題:
    1)誰來觸發(fā)更新?最好是由Subject通知觀察者更新,而不是客戶,因為客戶可能忘記調(diào)用Notify
    2)可以通過顯式傳參來指定感興趣的更新
    3)在發(fā)出通知前,確保Subject對象狀態(tài)的一致性,也就是Notify操作應該在最后被調(diào)用
    4)當Subject和Observer的依賴關系比較復雜的時候,可以通過一個更新管理器來管理它們之間的關系,這是與中介者模式的結(jié)合應用。


        討論完觀察者模式,那我們來看JUnit是怎么實現(xiàn)這個模式的。在junit.framework包中我們看到了一個Observer接口——TestListener,看看它的代碼:
    package junit.framework;

    /**
     * A Listener for test progress
     
    */
    public interface TestListener {
       
    /**
         * An error occurred.
         
    */
        
    public void addError(Test test, Throwable t);
       
    /**
         * A failure occurred.
         
    */
         
    public void addFailure(Test test, Throwable t);  
       
    /**
         * A test ended.
         
    */
         
    public void endTest(Test test); 
       
    /**
         * A test started.
         
    */
        
    public void startTest(Test test);
    }

        接口清晰易懂,就是一系列將測試過程的信息傳遞給觀察者的操作。具體的子類將接受這些信息,并按照它們的方式顯示給用戶。
         比如,我們看看swing的UI中的TestRunner,它將這些信息顯示在一個swing寫的UI界面上:
        public void startTest(Test test) {
            showInfo(
    "Running: "+test);
        }

        public void addError(Test test, Throwable t) {
            fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));
            appendFailure(
    "Error", test, t);
        }
        
    public void addFailure(Test test, Throwable t) {
            fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));
            appendFailure(
    "Failure", test, t);
        }
        public void endTest(Test test) {
            setLabelValue(fNumberOfRuns, fTestResult.runCount());
            fProgressIndicator.step(fTestResult.wasSuccessful());
        }

    可以看到,它將錯誤信息,異常信息保存在List或者Vector集合內(nèi),然后顯示在界面上:
    private void showErrorTrace() {
            
    int index= fFailureList.getSelectedIndex();
            
    if (index == -1)
                
    return;
        
            Throwable t
    = (Throwable) fExceptions.elementAt(index);
            
    if (fTraceFrame == null) {
                fTraceFrame
    = new TraceFrame();
                fTraceFrame.setLocation(
    100100);
               }
            fTraceFrame.showTrace(t);
            fTraceFrame.setVisible(
    true);
        }
        
    private void showInfo(String message) {
            fStatusLine.setFont(PLAIN_FONT);
            fStatusLine.setForeground(Color.black);
            fStatusLine.setText(message);
        }
        
    private void showStatus(String status) {
            fStatusLine.setFont(BOLD_FONT);
            fStatusLine.setForeground(Color.red);
            fStatusLine.setText(status);
        }

    而Junit中的目標對象(Subject)就是TestResult對象,它有添加觀察者的方法:

    /**
         * Registers a TestListener
         
    */
        
    public synchronized void addListener(TestListener listener) {
            fListeners.addElement(listener);
        }

    而通知觀察者又是怎么做的呢?請看這幾個方法,都是循環(huán)遍歷觀察者列表,并調(diào)用相應的更新方法:

    /**
         * Adds an error to the list of errors. The passed in exception caused the
         * error.
         
    */
        
    public synchronized void addError(Test test, Throwable t) {
            fErrors.addElement(
    new TestFailure(test, t));
            
    for (Enumeration e = fListeners.elements(); e.hasMoreElements();) {
                ((TestListener) e.nextElement()).addError(test, t);
            }
        }

        
    /**
         * Adds a failure to the list of failures. The passed in exception caused
         * the failure.
         
    */
        
    public synchronized void addFailure(Test test, AssertionFailedError t) {
            fFailures.addElement(
    new TestFailure(test, t));
            
    for (Enumeration e = fListeners.elements(); e.hasMoreElements();) {
                ((TestListener) e.nextElement()).addFailure(test, t);
            }
        }

        
    /**
         * Registers a TestListener
         
    */
        
    public synchronized void addListener(TestListener listener) {
            fListeners.addElement(listener);
        }

        
    /**
         * Informs the result that a test was completed.
         
    */
        
    public synchronized void endTest(Test test) {
            
    for (Enumeration e = fListeners.elements(); e.hasMoreElements();) {
                ((TestListener) e.nextElement()).endTest(test);
            }
        }


    使用這個模式后帶來的好處:
    1)上面提到的Subject與Observer的抽象耦合,使JUnit可以支持不同的使用方式
    2)支持了廣播通信,目標對象不關心有多少對象對自己注冊,它只是通知注冊的觀察者

    最后,我實現(xiàn)了一個簡單的ConsoleRunner,在控制臺執(zhí)行JUnit,比如我們寫了一個簡單測試:
    package junit.samples;

    import junit.framework.*;

    /**
     * Some simple tests.
     * 
     
    */
    public class SimpleTest extends TestCase {
        
    protected int fValue1;

        
    protected int fValue2;

        
    public SimpleTest(String name) {
            
    super(name);
        }

        
    public void setUp() {
            fValue1 
    = 2;
            fValue2 
    = 3;
        }

        
    public void testAdd() {
            
    double result = fValue1 + fValue2;
            
    assert(result == 5);
        }


        
    public void testEquals() {
            assertEquals(
    1212);
            assertEquals(
    12L12L);
            assertEquals(
    new Long(12), new Long(12));

            assertEquals(
    "Size"1212);
            assertEquals(
    "Capacity"12.011.990.01);
        }
    }

    使用ConsoleRunner調(diào)用這個測試,代碼很簡單,不多做解釋了:
    package net.rubyeye.junit.framework;

    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Vector;

    import junit.framework.Test;
    import junit.framework.TestListener;
    import junit.framework.TestResult;
    import junit.samples.SimpleTest;
    //實現(xiàn)觀察者接口
    public class ConsoleRunner implements TestListener {

        
    private TestResult fTestResult;

        
    private Vector fExceptions;

        
    private Vector fFailedTests;

        
    private List fFailureList;

        
    public ConsoleRunner() {
            fExceptions 
    = new Vector();
            fFailedTests 
    = new Vector();
            fFailureList 
    = new ArrayList();
        }

        
    public void endTest(Test test) {
            System.out.println(
    "測試結(jié)束:");
            String message 
    = test.toString();
            
    if (fTestResult.wasSuccessful())
                System.out.println(message 
    + " 測試成功!");
            
    else if (fTestResult.errorCount() == 1)
                System.out.println(message 
    + " had an error");
            
    else
                System.out.println(message 
    + " had a failure");

            
    for (int i = 0; i < fFailureList.size(); i++) {
                System.out.println(fFailureList.get(i));
            }
            
    for (int i = 0; i < fFailedTests.size(); i++) {
                System.out.println(fFailureList.get(i));
            }
            
    for (int i = 0; i < fExceptions.size(); i++) {
                System.out.println(fFailureList.get(i));
            }

            System.out.println(
    "------------------------");
        }

        
    public void startTest(Test test) {
            System.out.println(
    "開始測試:" + test);
        }

        
    public static TestResult createTestResult() {
            
    return new TestResult();
        }

        
    private String truncateString(String s, int length) {
            
    if (s.length() > length)
                s 
    = s.substring(0, length) + "";
            
    return s;
        }

        
    public void addError(Test test, Throwable t) {
            System.out.println(fTestResult.errorCount());
            appendFailure(
    "Error", test, t);
        }

        
    public void addFailure(Test test, Throwable t) {
            System.out.println(fTestResult.failureCount());
            appendFailure(
    "Failure", test, t);
        }

        
    private void appendFailure(String kind, Test test, Throwable t) {
            kind 
    += "" + test;
            String msg 
    = t.getMessage();
            
    if (msg != null) {
                kind 
    += ":" + truncateString(msg, 100);
            }
            fFailureList.add(kind);
            fExceptions.addElement(t);
            fFailedTests.addElement(test);
        }

        
    public void go(String args[]) {
            Method[] methods 
    = SimpleTest.class.getDeclaredMethods();

            
    for (int i = 0; i < methods.length; i++) {
                
    //取所有以test開頭的方法
                if (methods[i].getName().startsWith("test")) {
                    Test test 
    = new SimpleTest(methods[i].getName());
                    fTestResult 
    = createTestResult();
                    fTestResult.addListener(ConsoleRunner.
    this);
                    
    //執(zhí)行測試
                    test.run(fTestResult);
                }
            }

        }

        
    public static void main(String args[]) {
            
    new ConsoleRunner().go(args);
        }

    }

    posted @ 2007-04-05 17:19 dennis 閱讀(2908) | 評論 (0)編輯 收藏

    僅列出標題
    共56頁: First 上一頁 42 43 44 45 46 47 48 49 50 下一頁 Last 
    主站蜘蛛池模板: 一级特黄a大片免费| 亚洲精品第一国产综合野| 一级毛片免费播放试看60分钟| 在线观看人成网站深夜免费| 77777午夜亚洲| 成年午夜视频免费观看视频| 亚洲欧美国产国产一区二区三区| 成人黄页网站免费观看大全| 亚洲精品国产国语| 成年轻人网站色免费看| 色欲色欲天天天www亚洲伊| 日韩精品视频免费网址| 麻豆va在线精品免费播放| 免费大黄网站在线观| 无码毛片一区二区三区视频免费播放| 亚洲日韩中文在线精品第一| 中文永久免费观看网站| 久久精品国产亚洲av水果派| 无码精品A∨在线观看免费| 亚洲色大网站WWW永久网站| 免费日本黄色网址| 91视频免费观看高清观看完整| 亚洲AV无码一区二区乱子伦| 57pao一国产成永久免费| 亚洲人成色77777在线观看| 亚洲国产精品一区二区九九| 久久久久免费精品国产| 亚洲国产成人久久精品app | 性做久久久久免费观看| 美女巨胸喷奶水视频www免费| 亚洲av永久无码精品国产精品| 1000部拍拍拍18勿入免费凤凰福利| 亚洲中文字幕AV每天更新| 亚洲国产成人精品久久久国产成人一区二区三区综 | 亚洲欧洲视频在线观看| 日韩高清免费观看| 玖玖在线免费视频| 亚洲日韩中文字幕一区| 中文字幕亚洲图片| 日韩欧美一区二区三区免费观看| 五级黄18以上免费看|