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

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

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

    2008年10月19日

    不止一次我們的項目在靠開發(fā)人員硬扛著,bug來不及修改,文檔還在猛補,項目經理又在催著出版本,每修改一個bug都要在代碼的泥沼中摸爬滾打半天,卻又在制造著另外的bug,為了文檔而制造著根本沒有參考價值的文檔。每發(fā)布一個版本都要瞻前顧后并且總要無休止的加班,似乎開發(fā)人員永遠有干不完的活。

    我們不禁問自己,為什么會有那么多失敗的軟件,軟件開發(fā)我們到底還需要什么? 

    工具?我們不乏偉大的工具,IDE我們有eclipse、intelliJ、VC++等等,建模我們有Rose、together、Visio等等,配置管理我們Subversion、CVS、ClearCase等等,bug管理我們有ClearQuest、bugzilla等等,文檔我們word、wps等,還有集成、測試甚至生成代碼等等我們都有偉大的工具,工具我們不缺。流程?瀑布模型、迭代模型、UP、XP我們不缺流程管理的理論。知識?軟件開發(fā)算是一項知識型的工作,我們的開發(fā)人員一般都是本科碩士畢業(yè),況且搜索引擎如此發(fā)達的今天,知識根本不是問題,況且我們的軟件開發(fā)需要多么高深的知識嗎?

     優(yōu)秀的理論支持、有能力的人員、先進的工具,這些我們都具備,我們所缺的只是一種軟件開發(fā)的理念,缺少軟件開發(fā)的情商。我們一開始學習編程知識接觸到的就是C語言和數據結構,慢慢的一些結構化的思想就扎根于大腦,其實在商業(yè)軟件中數據結構、算法很少涉及,我們所需要的僅僅是一種設計、開發(fā)的理念。比如用面向對象本來是一種簡單的思想,目的是為了降低軟件的復雜性而出現的,可是讓熟悉了結構化編程的人去搞反而覺得很難。一些好的實踐經驗我們也經常提到,比如模塊化、松散耦合、面向接口編程、類應只關注本職工作等等開發(fā)設計理念以及規(guī)范命名、詳盡使用的注釋、清晰的結構等代碼規(guī)范以及每日構建、有效溝通、配置管理、bug管理等一些管理理念,這些做起來都非常容易,關鍵是懶惰是人的本性,不知不覺中我們就會犯著大家都在重復的錯誤。如果在項目開工之初就充分貫徹這些優(yōu)秀的理念,在項目進行中無論時間多緊都持之以恒,并且項目進行中不斷的反思代碼中的壞味道,一經發(fā)現立即重構,相信我們的開發(fā)過程會進入一個良性的循環(huán)中去,我們的開發(fā)人員將會體會到什么是快樂開發(fā)。

    posted @ 2008-11-17 23:09 徐辛波 閱讀(352) | 評論 (0)編輯 收藏
    徐辛波,西安,從事軟件開發(fā)設計工作,熟悉Java語言,愛好開發(fā)工作,特別是java相關的編程,業(yè)余關注開源項目,誠心結識志同道合之士組建開源團隊,共同學習、進步、協作、為中國開源事業(yè)貢獻微薄之力。
    徐辛波 sinpo.xu@gmail.com
    posted @ 2008-10-31 21:00 徐辛波 閱讀(359) | 評論 (1)編輯 收藏

    JDOM因其簡潔易用易懂的API而被廣泛的使用。JDOM常用的核心類及它們間的關系如下圖所示:

    Document代表了文檔對象,抽象類Content表示文檔中的內容元素,各種內容組成了文檔對象。常用的內容元素有xml元素Element、xml注釋Comment、文本Text。下面以如下片段來說明各類的含義。

    <?xml version="1.0" encoding="UTF-8"?>

    <customers>

    <customer>

    <name>徐辛波</name>

    <occupation>developer</occupation>

    <!-- comment:following is contact info -->

    <contact>

    <email>sinpo.xu@hotmail.com</email>

    <mobile>15029357227</mobile>

    <fix-phone>02985457683</fix-phone>

    </contact>

    </customer>

    </customers>

    上述文檔用Document來抽象;customers為文檔的根元素(root element ),Element即一個封閉起來的元素,element元素可以有子元素,如<mobile>15029357227</mobile>是一個元素,而<contact>...</contact>也是一個元素,甚至<customers>...</customers>也是一個大元素;<!-- ... -->代表了xml中注釋,注釋在JDOM中用Comment類來抽象;Text代表了xml中的文本值,如元素屬性的值、元素的值、注釋的內容等,父元素的Text為子元素和值組成的串,使用Text類可以方便的表示一些特殊字符,如:

    Element element = new Element("name");

    Text text = new Text("AAA.<、BBB/>.<CCC>");

    element.addContent(text);

    值得一提的是Element的方法addContent(Content content),因參數是抽象父類Content,所以可以添加Text、Element和Comment等,如果添加的是Text則自動作為element的文本值,如果是Element則作為element的子元素,如果是Comment則作為element的注釋,使用十分方便。元素的值如<name>徐辛波</name>中的“徐辛波”也是一個和元素平行的Content對象(Text對象),當使用Element的getDescendants()方法時將返回一個該元素所有后代的迭代器,這些后代包括Element、Comment、Text等,如元素<contact>的后代包括email、mobile、fix-phone三個元素以及這三個元素的Text共6個后代,如果計算后代時有父子嵌套則應注意,父元素作為一個后代,其嵌套的子元素作為另一個后代。

    剛才提到核心類都包含在org.jdom包下,jdom還包含了org.jdom.input和org.jdom.output兩個包分別來處理xml內容的輸入輸出。當要讀取xml資源時我們通常使用input包下的SAXBuilder類從輸入流構建dom對象,當資源加載后常用的做法是在內存中緩存,這樣后續(xù)的查找修改等操作就非常快。文檔加載后內存的中各個元素是記錄有各自的位置和關系的,即保持有上下文環(huán)境的。如果想要刪除一段內容(Element Comment Text),只用調用該內容的detach方法即可,這樣元素即和文檔脫離關系了,再對文檔進行遍歷或者持久化到磁盤上時游離的元素就不可見了。Jdom的輸出類包括XMLOutputter、DOMOutputter、SAXOutputter。最常用的是XMLOutputter,通過它可以將dom對象輸出到指定的輸出流,并且可以指定所輸出xml文件的格式,比如縮進的樣式等。DOMOutputter輸出org.w3c.dom.Document對象,用于JDOM對象同w3c dom對象轉換,SAXOutputter可以注冊回調函數來處理相應的sax事件。


    一下示例代碼實現一個常用的讀取配置文件并且允許更改后同步到磁盤的操作:
    package sinpo.usagedemo;

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URL;
    import java.util.List;

    import org.jdom.Document;
    import org.jdom.Element;
    import org.jdom.input.SAXBuilder;
    import org.jdom.output.Format;
    import org.jdom.output.XMLOutputter;

    /**
     * 讀取配置文件,并且修改后及時同步到磁盤
     @author 徐辛波(sinpo.xu@hotmail.com) 
     * Oct 23, 2008
     */
    public class Configuration {

        private Element root = null;

        private Document dom = null;

        private static final String resourceName = "/config.xml";

        private static Configuration _INSTANCE = null;

        public static synchronized Configuration getInstance() {
            if (_INSTANCE == null) {
                _INSTANCE = new Configuration();
            }

            return _INSTANCE;
        }

        private Configuration() {
            load();
        }

        public String getConfig(String configName) {
            String configValue = null;
            Element found = findRecursively(configName, root);
            if (found != null) {
                configValue = found.getText();
            }
            return configValue;
        }

        public void updateConfig(String configName, String newValue)
                throws IOException {
            Element found = findRecursively(configName, root);
            if (found != null) {
                found.setText(newValue);
            else {
                Element configNode = new Element(configName);
                configNode.addContent(newValue);
                // also: configNode.setText(newValue);
                root.addContent(configNode);
            }
            sync();
        }

        public void deleteConfig(String configNamethrows IOException {
            Element found = findRecursively(configName, root);
            if (found != null) {
                found.detach();
            }
            sync();
        }
        
        private void load() {
            SAXBuilder builder = new SAXBuilder();
            InputStream source = getClass().getResourceAsStream(resourceName);
            try {
                dom = builder.build(source);
                root = dom.getRootElement();
            catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 遞歸查找. 在指定的父節(jié)點下查找葉子元素
        private Element findRecursively(String name, Element parent) {
            Element found = null;
            List<Element> children = parent.getChildren();
            if (children != null) {
                for (int i = 0; i < children.size(); i++) {
                    Element element = children.get(i);
                    String tmpName = element.getName();
                    if ((name.equals(tmpName)) && (!hasChild(element))) {
                        return element;
                    }
                }

                for (int i = 0; i < children.size(); i++) {
                    Element element = children.get(i);
                    if (hasChild(element)) {
                        found = findRecursively(name, element);
                        if (found != null) {
                            return found;
                        }
                    }
                }
            }

            return found;
        }

        private boolean hasChild(Element element) {
            boolean hasChild = false;
            List children = element.getChildren();
            if ((children != null&& (children.size() 0)) {
                hasChild = true;
            }

            return hasChild;
        }

        private void sync() throws IOException {
            Format format = Format.getPrettyFormat();
            XMLOutputter outputter = new XMLOutputter(format);
            File file = null;
            URL url = getClass().getResource(resourceName);
            if (url == null) {
                file = new File(resourceName);
            else {
                file = new File(url.getPath());

                OutputStream out = null;
                try {
                    out = new FileOutputStream(file);
                    outputter.output(dom, out);
                    out.close();
                    out = null;
                catch (Exception e) {
                    e.printStackTrace();
                    if (out != null) {
                        out.close();
                    }
                }
            }
        }
    }
    posted @ 2008-10-25 21:02 徐辛波 閱讀(794) | 評論 (0)編輯 收藏

    關于線程間的交互和共享數據通常有輪詢和通知機制。一下舉例說明:Thread1和Thread2共享一塊數據ShareData,Thread1使用數據,Thread2更新數據。當Thread1使用數據時發(fā)現數據沒有更新就可以先休眠(sleep())一段時間然后再去判斷是否更新,如此反復直到數據可用,這就是所述的輪詢機制。可以看出輪詢機制需要不斷的輪詢數據狀態(tài),很耗費資源;當采用通知機制時過程是這樣的,Thread1發(fā)現數據不可用就在ShareData上等待(ShareData.wait()),當Thread2更新數據后就通知所有在ShareData上等待的線程(ShareData.notifyAll()),這樣Thread1受到通知繼續(xù)運行。

    關于等待和休眠還有另一個區(qū)別就是當線程等待時,該線程鎖定的資源是釋放掉的,這時其它線程是可以鎖定這些資源的,當線程被喚醒或者等待時限到時線程重新獲取資源才能繼續(xù)運行;而當線程休眠時線程鎖定的資源是不被釋放的。

    還有一點就是要在對象lock上等待時是必須先要獲取lock的對象鎖才能進行的,即必須要類似下面的邏輯 synchronized(lock){ lock.wait()}

    以下為一個簡單的示例:

    package  sinpo.usagedemo;

    /**
      * 該例子說明線程休眠與等待以及注意事項。
     
      @author  徐辛波(sinpo.xu@hotmail.com) 
      * Oct 22, 2008
      */
    public class  PendingThreadDemo  {
         public  Console console =  new  Console () ;
         private  void  writeToConsole1 () {
             synchronized ( console ){
                 try  {
                     Thread.sleep ( 1000 ) ; //NOTE:sleep時并未釋放console別的線程是不能鎖定console的
                     //TODO do things
                 catch  ( InterruptedException e ) {
                     e.printStackTrace () ;
                 }
             }
         }
        
         private  void  writeToConsole2 () {
             synchronized ( console ){
                 try  {
                     console.wait ( 1 * 1000 ) ; //NOTE:wait時別的線程是可以鎖定console的
                     //TODO do things
                 catch  ( InterruptedException e ) {
                     e.printStackTrace () ;
                 }
             }
         }
    }
    //控制臺類
    class  Console  {
         //TODO implements me
    }
    posted @ 2008-10-22 23:26 徐辛波 閱讀(1271) | 評論 (0)編輯 收藏
    麻煩有誰知道的告知一下。。。
    今天終于找到一個不錯的工具,能將用eclipse等編輯的java代碼做成一樣風格的html格式,試用了挺好用的,謝謝Java2Html團隊。
    posted @ 2008-10-20 22:42 徐辛波 閱讀(372) | 評論 (2)編輯 收藏

    這幾天需要實現一個底層基于UDP的協議,該協議底層使用UDP傳輸但是具有擁塞控制、超時重發(fā)、數據確認等功能又比TCP簡單 (RUDP,Reliable UDP)。在實現協議底層的UDP服務時準備使用Java的NIO,在網上查資料都是以TCP為例講的,于是自己研究了一下基于UDP的NIO。

    NIO的思路是基于多路選擇的,即由原來的每個連接都由一個線程來等待消息,改為每個連接都在選擇器上注冊,由選擇器來等待。當然NIO引入了很多新的概念,如Channel,Buffer、Charset、Selector等,使得編程更簡潔、更面向對象化。

    下面貼出用NIO API改造成UDP示例代碼,注意其中使用Charset來編碼解碼的過程(當然Charset還支持很多其他編碼不僅局限于默認編碼)以及Buffer的使用。

    package sinpo.usagedemo;

    import java.net.DatagramSocket;
    import java.net.InetSocketAddress;
    import java.net.SocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.nio.channels.DatagramChannel;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    import java.util.Set;

    /**
    * @author 徐辛波(sinpo.xu@hotmail.com) Oct 19, 2008
    */
    public class UDPServer extends Thread {
    public void run () {
    Selector selector = null ;
    try {
    DatagramChannel channel = DatagramChannel.open () ;
    DatagramSocket socket = channel.socket () ;
    channel.configureBlocking ( false ) ;
    socket.bind ( new InetSocketAddress ( 5057 )) ;

    selector = Selector.open () ;
    channel.register ( selector, SelectionKey.OP_READ ) ;
    } catch ( Exception e ) {
    e.printStackTrace () ;
    }

    ByteBuffer byteBuffer = ByteBuffer.allocate ( 65536 ) ;
    while ( true ) {
    try {
    int eventsCount = selector.select () ;
    if ( eventsCount > 0 ) {
    Set selectedKeys = selector.selectedKeys () ;
    Iterator iterator = selectedKeys.iterator () ;
    while ( iterator.hasNext ()) {
    SelectionKey sk = ( SelectionKey ) iterator.next () ;
    iterator.remove () ;
    if ( sk.isReadable ()) {
    DatagramChannel datagramChannel = ( DatagramChannel ) sk
    .channel () ;
    SocketAddress sa = datagramChannel
    .receive ( byteBuffer ) ;
    byteBuffer.flip () ;

    // 測試:通過將收到的ByteBuffer首先通過缺省的編碼解碼成CharBuffer 再輸出
    CharBuffer charBuffer = Charset.defaultCharset ()
    .decode ( byteBuffer ) ;
    System.out.println ( "receive message:"
    + charBuffer.toString ()) ;
    byteBuffer.clear () ;

    String echo = "This is the reply message from 服務器。" ;
    ByteBuffer buffer = Charset.defaultCharset ()
    .encode ( echo ) ;
    datagramChannel.write ( buffer ) ;
    }
    }
    }
    } catch ( Exception e ) {
    e.printStackTrace () ;
    }
    }

    }

    public static void main ( String [] args ) {
    new UDPServer () .start () ;
    }
    }
    Client
    package  sinpo.usagedemo;

    import  java.net.InetSocketAddress;
    import  java.net.SocketAddress;
    import  java.nio.ByteBuffer;
    import  java.nio.channels.DatagramChannel;
    import  java.nio.channels.SelectionKey;
    import  java.nio.channels.Selector;
    import  java.nio.charset.Charset;
    import  java.util.Iterator;
    import  java.util.Set;

    /**
      @author  徐辛波(sinpo.xu@hotmail.com)
      * Oct 19, 2008
      */
    public class  UDPClient  extends  Thread  {
         public  void  run () {
             DatagramChannel channel =  null ;
             Selector selector =  null ;
             try  {
                 channel = DatagramChannel.open () ;
                 channel.configureBlocking ( false ) ;
                 SocketAddress sa =  new  InetSocketAddress ( "localhost" 5057 ) ;
                 channel.connect ( sa ) ;
             catch  ( Exception e ) {
                 e.printStackTrace () ;
             }

             try  {
                 selector = Selector.open () ;
                 channel.register ( selector, SelectionKey.OP_READ ) ;
                 channel.write ( Charset.defaultCharset () .encode ( "Tell me your time" )) ;
             catch  ( Exception e ) {
                 e.printStackTrace () ;
             }
            
             ByteBuffer byteBuffer = ByteBuffer.allocate ( 100 ) ;
             while  ( true ) {
                 try  {
                     int  eventsCount = selector.select () ;
                     if  ( eventsCount >  0 ) {
                         Set selectedKeys = selector.selectedKeys () ;
                         Iterator iterator = selectedKeys.iterator () ;
                         while  ( iterator.hasNext ()) {
                             SelectionKey sk =  ( SelectionKey iterator.next () ;
                             iterator.remove () ;
                             if  ( sk.isReadable ()) {
                                 DatagramChannel datagramChannel =  ( DatagramChannel sk
                                         .channel () ;
                                 datagramChannel.read ( byteBuffer ) ;
                                 byteBuffer.flip () ;
                                
                                 //TODO 將報文轉化為RUDP消息并調用RUDP協議處理器來處理
                                
                                 System.out.println ( Charset.defaultCharset () .decode (
                                         byteBuffer ) .toString ()) ;
                                 byteBuffer.clear () ;
                                 datagramChannel.write ( Charset.defaultCharset ()
                                         .encode ( "Tell me your time" )) ;
                             }
                         }
                     }
                 catch  ( Exception e ) {
                     e.printStackTrace () ;
                 }
             }

         }
    }
    posted @ 2008-10-20 22:38 徐辛波 閱讀(9336) | 評論 (11)編輯 收藏
         摘要:   閱讀全文
    posted @ 2008-10-20 21:48 徐辛波 閱讀(279) | 評論 (0)編輯 收藏
    關于讀取資源文件(如文本文件、圖像、二進制文件等),一般不推薦直接給出操作系統(tǒng)的路徑,而是給出相對于當前類的相對路徑,這樣就可以使用類的裝載器來裝載資源文件。常用的方法有:
    Class類的getResourceAsStream(String resourcePath);
    ClassLoader類的getResourceAsStream(String resourcePath)
    Class類的該方法最終還是委派給ClassLoader的getResourceAsStream方法,但是使用中發(fā)現Class#getResourceAsStream()使用的是絕對路徑(以/開頭),而ClassLoader#getResourceAsStream()使用的相對路徑。
    propterty文件經常放在類路徑的根路徑下(最頂層包的上層目錄,如classes),這樣加載property文件時就可以先用Class#getResourceAsStream方法獲取輸入源,再從該輸入源load各entry。
    code piece:
    package sinpo.usagedemo;

    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Properties;

    import junit.framework.TestCase;

    /**
     @author 徐辛波(sinpo.xu@hotmail.com)
     * Oct 19, 2008
     */
    public class LoadResource extends TestCase {
        public void test() throws Exception {
            //usage 1: use absolute path (mostly used)
            InputStream in1 = this.getClass().getResourceAsStream("/sinpo/test2.properties");
            //usage 2: use relative path
            InputStream in2 = this.getClass().getClassLoader().getResourceAsStream("sinpo/test2.properties");
            //usage 3: use system class path
            InputStream in3 = ClassLoader.getSystemResourceAsStream("system.properties");
            
            //將讀取的資源作為Properties的輸入源
            Properties props = new Properties();
            props.load(in1);
            String propValue = props.getProperty("propKey");
            System.out.println(propValue);
            
            //將讀取的資源作為文本輸出
            InputStreamReader reader = new InputStreamReader(in1);
            BufferedReader bReader = new BufferedReader(reader);
            String content = bReader.readLine();
            //輸出第一行內容
            System.out.println(content);
            
            //TODO close them
        }
    }
    posted @ 2008-10-19 20:36 徐辛波 閱讀(1270) | 評論 (0)編輯 收藏

    導航

    <2008年10月>
    2829301234
    567891011
    12131415161718
    19202122232425
    2627282930311
    2345678

    統(tǒng)計

    常用鏈接

    留言簿(1)

    隨筆分類

    隨筆檔案

    最新隨筆

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 一区在线免费观看| 亚洲国产人成精品| 18禁在线无遮挡免费观看网站| 亚洲人成7777| 亚洲欧洲日韩国产综合在线二区| 国产免费一区二区三区VR| 无人在线直播免费观看| 久久国产免费直播| 男女污污污超污视频免费在线看| 99久久婷婷国产综合亚洲| 国产亚洲av片在线观看播放| 精品国产免费观看| 国产片AV片永久免费观看| 99视频在线免费看| 免费人成毛片动漫在线播放 | 免费黄色一级毛片| 亚欧人成精品免费观看| 99久久久国产精品免费牛牛 | 超pen个人视频国产免费观看| 四虎成年永久免费网站| 日韩免费在线观看视频| 最新亚洲成av人免费看| 国产成人无码免费看片软件| 日韩免费码中文在线观看| 亚洲AV成人片无码网站| 亚洲精品久久无码| 亚洲av无码专区在线观看亚| 成人区精品一区二区不卡亚洲| 亚洲人成高清在线播放| 亚洲电影在线播放| 亚洲国产成人精品青青草原| 亚洲天堂男人天堂| 亚洲一区二区三区高清| 久久精品国产亚洲精品2020| 91亚洲精品视频| 亚洲高清在线mv| 亚洲一区二区三区深夜天堂| 亚洲剧场午夜在线观看| 亚洲国产精品综合久久20| 亚洲综合小说另类图片动图| 亚洲av无一区二区三区|