如鵬網 大學生計算機學習社區

          CowNew開源團隊

          http://www.cownew.com 郵件請聯系 about521 at 163.com

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            363 隨筆 :: 2 文章 :: 808 評論 :: 0 Trackbacks

          #

          蓋茨北大演講被鬧場2007年4月20日

          from騰訊網。
          4月20日,比爾蓋茨在北大演講,在他為一行人頒獎時,一位男子手拿海報并大聲用英語叫喊反對微軟壟斷。

          男子海報上寫著“Free software”“Open resource”,抗議微軟壟斷,呼吁開源,事后該男子被警方帶走進行調查。

          據熟知內情的人士透露,此男子系CSDN的前市場總監,是個衷情于開源軟件的業內人士。據傳,此人甚至將名字改為帶有“開源”的字樣。

          組圖:蓋茨北大演講遭遇反壟斷抗議
          高舉手書海報

          組圖:蓋茨北大演講遭遇反壟斷抗議
          現場被控制

          組圖:蓋茨北大演講遭遇反壟斷抗議 

          來自CSDN的信息顯示,該名男子是LPI中國首席代表王洋,自號王開源,是開源軟件的積極分子。

          2005年王開源先生任即時科研集團總裁助理;2006年加入CSDN,參與創建OSDN開源社區,并與北京點擊查看北京及更多城市天氣預報大學、中國開源軟件聯盟合作,首次把“國際軟件自由日”引進中國。離開CSDN后加入LPI(Linux Professional Institute)。




          CowNew開源團隊聲援王開源大哥!!!
          希望政府不要再被蓋茨這個大資本家迷惑!!!
          國家采購首選開源!!!
          發展國內開源事業,做民族的脊梁!!!
          from:http://blog.csdn.net/arthur5933/archive/2007/04/20/1571507.aspx

                 《愿歌》



          未名湖畔囂聲澀 ,
          帝國列強作秀狼;
          博雅先賢清風淚,
          男兒七尺當自強!


                 開源
          2007年4月20日晨


          posted @ 2007-04-20 15:16 CowNew開源團隊 閱讀(1811) | 評論 (20)編輯 收藏

          以前寫書的時候寫的一個案例,如果應用于實際的項目還需要改進,應一個網友的要求貼上來,僅供參考
          <?xml version="1.0" encoding="GB2312" ?>
          <project name="dailybuild" default="main">
          <!--tomcat的路徑-->
          <property name="tomcathome.dir"
              value="C:\Program Files\Apache Software Foundation\Tomcat 5.0\"/>
          <!--tomcat管理控制臺路徑-->
          <property name="tomcathome.mgr.dir" value="http://127.0.0.1:8080/manager/"/>   
          <!--tomcat管理控制臺用戶名-->
          <property name="tomcat.username" value="admin"/>   
          <!--tomcat管理控制臺密碼-->
          <property name="tomcat.password"  value=""/>
          <!--tomcat中-->
          <property name="cownewwebpath" value="/CownewPISWeb"/>
          <!--Web應用的路徑-->   
          <property name="cownewwebhome.dir"
              value="${tomcathome.dir}webapps\CownewPISWeb\"/> 
          <!--Web應用的源碼路徑(主要供稍后的編譯用)-->     
          <property name="cownewwebhome.java.dir"
              value="${cownewwebhome.dir}WEB-INF\java\"/>
          <!--Web應用輸出的class路徑-->       
          <property name="cownewwebhome.classes.dir"
              value="${cownewwebhome.dir}WEB-INF\classes\"/> 
          <!--CVS根路徑-->    
          <property name="cvsRoot" value=":pserver:楊中科:123456@192.168.1.6:/cvsrep"/>    
          <!--代碼檢出的路徑-->
          <property name="cvs.outtemp.dir" value="${cownewwebhome.dir}cvsout/"/>
          <!--BVT測試(冒煙測試)的測試結果輸出的路徑-->
          <property name="bvt.output.dir" value="c:/bvtreport/"/>

          <taskdef name="stopTomcat" classname="org.apache.catalina.ant.StopTask">       
            <classpath>           
              <path location="${tomcathome.dir}/server/lib/catalina-ant.jar"/>       
            </classpath>   
          </taskdef>
          <taskdef name="startTomcat" classname="org.apache.catalina.ant.StartTask">       
            <classpath>           
              <path location="${tomcathome.dir}/server/lib/catalina-ant.jar"/>       
            </classpath>   
          </taskdef>
          <taskdef
          name="sendBuildEmail" classname="com.cownew.dailybuild.taskdefs.DailyBuildMailTask">       
            <classpath>           
              <path location="../lib/ant-dailybuild.jar"/>       
            </classpath>   
          </taskdef>
          <target name="main">
          <echo>停止Tomcat</echo>
          <stopTomcat url="${tomcathome.mgr.dir}" username="${tomcat.username}"
              password="${tomcat.password}" path="${cownewwebpath}"/>
          <echo>清除原有構建文件</echo>
          <delete dir="${cownewwebhome.dir}"/>
          <echo>開始從CVS下拉代碼</echo>
          <mkdir dir="${cvs.outtemp.dir}"/>
          <cvs cvsRoot="${cvsRoot}" package="CownewPISWeb" dest="${cvs.outtemp.dir}"/>
          <mkdir dir="${cownewwebhome.dir}"/>
          <copy todir="${cownewwebhome.dir}">
            <fileset dir="${cvs.outtemp.dir}CownewPISWeb/WebContent/"/>
          </copy>

          <mkdir dir="${cownewwebhome.java.dir}"/>
          <copy todir="${cownewwebhome.java.dir}">
            <fileset dir="${cvs.outtemp.dir}CownewPISWeb/src/"/>
          </copy>
          <delete dir="${cvs.outtemp.dir}"/>
          <echo>開始編譯源代碼</echo>
          <mkdir dir="${cownewwebhome.classes.dir}"/>
          <javac srcdir="${cownewwebhome.java.dir}" destdir="${cownewwebhome.classes.dir}" >
             <classpath>
                <pathelement location="E:\保留文檔\java\常用包\junit-3.8.1.jar" />
                <pathelement location="E:\保留文檔\java\常用包\struts.jar" />
                <pathelement location="E:\保留文檔\java\常用包\log4j.jar" />
                <pathelement location="E:\保留文檔\java\常用包\dom4j.jar" />
             </classpath>
          </javac>
          <delete dir="${cownewwebhome.java.dir}"/>
          <echo>啟動Tomcat</echo>
          <startTomcat url="${tomcathome.mgr.dir}" username="${tomcat.username}"
              password="${tomcat.password}" path="${cownewwebpath}"/>
          <echo>開始冒煙測試</echo>
          <delete>
            <fileset dir="${bvt.output.dir}"
                     includes="*.*"
                     defaultexcludes="false"/>
          </delete>
          <junit>
              <classpath>
                 <pathelement location="E:\保留文檔\java\常用包\junit-3.8.1.jar"/> 
                 <pathelement location="${cownewwebhome.classes.dir}"/>         
              </classpath>
              <formatter type="brief" usefile="true"/>
              <batchtest todir="${bvt.output.dir}">
                <fileset dir="${cownewwebhome.classes.dir}">
                    <include name="**/bvt/*.class" />
                 </fileset>
              </batchtest>   
          </junit>

          <echo>開始發送構建結果郵件</echo>

          <sendBuildEmail smtpHost="smtp.mycompany.com" smtpUserId="scmheader" smtpPassword="123456"
            senderEmail="scmheader@mycompany.com" title="日構建已經完成"
            jdbcClass="net.sourceforge.jtds.jdbc.Driver"
            dbUrl="jdbc:jtds:sqlserver://127.0.0.1/dailybuild;user=sa">
            日構建已經完成,請到此處查看構建日志:\\192.168.1.15\dailybuild\result.log
            點擊此處查看冒煙測試結果: \\192.168.1.15\bvtreport\
          </sendBuildEmail>
          </target>
          </project>

          posted @ 2007-04-20 09:10 CowNew開源團隊 閱讀(1189) | 評論 (1)編輯 收藏

           

          與VCL、AWT等框架中的圖形界面框架不同,Swing創造性的采用了MVC(Model View Controller) 模式。MVC 把控件(Component)劃分成三個部分:模型( Model):管理這個模塊中所用到的數據和值,如某個數據的最大值、最小值、當前值等數據;視圖( View):管理如何將模型顯示給用戶;控制器(Controller) 決定如何處理用戶和該模塊交互時產生的事件,如用戶單擊一個按鈕等。Sun 出于對視圖和控制器之間的依賴關系的考慮, 在 Swing 設計中將 MVC體系簡化為分離模型體系 ( Separable Model Architecture) , 將其中的控制器和視圖結合成 UI 代理。

          界面中的每個控件都包含三種特征:

          它的狀態:比如一個按鈕的狀態

          它的外觀:顏色、尺寸等

          它的行為:對事件作出的反應

          以一個按鈕為例,它有可用、不可用狀態,在不同的LookAndFeel中有不同的外觀顯示,在鼠標按下、鼠標右擊等事件中有自己獨特的響應方式。如果管理按鈕狀態、繪制按鈕外觀,響應時間等任務都由按鈕負責的話就違背了面向對象設計中的“單一責任原則”。Swing開發人員采用MVC模式解決了此問題,將控件的LookAndFeel同一個對象關聯到一起,同時將其內容保存到另一個對象中。控制器負責控制用戶輸入事件。比如鼠標單擊、按鍵操作等,它會決定將這些事件轉換成模型中的改變.還是視圖中的改變。例如,假定用戶在文本框中按下某個鍵,控制器就會調用模型的“插入字符”命令。隨后,模型會通知視圖更新來顯示新的模型。視圖不用關心什么時候進行文字改變,只要模型通知它更新它就會更新。這樣控制器只用與用戶交互并把交互結果反映到模型中去;模型負責維護狀態,當狀態變化時通知視圖更新顯示;視圖不負責用戶交互的狀態維護,它只是根據模型中的狀態繪制不同的界面。

          Swing中的大多數控件的模型是由一個名字以Model結尾的接口實現的。比如按鈕對應的模型接口就是 ButtonModel,JDK中定義了ButtonModel的默認實現類DefaultButtonModel。下面是ButtonModel各個方法的說明:

          boolean isArmed():如果按鈕被按下,且鼠標指針仍停留在按鈕上則返回true

          boolean isSelected():如果按鈕處于選擇狀態則返回true

          boolean isEnabled():如果按鈕可用則返回true

          boolean isPressed():如果按鈕被按下,但鼠標沒有松開,則返回true

          boolean isRollover():如果鼠標指針在按鈕之上則返回true

          public int getMnemonic():返回按鈕的助記鍵

          public String getActionCommand():返回命令字符串

          此外還有對應的設置狀態方法:public void setArmed(boolean b)、public void setSelected(boolean b)、public void setEnabled(boolean b)、public void setPressed(boolean b)、public void setRollover(boolean b)、public void setMnemonic(int key)、public void setActionCommand(String s)等。

          每一個JButton都保存著一個ButtonModel對象,我們可以通過JButton的getModel方法來取得該模型對象:

          JButton btn = new JButton(“test”);

          ButtonModel btnModel = btn.getModel();

          通過這個模型對象我們就可以得到按鈕的是否可用等狀態,不過這個模型是給控件開發者使用的,對于普通使用者來說無需直接調用它,JButton提供了方法來間接的取得這些屬性,這一點可以從AbstractButton類的isSelected方法中看出來:

          public boolean isSelected()

          {

          return model.isSelected();

          }

          Swing中大部分控件都由自己的模型,比如JList控件的ListModel、JTable的TableModel、JSpinner的SpinnerModel、JComboBox的SpinnerModel(SpinnerModel是從ListModel派生出來的),這些模型也由默認的實現,名稱通常為模型名前加Default。

          Swing中的大多數控件的視圖是由一個名字以UI結尾的類實現的,比如按鈕對應的模型接口就是 ButtonUI。由于視圖在不同的LookAndFeel中有不同的展現形式,所以控件的視圖對每一種LookAndFeel都提供了不同的實現。以JLabel為例,它就有MetalLabelUI、MotifLabelUI、WindowsLabelUI等對應不同LookAndFeel的實現。所有的視圖都要直接或者間接的從ComponentUI抽象類派生,ComponentUI類中的方法都是供Model回調使用的,下面是ComponentUI主要方法的說明:

          public void installUI(JComponent c):這個方法在ComponentUI 實例被安裝到UI代理的時候被觸發,用來根據LookAndFeel配置控件。它需要完成如下工作:為Color、Font、Border、Icon等類型的屬性設定默認值;根據需要設置布局管理器;創建子控件;初始化監聽器;為控件設置PropertyChangeListener 監聽器以檢測控件屬性變化事件;初始化快捷鍵、Tab鍵順序等;初始化數據;

          public void uninstallUI(JComponent c):這個方法在ComponentUI 實例被從UI代理移除的時候觸發。需要在此方法中撤銷任何在installUI中進行的配置,要保證JComponent實例變為潔凈狀態(也就是沒有監聽器,沒有LookAndFeel專有屬性等)。它需要完成如下工作:從控件中移除border;從控件中移除布局管理器;從控件中移除子控件;從控件中移除事件、屬性監聽器、從控件中移除快捷鍵、Tab鍵順序等;將數據標記為可以垃圾回收。

          public void paint(Graphics g, JComponent c):為本視圖的LookAndFeel繪制控件。

          public void update(Graphics g, JComponent c):通知UI代理繪制指定控件。當特定的控件被繪制的時候此方法會被觸發。這個方法的默認實現是用背景色填充控件,并且立即調用paint方法。

          public Dimension getPreferredSize(JComponent c):返回當前LookAndFeel下控件的最佳尺寸。默認實現是返回null;

          public Dimension getMinimumSize(JComponent c):返回當前LookAndFeel下控件的最小尺寸。默認實現是返回getPreferredSize的值;

          public Dimension getMaximumSize(JComponent c):返回當前LookAndFeel下控件的最大尺寸。默認實現是返回getPreferredSize的值;

          public boolean contains(JComponent c, int x, int y):判斷指定的x、y坐標是否存在于當前LookAndFeel下的控件中。

          public static ComponentUI createUI(JComponent c):為指定的控件返回UI代理實例。如果UI代理子類是無狀態的,它也可以返回多控件共享的實例。如果UI代理子類是有狀態的,則它必須為某個控件返回一個新的實例。

          public int getAccessibleChildrenCount(JComponent c):返回所有可訪問子控件的數量。

          public Accessible getAccessibleChild(JComponent c, int i):返回指定的子控件

          posted @ 2007-04-19 19:37 CowNew開源團隊 閱讀(3879) | 評論 (3)編輯 收藏

                目前Hibernate的輔助開發工具有很多,商業的有MyEclipse,自由工具則有HibernateTool、HibernateSynch、Middlegen等,這些工具提供了現有數據庫表來生成代碼和配置文件等功能,大大簡化了開發。這些工具的缺陷也是非常明顯的,其指導思想不明確,僅僅是一個代碼生成器而已。比如HibernateTool暗示開發人員要先建立數據庫表,這其實是一種數據驅動的開發模式,使得開發人員先要去思考數據是如何存儲的、有哪些字段,而不是先思考系統對象之間的關系是怎么樣的。
                基于此,我們開發了一個正向建模的Hibernate輔助工具,使用這個工具可以通過圖形界面建立模型,然后可以由模型生成持久類、映射文件和數據庫表。這樣開發人員只要在工具中定義對象的模型即可,工具可以自動完成其他的工作。并且工具要保留設計的模型,這樣開發人員可以迭代的修改模型,這就可以保證前期的工作成果盡可能多的被利用了。
                此版修復的Bug主要有界面布局的刷新、生成代碼的正確性等,此版本在Eclipse3.2.0下測試通過,下載包包含二進制文件和源碼。
                CowNew開源團隊 http://www.cownew.com   

                         
          文檔PDF格式

          文檔Word格式

          二進制文件和源碼下載>>>

          posted @ 2007-04-17 18:50 CowNew開源團隊 閱讀(1638) | 評論 (4)編輯 收藏

          2007年4月14日,CowNew開源團隊組織了團隊第一次北京地區聚會。本次聚會參會成員:散仙、Problem、星、Kingchou、紅孔雀、曲金龍、楊中科。此外我們還邀請到了言實文化傳播有限公司的執行董事屈辰晨先生、北京科技大學物流研究所的潘洪波先生和北京華美漢盛軟件公司的張成先生及其同事。

           

           

           

           

          posted @ 2007-04-16 15:27 CowNew開源團隊 閱讀(302) | 評論 (1)編輯 收藏

           

          經常可以從開發人員口中聽到“面向對象”這個詞:

          場景1、

          A:我今天開始用面向對象的方法設計程序了!

          B:你怎么做的?

          A:我把保存文件、加載文件封裝成了一個類,以后只要調用這個類就可以實現文件操作了。

          場景2、

          A:我開始學習Java了,面向對象的語言,你不要再學VB了,好土呀!

          B:VB怎么了?

          A:VB是面向過程的,已經過時了,Java中都是類,很時髦!

          B:VB中也有類呀!

          A:(無語)

          場景3、

          A:面向對象思想就是好呀,我真的離不開Java了!

          B:你又用什么高超技術了?

          A:我今天從一個操縱數據庫的類繼承了一個子類,然后重寫了它的保存到數據庫的方法,然后把數據通過Socket發送到了遠程客戶端了,而調用者根本不知道,哈哈!

          場景4、

          A:我推薦你用的Java不錯吧?

          B:真是不錯,面向對象就是好,JDK里邊也有好多好多的類可以用,不用像在VB里邊那樣要去查API文檔了。

          A:但是我聽說現在又出了個面向方面編程,咱們看來又落伍了呀,看來做編程真的不是長久之計。

          寫幾個類就是面向對象了嗎?繼承父類就是為了重用父類的代碼嗎?覆蓋父類的方法就可以瞞天過海了嗎?VB中也有類,它是面向對象嗎?

          1.1

          類與對象

          “類”和“對象”是面向對象編程中最基本的概念,從語言的角度來講,“類”是用戶自定義的具有一定行為的數據類型,“對象”則是“類”這種數據類型的變量。通俗的講,“類”是具有相同或相似行為的事物的抽象,“對象”是“類”的實例,是是一組具有相關性的代碼和數據的組合體,是有一定責任的實體。

          類本身還可以進一步抽象為類型,類型是一種更高層次上的抽象,它只用來描述接口,比如抽象類和接口就是一種類型。當一個類型的接口包含另外一個類型的接口時,我們就可以說它是此類型的子類型。類型是用來標識特定接口的,如果一個對象接受某個接口定義的所有行為,那么我們就可以說該對象具有該類型。一個對象同時擁有多種類型。

          面向對象編程的特性

          面向對象編程有三個特性:封裝,繼承,多態。這三個特性從低級到高級描述了面向對象的特征。一種語言只有同時具備這三種特性才能被稱為面向對象的語言。VB中也有類,它的類也支持封裝和簡單的繼承,但是它不支持所有的繼承語義和多態,因此VB只能被稱為基于對象的語言。

          封裝是所有抽象數據類型(ADT)的特性,很多剛剛接觸面向對象的人認為封裝就是就是面向對象。將程序按照一定的邏輯分成多個互相協作的部分,并將對外界有用的穩定的部分暴露出來,而將會發生的改變隱藏起來,外界只能通過暴露的部分向這個對象發送操作請求從而享受對象提供的服務,而不必管對象內部是如何運行的,這就是封裝。理解封裝是理解面向對象的第一個步驟,40%的程序員對面向對象的理解僅停留在封裝這個層次。

          繼承也稱為派生,繼承關系中,被繼承的稱為基類,從基類繼承而得的被稱為派生類或者子類。繼承是保持對象差異性的同時共享對象相似性的復用。能夠被繼承的類總是含有并只含有它所抽象的那一類事務的共同特點。繼承提供了實現復用,只要從一個類繼承,我們就擁有了這個類的所有行為。理解繼承是理解面向對象的第二個步驟,50%的程序員對面向對象的理解僅停留在繼承這個層次。語義上的“繼承”表示“是一種(is-a)”的關系。很多人體會到了繼承在代碼重用方面的優點,而忽視了繼承的語義特征。于是很多濫用繼承的情況就發生了,關于這一點我們將會在后邊介紹。

          多態是“允許用戶將父對象設置成為一個或更多的它的子對象相等的技術,賦值后,基類對象就可以根據當前賦值給它的派生類對象的特性以不同的方式運作”(Charlie Calvert)。多態擴大了對象的適應性,改變了對象單一繼承的關系。多態是行為的抽象,它使得同名方法可以有不同的響應方式,我們可以通過名字調用某一方法而無需知道哪種實現將被執行,甚至無需知道執行這個實現的對象類型。多態是面向對象編程的核心概念,只有理解了多態,才能明白什么是真正的面向對象,才能真正發揮面向對象的最大能力。不過可惜的是,只有極少數程序員能真正理解多態。

          對象之間的關系

          對象之間有兩種最基本的關系:繼承關系,組合關系。

          繼承關系

          繼承關系可以分為兩種:一種是類對接口的繼承,被稱為接口繼承;另一種是類對類的繼承,被稱為實現繼承。繼承關系是一種“泛化/特化”關系,基類代表一般,而派生類代表特殊。

          組合關系。

          組合是由已有的對象組合而成新對象的行為,組合只是重復運用既有程序的功能,而非重用其形式。組合與繼承的不同點在于它表示了整體和部分的關系。比如電腦是由CPU、內存、顯示器、硬盤等組成的,這些部件使得電腦有了計算、存儲、顯示圖形的能力,但是不能說電腦是由CPU繼承而來的。

          1.2

          對象之間有兩種最基本的關系:繼承關系,組合關系。通過這兩種關系的不斷迭代組合最終組成了可用的程序。但是需要注意的就是要合理使用這兩種關系。

          派生類是基類的一個特殊種類,而不是基類的一個角色。語義上的“繼承”表示“is-a”(是一種)的關系,派生類“is-a”基類,這是使用繼承關系的最基本前提。如果類A是類B的基類,那么類B應該可以在任何A出現的地方取代A,這就是“Liskov代換法則(LSP)”。如果類B不能在類A出現的地方取代類A的話,就不要把類B設計為類A的派生類。

          舉例來說,“蘋果”是“水果”的派生類,所以“水果是植物的果實”這句話中的“水果”可以用“蘋果”來代替:“蘋果是植物的果實”;而“蘋果”不是“香蕉”的派生類,因為“香蕉是一種種子退化的了的植物果實”不能被“蘋果”替換為“蘋果是一種種子退化的了的植物果實”。

          舉這個例子好像有點多余,不過現實的開發中卻經常發生“蘋果”從“香蕉”繼承的事情。

          某企業中有一套信息系統,其中有一個“客戶(Customer)”基礎資料,里邊記錄了客戶的名稱、地址、email等信息。后來系統要進行升級,增加一個“供應商(Supplier)”基礎資料,開發人員發現“供應商”中有“客戶”中的所有屬性,只是多了一個“銀行帳號”屬性,所以就把“供應商”設置成“客戶”客戶的子類。

          圖 2.1

          到了年終,老板要求給所有的客戶通過Email發送新年祝福,由于“供應商”是一種(is-a)“客戶”,所以系統就給“供應商”和“客戶”都發送了新年祝福。第二天很多供應商都感動流涕的給老板打電話“謝謝老板呀,我們供應商每次都是求著貴公司買我們的東西,到了年終你們還忘不了我們,真是太感謝了!”。老板很茫然,找來開發人員,開發人員這才意識到問題,于是在發送Email的程序里做了判斷“如果是供應商則不發送,否則發送”,一切ok了。到了年初,老板要求給所有很長時間沒有購買他們產品的“客戶”,打電話進行問候和意見征集。由于“供應商”是一種(is-a)“客戶”,所以第二天電話里不斷出現這樣的回答:“你們搞錯了吧,我們是你們的供應商呀!”。老板大發雷霆,開發人員這才意識到問題的嚴重性,所以在系統的所有涉及到客戶的地方都加了判斷“如果是供應商則……”,一共修改了60多處,當然由于疏忽遺漏了兩處,所以后來又出了一次類似的事故。

          我們可以看到錯誤使用繼承的害處了。其實更好的解決方案應該是,從“客戶”和“供應商”中抽取一個共同的基類“外部公司”出來:

          圖 2.2

          這樣就將“客戶”和“供應商”之間的繼承關系去除了。

          派生類不應大量覆蓋基類的行為。派生類具有擴展基類的責任,而不是具有覆蓋(override)基類的責任。如果派生類需要大量的覆蓋或者替換掉基類的行為,那么就不應該在兩個類之間建立繼承關系。

          讓我們再來看一個案例:

          一個開發人員要設計一個入庫單、一張出庫單和一張盤點單,并且這三張單都有登帳的功能,通過閱讀客戶需求,開發人員發現三張單的登帳邏輯都相同:遍歷單據中的所有物品記錄,然后逐筆登到臺帳上去。所以他就設計出了如下的程序:

          圖 2.3

          把登帳邏輯都寫到了“庫存業務單據”這個抽象類中,三張單據從這個類繼承即可。過了三個月,用戶提出了新的需求:盤點單在盤點過程中,如果發現某個貨物的盤虧量大于50則停止登帳,并向操作人員報警。所以開發人員在盤點單中重寫了“庫存業務單據”的“登帳”方法,實現了客戶要求的邏輯。又過了半個月,客戶要求出庫登帳的時候不僅要進行原先的登帳,還要以便登帳一邊計算出庫成本。所以開發人員在出庫單中重寫了“庫存業務單據”的“登帳”方法,實現了客戶要求的邏輯。到了現在“庫存業務單據”的“登帳”方法的邏輯只是對“入庫單”有用了,因為其他兩張單據都“另立門戶”了。

          這時候就是該我們重新梳理系統設計的時候了,我們把“庫存業務單據”的“登帳”方法設置成抽象方法,具體的實現代碼由具體子類自己決定:

          圖 2.4

          注意此處的“庫存業務單據”中的“登帳”方法是斜體,在UML中表示此方法是一個抽象方法。這個不難理解,每張單據都肯定有登帳行為,但是每張單據的登帳行為都有差異,因此在抽象類中定義類的“登帳”方法為抽象方法以延遲到子類中去實現。

          繼承具有如下優點:實現新的類非常容易,因為基類的大部分功能都可以通過繼承關系自動賦予派生類;修改或者擴展繼承來的實現非常容易;只要修改父類,派生的類的行為就同時被修改了。

          初學面向對象編程的人會認為繼承真是一個好東西,是實現復用的最好手段。但是隨著應用的深入就會發現繼承有很多缺點:繼承破壞封裝性。基類的很多內部細節都是對派生類可見的,因此這種復用是“白箱復用”;如果基類的實現發生改變,那么派生類的實現也將隨之改變。這樣就導致了子類行為的不可預知性;從基類繼承來的實現是無法在運行期動態改變的,因此降低了應用的靈活性。

          繼承關系有很多缺點,如果合理使用組合則可以有效的避免這些缺點,使用組合關系將系統對變化的適應力從靜態提升到動態,而且由于組合將已有對象組合到了新對象中,因此新對象可以調用已有對象的功能。由于組合關系中各個各個對象的內部實現是隱藏的,我們只能通過接口調用,因此我們完全可以在運行期用實現了同樣接口的另外一個對象來代替原對象,從而靈活實現運行期的行為控制。而且使用合成關系有助于保持每個類的職責的單一性,這樣類的層次體系以及類的規模都不太可能增長為不可控制的龐然大物。因此我們優先使用組合而不是繼承。

          當然這并不是說繼承是不好的,我們可用的類總是不夠豐富,而使用繼承復用來創建一些實用的類將會不組合來的更快,因此在系統中合理的搭配使用繼承和組合將會使你的系統強大而又牢固。

          1.3

          接口的概念

          接口是一種類型,它定義了能被其他類實現的方法,接口不能被實例化,也不能自己實現其中的方法,只能被支持該接口的其他類來提供實現。接口只是一個標識,標識了對象能做什么,至于怎么做則不在其控制之內,它更像一個契約。

          任何一個類都可以實現一個接口,這樣這個類的實例就可以在任何需要這個接口的地方起作用,這樣系統的靈活性就大大增強了。

          接口編程的實例

          SQL語句在各個不同的數據庫之間移植最大的麻煩就是各個數據庫支持的語法不盡相同,比如取出表的前10行數據在不同數據庫中就有不同的實現。

          MSSQLServer:Select top 10 * from T_Table

          MySQL:select * from T_Table limit 0,10

          Oracle:select * from T_Table where ROWNUM <=10

          我們先來看一下最樸素的做法是怎樣的:

          首先定義一個SQL語句翻譯器類:

          public class Test1SQLTranslator

          {

          private int dbType;

          public Test1SQLTranslator(int dbType)

          {

          super();

          this.dbType = dbType;

          }

          public String translateSelectTop(String tableName, int count)

          {

          switch (dbType) {

          case 0:

          return "select top " + count + " * from " + tableName;

          case 1:

          return "select * from " + tableName + " limit 0," + count;

          case 2:

          return "select * from " + tableName + " where ROWNUM<=" + count;

          default:

          return null;

          }

          }

          }

          然后如下調用

          public static void main(String[] args)

          {

          String tableName = "T_Table";

          int count = 10;

          int dbType = 0;

          Test1SQLTranslator translator = new Test1SQLTranslator(dbType);

          String sql = translator.translateSelectTop(tableName,count);

          System.out.println(sql);

          }

          如果要增加對新的數據庫的支持,比如DB2,那么就必須修改Test1SQLTranslator類,增加一個對DB2的case語句,這種增加只能是在編輯源碼的時候進行添加,無法在運行時動態添加。再來看一下如果用基于接口的編程方式是如何實現的。

          首先,定義接口ISQLTranslator,這個接口定義了所有SQL翻譯器的方法,目前只有一個翻譯Select top的方法:

          public interface ISQLTranslator

          {

          public String translateSelectTop(String tableName, int count);

          }

          接著我們為各個數據庫寫不同的翻譯器類,這些翻譯器類都實現了ISQLTranslator接口:

          public class MSSQLServerTranslator implements ISQLTranslator

          {

          public String translateSelectTop(String tableName, int count)

          {

          return "select top " + count + " * from " + tableName;

          }

          }

          public class MySQLTranslator implements ISQLTranslator

          {

          public String translateSelectTop(String tableName, int count)

          {

          return "select * from " + tableName +" limit 0,"+count;

          }

          }

          public class OracleSQLTranslator implements ISQLTranslator

          {

          public String translateSelectTop(String tableName, int count)

          {

          return "select * from " + tableName+" where ROWNUM<="+count;

          }

          }

          如下調用:

          public static void main(String[] args)

          {

          String tableName = "T_Table";

          int count = 10;

          ISQLTranslator translator = new MSSQLServerTranslator();

          String sql = translator.translateSelectTop(tableName, count);

          System.out.println(sql);

          }

          運行以后,打印出了:

          select top 10 from T_Table

          可以看到,不同的數據庫翻譯實現由不同的類來承擔,這樣最大的好處就是可擴展性極強,比如也許某一天出現了了支持中文語法的數據庫,我要為它做翻譯器只需再增加一個類:

          public class SinoServerTranslator implements ISQLTranslator

          {

          public String translateSelectTop(String tableName, int count)

          {

          return "讀取表"+tableName+"的前"+count+"行";

          }

          }

          修改調用代碼:

          public static void main(String[] args)

          {

          String tableName = "T_Table";

          int count = 10;

          ISQLTranslator translator = new SinoServerTranslator();

          String sql = translator.translateSelectTop(tableName, count);

          System.out.println(sql);

          }

          運行后控制臺打印出:

          讀取表T_Table的前10行

          這里的translator 可以隨意實例化,只要實例化的類實現了ISQLTranslator 就可以了,這個類也可以通過配置文件讀取,甚至是其他類傳遞過來的,這都無所謂,只要是實現了ISQLTranslator 接口它就能正常工作。

          如果要給SQL語句加上驗證功能,也就是翻譯的時候首先驗證一下翻譯的結果是否能在數據庫中執行,我們就可以采用偷天換日的方式來進行。

          首先創建一個VerifyTranslator類:

          public class VerifyTranslator implements ISQLTranslator

          {

          private ISQLTranslator translator;

          private Connection connection;

          public VerifyTranslator(ISQLTranslator translator, Connection connection)

          {

          super();

          this.translator = translator;

          this.connection = connection;

          }

          public String translateSelectTop(String tableName, int count)

          {

          String sql = translator.translateSelectTop(tableName, count);

          PreparedStatement ps = null;

          try

          {

          ps = connection.prepareStatement(sql);

          ps.execute();

          } catch (SQLException e)

          {

          DbUtils.close(ps);

          return "wrong sql";

          }

          return sql;

          }

          }

          這個類接受一個實現了ISQLTranslator 接口的變量和數據庫連接做為構造參數,最重要的是這個類本身也實現了ISQLTranslator 接口,這樣它就完全能“偽裝”成SQL翻譯器來行使翻譯的責任了,不過它沒有真正執行翻譯,它把翻譯的任務轉發給了通過構造函數傳遞來的那個翻譯器變量:

          String sql = translator.translateSelectTop(tableName, count);

          它自己的真正任務則是進行SQL語句的驗證:

          ps = connection.prepareStatement(sql);

          再次修改調用代碼:

          public static void main(String[] args)

          {

          String tableName = "T_Table";

          int count = 10;

          ISQLTranslator translator = new VerifyTranslator(

          new SinoServerTranslator(), getConnection());

          String sql = translator.translateSelectTop(tableName, count);

          System.out.println(sql);

          }

          運行后控制臺打印出:

          wrong sql

          下面這段代碼看上去是不是很眼熟呢?

          ISQLTranslator translator = new VerifyTranslator(new SinoServerTranslator(), getConnection());

          這段代碼和我們經常寫的流操作非常類似:

          InputStream is = new DataInputStream(new FileInputStream(new File(“c:/boot.ini”)));

          這就是設計模式中經常提到的“裝飾者模式”。

          針對接口編程

          從上面的例子我們可以看出,當代碼寫到:

          String sql = translator.translateSelectTop(tableName, count);

          的時候,代碼編寫者根本不關心translator這個變量到底是哪個類的實例,它只知道它調用了接口約定支持的translateSelectTop方法。

          當一個對象需要與其他對象協作完成一項任務時,它就需要知道那個對象,這樣才能調用那個對象的方法來獲得服務,這種對象對另一個協作對象的依賴就叫做關聯。如果一個關聯不是針對具體類,而是針對接口的時候,任何實現這個接口的類都可以滿足要求,因為調用者僅僅關心被依賴的對象是不是實現了特定接口。

          當發送的請求和具體的請求響應者之間的關系在運行的時候才能確定的時候,我們就稱之為動態綁定。動態綁定允許在運行期用具有相同接口的對象進行替換,從而實現多態。多態使得對象間彼此獨立,所有的交互操作都通過接口進行,并可以在運行時改變它們之間的依賴關系。

          針對接口編程,而不是針對實現編程是面向對象開發中的一個非常重要的原則,也是設計模式的精髓!

          針對接口編程有數不清的例子,比如在Hibernate中,集合屬性必須聲明為Set、Map、List等接口類型,而不能聲明為HashSet、HashMap、ArrayList等具體的類型,這是因為Hibernate在為了實現LazyLoad,自己開發了能實現LazyLoad功能的實現了Set、Map、List等接口的類,因為我們的屬性的類型只聲明為這些屬性為這些接口的類型,因此Hibernate才敢放心大膽的返回這些特定的實現類。

          現實的開發過程中有如下一些違反針對接口編程原則的陋習:

          陋習1

          ArrayList list = new ArrayList();

          for(int i=0;i<10;i++)

          {

          list.add(……);

          }

          這里使用的是ArrayList的add方法,而add方法是定義在List接口中的,因此沒有必要聲明list變量為ArrayList類型,修改如下:

          List list = new ArrayList();

          for(int i=0;i<10;i++)

          {

          list.add(……);

          }

          陋習2

          public void fooBar(HashMap map)

          {

          Object obj = map.get(“something”);

          ……

          }

          在這個方法中只是調用Map接口的get方法來取數據,所以就不能要求調用者一定要傳遞一個HashMap類型的變量進來。修改如下:

          public void fooBar(Map map)

          {

          Object obj = map.get(“something”);

          ……

          }

          這樣修改以后用戶為了防止傳遞給fooBar方法的Map被修改,用戶就可以這樣調用了:

          Map unModMap = Collections.unmodifiableMap(map);

          obj.fooBar(unModMap);

          Collections.unmodifiableMap是JDK提供的一個工具類,可以返回一個對map的包裝,返回的map是不可修改的,這也是裝飾者模式的典型應用。

          試想如果我們把接口聲明為public void fooBar(HashMap map)用戶還能這么調用嗎?

          1.4 抽象類

          抽象類的主要作用就是為它的派生類定義公共接口,抽象類把它的部分操作的實現延遲到派生類中來,派生類也能覆蓋抽象基類的方法,這樣可以很容易的定義新類。抽象類提供了一個繼承的出發點,我們經常定義一個頂層的抽象類,然后將某些位置的實現定義為抽象的,也就是我們僅僅定義了實現的接口,而沒有定義實現的細節。

          一個抽象類應該盡可能多的擁有共同的代碼,但是不能把只有特定子類才需要的方法移動到抽象類中。Eclipse的某些實現方式在這一點上就做的不是很好,Eclipse的一些界面類中提供了諸如CreateEmailField之類的方法來創建界面對象,這些方法并不是所有子類都用得到的,應該把它們抽取到一個工具類中更好。同樣的錯誤在我們的案例的JCownewDialog中也是存在的,這個類中就提供了CreateOKBtn、CreateCanceBtn兩個方法用來創建確定、取消按鈕。

          在設計模式中,最能體現抽象類優點的就是模版方法模式。模版方法模式定義了一個算法的骨架,而具體的實現步驟則由具體的子類類來實現。JDK中的InputStream類是模版方法的典型代表,它對skip等方法給出了實現,而將read等方法定義為抽象方法等待子類去實現。后邊案例中的PISAbstractAction等類也是模版方法的一個應用。

          在實際開發中接口和抽象類從兩個方向對系統的復用做出了貢獻,接口定義了系統的服務契約,而抽象類則為這些服務定義了公共的實現,子類完全可以從這些抽象類繼承,這樣就不用自己實現自己所不關心的方法,如果抽象類提供的服務實現不滿足自己的要求,那么就可以自己從頭實現接口的服務契約。

          posted @ 2007-04-10 15:14 CowNew開源團隊 閱讀(1611) | 評論 (8)編輯 收藏

              CowNew開源團隊是目前國內較活躍的開源團隊之一,我們致力于基礎平臺的開發以及知識推廣,目前已經有數個成熟的基礎產品問世,并與國內數家網站、出版社建立了知識推廣的合作關系。目前團隊處于成長的轉型期,急需有志之士加盟共同為中國的開源事業奮斗。
              我們需要如下幾類隊友:
              (1)基礎平臺開發工程師
               要求:對軟件開發有很強的興趣;有較多的時間能夠投入到開源開發中來;有較強的計算機學科基礎(編譯原理、數據結構、操作系統等)或者熟悉Eclipse、NetBeans等平臺的插件開發;能夠勝任基礎平臺的開發;
               職責:SQL翻譯器、SQL調優器、數據庫監控、開發工具等基礎平臺的開發;
              (2)技術類圖書作者
              要求:對軟件開發有很強的興趣;有較強的計算機學科基礎(編譯原理、數據結構、操作系統等);有實際的開發經驗;有較好的書面表達能力;有較多的時間能夠投入到書籍編寫中來;
              職責:計算機學科基礎類圖書的編寫;
              (3)外聯人員
              要求:有較強的溝通能力和組織協調能力;有校園活動的組織經驗;有較多的時間能夠投入到團隊的宣傳工作中來;
              職責:團隊校園活動的組織以及團隊的對外宣傳;
           
              注意:我們的團隊目前只是一個非盈利性、非實體組織,目前仍然在發展的探索中,我們無法在短時間之類讓您看到經濟回報,團隊成員也全部是利用業余時間從事這項工作的,所以如果您需要的一個能立即給您帶來經濟回報的全職工作,那么我們的團隊并不適合您。CowNew開源團隊的明天是美好的,如果您有創業精神,如果您有充足的時間和熱情投入到這份具有前途的事業中的話,那么我們歡迎您的加入。
              團隊在4月14日將會舉辦一次北京地區的聚會活動,希望新加入的隊友能夠參加我們的活動。
              有意加入團隊的朋友請將您要加入的職位和您的基本情況通過Email發送給我們,我們的Email是:about521@163.com
              http://www.cownew.com
               thanks to dudu and other administrators.
            
          posted @ 2007-04-10 14:33 CowNew開源團隊 閱讀(1105) | 評論 (0)編輯 收藏

                最近要做一個Python的基于Eclipse的界面設計器,因此我對各種GUI設計工具做了一下分析,發現GUI設計工具也有一個門派。
                在那個懵懂的年代,一切界面代碼都是要開發人員手工書寫,這無疑增加了開發難度,Delphi、VB等工具的出現扭轉了這個局面,使用這些工具開發人員只要在控件面板上拖拖拽拽就可以完成界面的設計,做到了“所見即所得”的開發方式。仔細分析,GUI設計工具有如下幾個門派:基于界面文件的純代碼生成、代碼生成與界面文件結合、無界面文件方式。

                基于界面文件的純代碼生成:NetBeans是這類工具的典型代表(如果我沒記錯的話JBuilder也是這樣實現的),NetBeans中與界面設計有關的有兩個文件:.java文件和.form文件。.form文件中是以XML格式描述界面布局和控件的屬性等信息;.java文件則是通過解析.form文件生成的代碼,生成的界面代碼主要位于initComponents方法中,這個方法在NetBeans IDE中是無法手工編輯的。在用戶拖拉控件的時候,NetBeans就將拖拉的控件描述增加到.form文件中,并且即時將新的代碼生成到.java文件中。這樣實現的好處有如下幾點:IDE實現容易,IDE的開發人員只要關注于如何將界面信息轉化為.form文件和如何將.form文件解析生成.java代碼即可,無需關心用戶修改.java代碼造成的反向解析問題;.java文件可以脫離.form而存在,也就是.form文件只是在設計期有意義,而在運行期是無用的。缺點是:用戶無法手工修改生成的代碼。
               代碼生成與界面文件結合:Delphi和VB是這類工具的典型代表。以Delphi為例,在Delphi中新建以后界面以后將會存在兩個文件:.dfm和.pas,.dfm描述了界面布局和控件的屬性等信息,.pas則定義了控件的變量和事件處理函數。在編譯的時候.dfm被編譯到可執行文件中,運行的時候動態解析.dfm文件來構建界面。與NetBeans不同的就是.dfm文件是有運行期的意義的,如果沒有.dfm文件文件,程序將無法編譯運行。這樣的方式通常只適用于Delphi、VB這樣代碼和IDE結合過于緊密的語言,很難將生成的代碼進行手工修改。
            無界面文件方式:Eclipse的Visual Editor是最經典的例子。使用Visual Editor進行GUI繪制的時候,只存在一個.java文件,Visual Editor將用戶繪制的界面直接解析為.java代碼,如果用戶修改了.java代碼,Visual Editor會運行一個虛擬機,在虛擬機中運行用戶修改后的文件,得到運行時的程序界面,然后將這個界面繪制到窗口設計器中。這樣做可以將所有的界面信息都集成到一個文件中,并且支持用戶手工修改生成的代碼;由于設計器中的界面是通過另外一個虛擬機運行而得到的,在界面設計器中看到的界面就是運行時的界面,這樣保證了真正的“所見即所得”。這樣做的壞處也是明顯的,由于需要重新啟動一個虛擬機,導致了速度很慢,資源占用比較高,使用Visual Editor的時候經常造成Eclipse內存不足退出。   
           
                我在開發界面設計器的早期采用的基于界面文件的純代碼生成方式,系統中有一個.aui文件和對應生成的.py源代碼文件,后來由于系統需求(主要是要求允許開發人員修改生成的代碼),我就準備改用無界面文件方式。如果采用Visual Editor的無界面文件方式難度是比較大的,而且會導致資源占用太大,因此我采用了另外一種思路,也就是在內存中為每個界面維護一個對象模型(樹狀結構),在用戶繪制界面的時候去修改這個對象模型,在用戶保存界面的時候去解析這個對象模型生成.py源代碼;在由.py源代碼加載繪制設計器中的界面的時候,首先通過解析.py 源代碼生成源代碼的抽象語法樹(AST),然后解析這個AST生成界面的對象模型,這樣就可以很輕松的繪制界面了。這樣做不僅有Visual Editor的優點,而且占用資源比較小;不過由于手工修改代碼的千差萬別,如果開發人員修改的代碼采用了比較生僻的語法,有可能造成用戶修改的代碼無法正確的解析為對象模型,造成.py源代碼加載繪制設計器中的界面的時候發生異常,解決這個問題的唯一一個辦法就是建議開發人員盡量采用常用的代碼來修改生成的界面代碼。
                由于HTML代碼本身就是一個樹狀模型,無需進行代碼和模型間的轉換,所以網頁設計器就不存在上邊說的這些幫派了。

           http://www.cownew.com CowNew開源團隊 
           楊中科 email:about521愛特163.com
          posted @ 2007-04-08 17:24 CowNew開源團隊 閱讀(3256) | 評論 (4)編輯 收藏

          ???IWorkbenchPage page = Activator.getActivePage();

          ???IAdapterManager mgr = Platform.getAdapterManager();
          ???String propPageName = IPropertySheetPage.class.getName();
          ???PropertySheetPage proppage = (PropertySheetPage) mgr.loadAdapter(
          ?????page, propPageName);
          ???if (proppage != null)
          ???{
          ???????proppage.refresh();
          ???}

          posted @ 2007-03-28 15:22 CowNew開源團隊 閱讀(712) | 評論 (0)編輯 收藏

          ??????可能大家常常會有這樣的疑問,為什么我發的帖子始終沒有人回復呢?這種感覺一定很讓人沮喪吧?實際上,并不是沒有人愿意伸出自己的手,但是這前提是,你要掌握提問的智慧,必須激起看你帖人的回答欲望。

          ??????在提出技術問題前,檢查你有沒有做到:
          1. 通讀幫助文檔,試著自己找答案。
          2. 通過搜索引擎搜索。
          3. 自己嘗試進行問題解決。

          ??????當你提出問題的時候,首先要說明在此之前你干了些什么,這將有助于樹立你 的形象:你不是一個妄圖不勞而獲的乞討者,不愿浪費別人的時間。一個問題如果提的好那么即使大家不知道怎么解決,也會幫你出謀劃策,和你一起解決;如果一個問題提得不好,那么即使再簡單,也沒人搭理你!

          ??????周全的思考,準備好你的問題,草率的發問只能得到草率的回答,或者根本得 不到任何答案。越表現出在尋求幫助前為解決問題付出的努力,你越能得到實 質性的幫助。

          ??????決不要自以為夠資格得到答案,你沒這種資格。畢竟你沒有為這種服務支付任何報酬。你要自己去“掙”回一個答案,靠提出一個有內涵的,有趣的,有思維激勵作用的問題--一個對社區的經驗有潛在貢獻的問題,而不僅僅是被動的從他人處索要知識--去掙到這個答案。

          ??????這是對自己的尊重,也是對別人的尊重。

          posted @ 2007-03-28 10:59 CowNew開源團隊 閱讀(260) | 評論 (0)編輯 收藏

          僅列出標題
          共30頁: First 上一頁 12 13 14 15 16 17 18 19 20 下一頁 Last 
          主站蜘蛛池模板: 九龙坡区| 新田县| 松阳县| 永修县| 榆林市| 汨罗市| 怀来县| 武宣县| 明星| 辉南县| 昭通市| 鹰潭市| 奉节县| 弥勒县| 神木县| 同仁县| 中西区| 阜阳市| 仙居县| 武宣县| 垫江县| 郯城县| 长治市| 石嘴山市| 女性| 德钦县| 咸丰县| 柳河县| 鄯善县| 社旗县| 怀仁县| 神农架林区| 漯河市| 孙吴县| 曲麻莱县| 普兰店市| 藁城市| 綦江县| 尉犁县| 张家口市| 淳安县|