2008年10月20日

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

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

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

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

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

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

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

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

          Element element = new Element("name");

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

          element.addContent(text);

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

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


          一下示例代碼實(shí)現(xiàn)一個(gè)常用的讀取配置文件并且允許更改后同步到磁盤的操作:
          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;

          /**
           * 讀取配置文件,并且修改后及時(shí)同步到磁盤
           @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é)點(diǎn)下查找葉子元素
              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 徐辛波 閱讀(797) | 評(píng)論 (0)編輯 收藏

          關(guān)于線程間的交互和共享數(shù)據(jù)通常有輪詢和通知機(jī)制。一下舉例說(shuō)明:Thread1和Thread2共享一塊數(shù)據(jù)ShareData,Thread1使用數(shù)據(jù),Thread2更新數(shù)據(jù)。當(dāng)Thread1使用數(shù)據(jù)時(shí)發(fā)現(xiàn)數(shù)據(jù)沒(méi)有更新就可以先休眠(sleep())一段時(shí)間然后再去判斷是否更新,如此反復(fù)直到數(shù)據(jù)可用,這就是所述的輪詢機(jī)制??梢钥闯鲚喸儥C(jī)制需要不斷的輪詢數(shù)據(jù)狀態(tài),很耗費(fèi)資源;當(dāng)采用通知機(jī)制時(shí)過(guò)程是這樣的,Thread1發(fā)現(xiàn)數(shù)據(jù)不可用就在ShareData上等待(ShareData.wait()),當(dāng)Thread2更新數(shù)據(jù)后就通知所有在ShareData上等待的線程(ShareData.notifyAll()),這樣Thread1受到通知繼續(xù)運(yùn)行。

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

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

          以下為一個(gè)簡(jiǎn)單的示例:

          package  sinpo.usagedemo;

          /**
            * 該例子說(shuō)明線程休眠與等待以及注意事項(xiàng)。
           
            @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時(shí)并未釋放console別的線程是不能鎖定console的
                           //TODO do things
                       catch  ( InterruptedException e ) {
                           e.printStackTrace () ;
                       }
                   }
               }
              
               private  void  writeToConsole2 () {
                   synchronized ( console ){
                       try  {
                           console.wait ( 1 * 1000 ) ; //NOTE:wait時(shí)別的線程是可以鎖定console的
                           //TODO do things
                       catch  ( InterruptedException e ) {
                           e.printStackTrace () ;
                       }
                   }
               }
          }
          //控制臺(tái)類
          class  Console  {
               //TODO implements me
          }
          posted @ 2008-10-22 23:26 徐辛波 閱讀(1273) | 評(píng)論 (0)編輯 收藏
          麻煩有誰(shuí)知道的告知一下。。。
          今天終于找到一個(gè)不錯(cuò)的工具,能將用eclipse等編輯的java代碼做成一樣風(fēng)格的html格式,試用了挺好用的,謝謝Java2Html團(tuán)隊(duì)。
          posted @ 2008-10-20 22:42 徐辛波 閱讀(376) | 評(píng)論 (2)編輯 收藏

          這幾天需要實(shí)現(xiàn)一個(gè)底層基于UDP的協(xié)議,該協(xié)議底層使用UDP傳輸?shù)蔷哂袚砣刂啤⒊瑫r(shí)重發(fā)、數(shù)據(jù)確認(rèn)等功能又比TCP簡(jiǎn)單 (RUDP,Reliable UDP)。在實(shí)現(xiàn)協(xié)議底層的UDP服務(wù)時(shí)準(zhǔn)備使用Java的NIO,在網(wǎng)上查資料都是以TCP為例講的,于是自己研究了一下基于UDP的NIO。

          NIO的思路是基于多路選擇的,即由原來(lái)的每個(gè)連接都由一個(gè)線程來(lái)等待消息,改為每個(gè)連接都在選擇器上注冊(cè),由選擇器來(lái)等待。當(dāng)然NIO引入了很多新的概念,如Channel,Buffer、Charset、Selector等,使得編程更簡(jiǎn)潔、更面向?qū)ο蠡?/font>

          下面貼出用NIO API改造成UDP示例代碼,注意其中使用Charset來(lái)編碼解碼的過(guò)程(當(dāng)然Charset還支持很多其他編碼不僅局限于默認(rèn)編碼)以及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 () ;

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

          String echo = "This is the reply message from 服務(wù)器。" ;
          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 將報(bào)文轉(zhuǎn)化為RUDP消息并調(diào)用RUDP協(xié)議處理器來(lái)處理
                                      
                                       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 徐辛波 閱讀(9339) | 評(píng)論 (11)編輯 收藏
               摘要:   閱讀全文
          posted @ 2008-10-20 21:48 徐辛波 閱讀(281) | 評(píng)論 (0)編輯 收藏

          導(dǎo)航

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

          統(tǒng)計(jì)

          • 隨筆 - 9
          • 文章 - 0
          • 評(píng)論 - 14
          • 引用 - 0

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          最新隨筆

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 津南区| 赞皇县| 恩平市| 洛川县| 凌源市| 濉溪县| 甘洛县| 吉安县| 武隆县| 陇西县| 宁城县| 澄迈县| 西和县| 昭觉县| 百色市| 肃宁县| 环江| 循化| 如皋市| 聊城市| 宜城市| 太谷县| 东兰县| 湖州市| 皋兰县| 汤原县| 天柱县| 连南| 武城县| 乌海市| 邵阳市| 汉沽区| 原阳县| 普宁市| 崇州市| 大新县| 中山市| 修文县| 青川县| 新津县| 浠水县|