莊周夢蝶

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

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

          到rubyforge下載ruby-DBI,解壓后cd到目錄運(yùn)行如下命令:
          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ù)庫無關(guān),提供與數(shù)據(jù)庫無關(guān)的標(biāo)準(zhǔn)接口
          2.Database Driver——數(shù)據(jù)庫驅(qū)動(dòng),與數(shù)據(jù)庫相關(guān)

          DBI也是很簡單易用的,一個(gè)完整的使用例子,對于初學(xué)者可能有點(diǎn)幫助:
          require 'dbi'
          begin
            
          #連接數(shù)據(jù)庫
            dbh=DBI.connect("DBI:Mysql:dbi_test:localhost","root","")
            
            dbh.columns(
          "simple").each do |h|
              p h
            end
            
          #示范3種事務(wù)處理方式
            #手動(dòng)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é)果集以及測量查詢時(shí)間
            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 閱讀(4657) | 評論 (1)編輯 收藏

              這個(gè)包的類主要用于spring框架的異常處理和一些核心的助手類(與框架具體部分無關(guān)的)。
              這個(gè)包中主要應(yīng)用到了簡單工廠模式,用于判斷jdk版本,根據(jù)jdk版本不同提供不同的集合類、當(dāng)前方法棧信息等。我們來看看是如何判斷當(dāng)前用戶的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屬性來進(jìn)行jdk版本的判斷。而CollectionFactory依據(jù)這個(gè)類來創(chuàng)建不同的集合類型,如果是jdk1.4就優(yōu)先使用jdk1.4的集合框架,再次選擇Commons Collections,最后才不得已就使用jdk1.3的集合框架,這里比較有趣的是判斷Commons Collections的方法就是嘗試Class.forName一個(gè)Commons集合框架中的對象,如果成功,當(dāng)然證明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 也都是工廠類。可以看到,一個(gè)優(yōu)秀的通用框架對于版本的兼容性非常重視。

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

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

              
          /**
               * 查找當(dāng)前方法調(diào)用是否則在某類的某個(gè)方法中
               * 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);

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

          }

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

              
          static class Jdk14ControlFlow implements ControlFlow {
             
          }

          在這里,我們可以學(xué)到的東西就如何去判斷當(dāng)前方法棧的信息?jdk1.4之前只能通過對StackTrace的字符串進(jìn)行分析,而jdk1.4引入了java.lang.StackTraceElement用于獲取當(dāng)前方法調(diào)用所處的棧幀的信息,看看spring的使用方法,相當(dāng)簡單:
          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;
                  }
          }

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

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

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

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

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

          gradient*

          梯度,比如gradient:red-blue

          granite

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

          logo

          logo型的圖像. 如: "logo:"后面會(huì)多顯示一個(gè)五角星^_^

          netscape

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

          null*

          空白 使用方式: "null:"

          rose


          玫瑰 使用方式 : "rose:"

          xc*

          設(shè)置一個(gè)背景色,比如"xc:green"


          一個(gè)修改的例子,在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應(yīng)用的結(jié)合,和一般的驗(yàn)證碼原理一樣,將產(chǎn)生的隨機(jī)數(shù)存儲(chǔ)在session或者request范圍內(nèi),提交的時(shí)候進(jìn)行比較驗(yàn)證即可。比如產(chǎn)生圖片的時(shí)候?qū)㈦S機(jī)字母存儲(chǔ)在session[:code]中:

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

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

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

           unless session[:code]==params[:type_code]
                flash[:notice]
          ='驗(yàn)證碼填寫錯(cuò)誤,請重新注冊,謝謝!'
                
          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 閱讀(3446) | 評論 (3)編輯 收藏

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

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

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

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

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

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

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

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

          6.Range,書中翻譯為區(qū)間,我倒更喜歡范圍這個(gè)詞。區(qū)間的3個(gè)用途:
          1)用作序列,最常見的,如1..2,a..z等,可以定義自己的區(qū)間,只要實(shí)現(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)作為間隔,看看某個(gè)值是否落入?yún)^(qū)間范圍內(nèi),使用===操作符比較

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

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

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

          9.ruby中的多線程:
          1)ruby創(chuàng)建線程,見下面這個(gè)例子,開3個(gè)線程分別訪問3個(gè)站點(diǎn),并且對3個(gè)線程通過調(diào)用join方法,直到3個(gè)線程都結(jié)束,主線程才結(jié)束,來自書中例子:
          require 'net/http'
          pages
          =%w(www.javaeye.com www.sina.com.cn www.aygfsteel.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)線程中如何共享變量?可以通過[]=簡單地把當(dāng)前線程看成一個(gè)散列表,這里沒有考慮同步問題:
          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)通過設(shè)置abort_on_exception,如果是true,未處理的線程異常將殺死所有正在運(yùn)行的線程,如果是false,則殺死當(dāng)前運(yùn)行的線程,其他線程繼續(xù)運(yùn)行。修改上面的例子查看下:
          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}"
          輸出(隨機(jī)的):
          8, 1, 6, 3, Failed:boom!
          2, 4, 7, 0, 5, count =9

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

          4)通過線程的一系列方法:pass,join,value,stop來進(jìn)行線程的調(diào)度
          5)互斥的實(shí)現(xiàn),與其他語言一樣,不外乎加鎖、信號量、隊(duì)列的方式。看看加鎖是如何做的,通過monitor庫的關(guān)鍵字synchronize實(shí)現(xiàn),如下面這個(gè)例子,兩個(gè)線程遞增同一個(gè)變量,似乎結(jié)果應(yīng)該是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é)果不會(huì)是20000,而是比它小的一個(gè)數(shù)值,這里的問題就是因?yàn)樵L問共享資源沒有進(jìn)行同步的緣故,使用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)條件變量和隊(duì)列的方式不準(zhǔn)備抄書了,ruby中對線程的操作都是直接調(diào)用操作系統(tǒng)的命令,特別是*nix支持的非常好,可惜我對linux也是個(gè)初哥。

          10.ruby中表達(dá)式很重要的一個(gè)特點(diǎn)是:任何表達(dá)式都有返回值,包括賦值語句、條件語句、循環(huán)語句之類。
          1)ruby中對布爾表達(dá)式的規(guī)定是:任何不是nil或者常量false的值都為真
          2)注意,在方法中調(diào)用訪問屬性的函數(shù),需要寫上調(diào)用者self,否則將處理為局部變量
          3)defined?方法用于返回參數(shù)的描述,如果未定義,返回nil
          4)邏輯表達(dá)式中,and和or的優(yōu)先級低于&&,||
          5)ruby沒有for語句,因?yàn)閞uby通過內(nèi)建在對象中的迭代器提供了循環(huán)訪問的能力,最簡單的內(nèi)建迭代器:loop do ....end
          6)只要你的類支持each方法,你就可以使用for ... in ..語句循環(huán)它
          7)對循環(huán)可以使用break(打斷跳出),redo(從頭重新循環(huán),當(dāng)前迭代),next進(jìn)行調(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對應(yīng)的是begin...rescue...ensure...end,將產(chǎn)生異常的代碼放在這個(gè)塊中進(jìn)行處理。可以通過$!得到異常信息,或者提供局部變量名,我改寫了一下我的google在線翻譯機(jī),增加異常處理,并用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)絡(luò)錯(cuò)誤:"+e
            
          else
              result 
          = $1 
              puts 
          '翻譯內(nèi)容:'+temp[0]
              puts 
          'google返回:'+result
              puts 
          '-------------------退出請打e或者exit---------------'
              translate
            end
          end
          translate
          引發(fā)一個(gè)異常使用raise語句,重新引發(fā)當(dāng)前異常,如果沒有,就引發(fā)一個(gè)RuntimeError,常見使用方式:
          raise InterfaceException,"keyboard failure",caller
          其中的caller生成了棧的信息。另外,catch...throw語句用于在異常發(fā)生時(shí)從深度嵌套的結(jié)構(gòu)中跳轉(zhuǎn)出來。

          12。關(guān)于模塊,作用有二:作為命名空間和Mixin機(jī)制。模塊的Mixin機(jī)制可以說是ruby的一個(gè)精華所在,通過Mixin,可以變相地實(shí)現(xiàn)了多重繼承,并且可以動(dòng)態(tài)地為類添加和刪除功能。這一部分注意兩點(diǎn):
          1)模塊中定義的實(shí)例變量可能與包含模塊的類的實(shí)例變量產(chǎn)生名稱沖突。可以使用模塊一級的散列表,以當(dāng)前對象的ID做索引,來保存特定于當(dāng)前模塊的實(shí)例變量解決這個(gè)問題。比如:
          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)是關(guān)于方法的查找路徑,順序是:當(dāng)前類-》類的mixin模塊-》超類-》超類的mixin,另外mixin的模塊,最后混入的同名方法將覆蓋前面混入的。

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

          或者進(jìn)入irb后手工require:
          require 'irb/completation'

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

          3)在linux下可以通過配置.irbrc配置文件來進(jìn)行初始化定制,在windows環(huán)境你可以在ruby安裝目錄下的bin看到一個(gè)irb.bat文件,通過配置文件來定制irb,比如我們?yōu)閕rb增加ri和tab自動(dòng)補(bǔ)齊功能:
          @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>——?jiǎng)?chuàng)建子會(huì)話,如果提供obj,作為self
          jobs,irb_jobs——列出irb的子會(huì)話
          irb_fg,fg n——切換子會(huì)話
          kill n,irb_kill n——?dú)⑺酪粋€(gè)irb子會(huì)話

          14.類的實(shí)例變量,類除了類變量、實(shí)例變量外,還有一個(gè)類的實(shí)例變量的概念:
          class Test
            #類的實(shí)例變量
            @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 閱讀(2338) | 評論 (0)編輯 收藏

              這個(gè)包的說明是:Support for styling values as Strings, with ToStringCreator as central class.
          這個(gè)包簡單來說就是提供一個(gè)pretty-printing功能的輔助類,而ToStringCreator就是用于產(chǎn)生一個(gè)可以輸出經(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);
          或者寫個(gè)簡單例子感受下:
          int [] a={1,2,3,4,5,6,7,8,9};
          System.out.println(
          new ToStringCreator(a).toString());
              
          輸出:
          [@18558d2 array<Integer>[123456789]]

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

              首先,你需要理解ToStringStyler和ValueStyle兩個(gè)接口,ToStringStyler定義了描述一個(gè)輸入的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模式,而兩個(gè)接口ToStringStyler、ValueStyler和它們的相應(yīng)實(shí)現(xiàn)DefaultToStringStyler、DefaultValueStyler又是策略模式(Strategy)的應(yīng)用體現(xiàn)。ValueStyler和DefaultValueStyler之間不僅僅是策略模式,同時(shí)也是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 其實(shí)又是使用ValueStyler是訪問每個(gè)不同類型的子元素并返回優(yōu)美格式的String。實(shí)現(xiàn)的相當(dāng)精巧和靈活:
             
          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 閱讀(1136) | 評論 (0)編輯 收藏

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

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

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

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



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

          ConcreteComponent——定義一個(gè)對象,可以給這個(gè)對象添加職責(zé)

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

          ConcreteDecorator——具體裝飾類

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

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

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


          //默認(rèn)實(shí)現(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)實(shí)現(xiàn),我們還體現(xiàn)了針對接口編程的原則,替換一個(gè)新的打印方式很靈活,但是客戶開始提需求了——人生無法避免的三件事:交稅、死亡和需求變更。客戶要求打印頁眉,你首先想到的是繼承:
          package com.rubyeye.design_pattern.decorator;

          public class AnotherPrintTicket implements PrintTicket {

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

          }

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

          package com.rubyeye.design_pattern.decorator;

          public class PrintTicketDecorator implements PrintTicket {

              
          protected PrintTicket printTicket;

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

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

          }

          然后,我們實(shí)現(xiàn)兩個(gè)具體的裝飾類——打印頁眉和頁腳:
          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里面的應(yīng)用。作為一個(gè)測試框架,應(yīng)該方便地支持二次開發(fā),也許用戶開發(fā)自己的TestCase,添加自定義的功能,比如執(zhí)行重復(fù)測試、多線程測試等等。動(dòng)態(tài)添加職責(zé),而又不想使用靜態(tài)繼承,這正是Decorator使用的地方。在junit.extensions包中有一個(gè)TestDecorator,正是所有裝飾類的父類,也是作為二次開發(fā)的基礎(chǔ),它實(shí)現(xiàn)了Test接口,而Test接口就是我們定義的抽象接口:
          public class TestDecorator implements  Test {
              
          //保有一個(gè)指向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)提供了兩個(gè)裝飾類:junit.extensions.ActiveTest用于處理多線程,junit.extensions.RepeatedTest用于執(zhí)行重復(fù)測試,看看RepeatedTest是怎么實(shí)現(xiàn)的:
          public class RepeatedTest extends  TestDecorator {
                  
          //重復(fù)次數(shù)
              private int fTimesRepeat;

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

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

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

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

              在JUnit執(zhí)行測試時(shí),我們經(jīng)常需要初始化一些環(huán)境供測試代碼使用,比如數(shù)據(jù)庫連接、mock對象等等,這些初始化代碼應(yīng)當(dāng)在每一個(gè)測試之前執(zhí)行并在測試方法運(yùn)行后清理。在JUnit里面就是相應(yīng)的setUp和tearDown方法。如果沒有這兩個(gè)方法,那么我們要在每個(gè)測試方法的代碼內(nèi)寫上一大堆重復(fù)的初始化和清理代碼,這是多么愚蠢的做法。那么JUnit是怎么讓setUp和tearDown在測試執(zhí)行前后被調(diào)用的呢?
              如果你查看下TestCase方法,你會(huì)發(fā)現(xiàn)TestCase和TestSuite的run()方法都是將執(zhí)行測試的任務(wù)委托給了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);
              }

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


                  //將被子類實(shí)現(xiàn)
              protected void setUp() throws Throwable {
              }
              
          //同上,將被具體的TestCase實(shí)現(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();
                  }
              }

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

          Template Method模式

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

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

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

              了解了Template Method模式之后,讓我們回到JUnit的源碼,看看runTest()方法,這里主要應(yīng)用的是java的反射技術(shù),對于學(xué)習(xí)反射技術(shù)的有參考價(jià)值:
          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 閱讀(2670) | 評論 (0)編輯 收藏

              今天在javaeye論壇上看到有人有這個(gè)需求,順手寫了下。原理就是通過遍歷Controllers目錄,并用正則表達(dá)式取出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 閱讀(972) | 評論 (0)編輯 收藏


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

          觀察者模式(Observer)

          Observer是對象行為型模式之一

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

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

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

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

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


              討論完觀察者模式,那我們來看JUnit是怎么實(shí)現(xiàn)這個(gè)模式的。在junit.framework包中我們看到了一個(gè)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,它將這些信息顯示在一個(gè)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());
              }

          可以看到,它將錯(cuò)誤信息,異常信息保存在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中的目標(biāo)對象(Subject)就是TestResult對象,它有添加觀察者的方法:

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

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

          /**
               * 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);
                  }
              }


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

          最后,我實(shí)現(xiàn)了一個(gè)簡單的ConsoleRunner,在控制臺(tái)執(zhí)行JUnit,比如我們寫了一個(gè)簡單測試:
          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)用這個(gè)測試,代碼很簡單,不多做解釋了:
          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;
          //實(shí)現(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 閱讀(2911) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共56頁: First 上一頁 42 43 44 45 46 47 48 49 50 下一頁 Last 
          主站蜘蛛池模板: 德保县| 花垣县| 伊吾县| 齐齐哈尔市| 大埔县| 荣成市| 额济纳旗| 阳东县| 汕尾市| 肥乡县| 潜山县| 榕江县| 喀喇沁旗| 连江县| 永春县| 海阳市| 嘉善县| 临沭县| 临猗县| 尖扎县| 绥江县| 富平县| 新干县| 怀仁县| 永和县| 得荣县| 突泉县| 紫阳县| 沛县| 贺兰县| 阜宁县| 马山县| 陇川县| 闵行区| 西峡县| 南木林县| 时尚| 馆陶县| 昌平区| 津南区| 高平市|