Russel 的个人资料-= 訓練登陸火星 =-照片日志列表 工具 帮助

日志


12月8日

[DP]給自己找麻煩的實錄

 軟體開發到一定程度之後,就會發現原本的寫法總有些很怪的瑕疵,之所以稱為「瑕疵」,原因不外乎「程式可以跑,但是可讀性很差」,最後程式就會黏著在很奇怪的地方,最後的結果就是程式更動變得困難不易維護,臭蟲發生的機率也大大的提高。
 
 最近在寫一支程式,其實是從別人那裡接手的...看得出來這個程式開發得很快,不過也拜他所賜,也讓我玩了一些東西。
 第一個階段的程式大概是這樣的感覺:Step1
  大概描述一下好了:這兩個物件,另外再去呼叫了Web Service來取得資料,因此基本上這兩個物件裡的兩個Operation做的事情是一樣的,但是分佈在不同的物件裡。試想一種情況:如果這兩個Operation回傳的值不同了,或者名稱改變了,參數調整了,甚至消失了,那維護人員第一件事就是打開整個專案,並且搜尋整個專案,把原本的Method改成新的名稱、參數,甚至重新處理。
 這不是個很建康的方法,其實整個方法描述下來有很多地方可以改善。因此當我接手這個系統的維護時,我第一件事情,就是檢討這個Model是不是有值得修改的地方。
 
 因此我改了另一個方式來處理,這個方式,會用到幾個不錯的觀念:
Step2
  我把所有的WebService Method用另一個物件包了起來,並且叫做Web Service Adapter,所有的物件如果要去呼叫Web Service,一律透過這個Adapter去呼叫。
 這個方法的優點是,把分散的各地的Web Service Method收歛在自己可以控制的範圍內(就是那個Adapter物件),但仍有一個問題:Adapter物件也可能會產生變數調整、物件改名等問題。
 這個問題很難解決,但先聖先賢們發展一個理論,叫OCP(Open-Close Principle),大概的基礎就是保持物件內的彈性,並且不會因為修改Method產生困擾。保持彈性的方法有很多,基本上大原則就是不要修改方法,而以多型或者覆載的方式來處理,如此一來就能保持物件之間的彈性,維護時也只要針對物件去維護,乾淨又整齊。
 把物件抽出來之後,更好的優點就是:如果Web Service回傳的值是需要另外處理的,也可以在Adapter物件裡統一處理,這個優點真是令我感動,這樣一來,去使用這些Method的物件可以更專注在自身所需要的資料,而不用再另外去分心這些資料是不是物件所需的資料。
 
 再來就是程式內部的處理了,在第二階段的處理上,我加上了一個偷吃步的方法:Try-Catch。這個方法讓我碰了一個釘子,因為Try-Catch雖然可以讓程式更乾淨,因為我只要是錯誤的訊息,可以一律拋出一個Exception,那呼叫的物件只要用Catch包上之後,就可以統一來處理不正確的訊息,但是產生了兩個問題:
 一、Try-Catch會降低整體的效能,我看過一個很傳神的說法,Try-Catch就彷彿是奔馳的高鐵上那支緊急暫停拉把,加上去之後就像是拉下緊急暫停拉把...
 二、有可能會低估了原本丟出來的Exception的緊要程度,當然啦!如果是針對每一個Exception來做處理,那又另當別論了。
 (很可惜我沒有留下這個階段的程式,其實我很愛這個階段的程式碼,乾淨到只要專注正常的流程,只是效能的關係不得不放棄,這讓我惆悵了好一陣子。)
 
 第三階段的修改,就是把Try-Catch全部拿掉,雖然說回到第一階段,但是在Adapter物件上,我補上implement IDisposable
 這個動件使得我每次去呼叫Web Service可以有一個統一去Dispose Object & Reference的地方,這個東西在增進效能上無關痛癢,但是在資源的使用上幫助就很大。以.Net Framework來說,去Dispose Web Service算是幫大家一個大忙(請原諒有點無能的http.sys以及IIS
 
 當程式寫完交出去,我大概花了將近一個月的時間,感覺是很長久,實際上後半段(約三個星期)都是進行這部份的調效。會有這種想法其實跟大學時留下的習慣有關:我習慣把程式寫得很短,用各種方式來處理程式,並比較其中的優缺點,唸研究所之後,買了一本「聖經」,叫「物件導向設計模式」,這本書根本就是把我打得暈頭轉向的一本書,但是隨著工作經驗的不斷累積,書裡的程式竟然慢慢的開始對我微笑...
 所以我把書放在公司裡,免得嚇到我了...
 
 下次會介紹兩個簡單的Design pattern,State以及Strategy,我覺得Pattern雖然有很多,這兩個卻是最容易進入Pattern邪惡的殿堂...科科科。
10月7日

[C#]處理一連串if/elseif的理想做法

 最近在整理一支程式,發現有一段這種程式:

   1: if(i==1)
   2:  .....
   3: else if(i == 2)
   4:  ....
   5: else if (i == 3)
   6:  ....
   7: else if (i == 4)
   8:  ....
   9: else if (i == 5)
  10:  ....
  11:  ....
  12:  ....
  13: else if (i == 200000)
  14:  ....
  15: else
  16:  Console.WriteLine("This loop works die almost!!");
 
 也許這個數字有點誇張,但是比較常見的是四到五個,甚至接近十個,放進資料庫裡覺得太少,用If/elseif來做又很長的的程式碼。
 也許有人會換成用Switch case來做,效能好一點,但是還是一長串,Like this:
   1: switch (strErrorCode)
   2: {
   3:     case "10":
   4:         return "Ten";
   5:     case "11":
   6:         return "Eleven";
   7:     case "12":
   8:         return "Twelve";
   9:     case "14":
  10:         return "拾肆";
  11:     case "15":
  12:         return "拾伍";
  13:     case "16":
  14:         return "拾陸";
  15:     case "17":
  16:         return "拾柒";
  17:     case "62":
  18:         return "很多的Case";
  19:     case "31":
  20:         return "放進資料庫又太多";
  21:     case "52":
  22:         return "放在程式裡也太多";
  23:     case "60":
  24:         return "更重要的是";
  25:     case "70":
  26:         return "維護上也很麻煩";
  27:     default:
  28:         return "有時候還會忘記Default!";
  29: }
 處理這方面的問題,我們先從學理的角度來研究這個問題:資料量多大時,放進資料庫會比較適合。一般來說,資料庫在搜尋資料時,並不是透過小學生的循序搜尋法來找資料(即上例Case1),而是透過其他的演算法來處理:也許是二分,也許是內插,但無論如何,搜尋的成本都沒有想像中的高,因此,把if/else或者switch/case的資料放進資料庫,增加的成本要怎麼估算?就是這些資料究竟有沒有由DBMS管理的需求?像上面的Case1以及Case2,其實都沒有進資料庫管理的必要。如果資料值得透過DBMS來管理小型資料的話,我們先假設資料庫是利用比較簡易的二分搜尋法來找資料,假設我們的if/else已經大於8筆(2的3次方),其實是可以進資料庫做管理,因為在Worse case下,在三到四次以內就能找到所需的資料(log(base 2)8 = 3 log(base 2) 16 = 4),但如果透過if/else來做處理,Worse case的成本是8,算起來是相當值得的。
 但是,如果我們不考慮使用資料庫,但也不想用if/else的處理方法?我有個建議,就是抽出來用Collection。
 做法其實很簡單,就是將那一個很大的判斷式,用另一個物件來處理,該物件裡再用Collection來管理資料,最後再利用method來取得資料。
 原本if/else的部份先改成:
   1: protected string errorMessageHandle(string strErrorCode)
   2: {
   3:     errorMessageHandler errorHandler = new errorMessageHandler();
   4:     return errorHandler.getErrorMessage(strErrorCode);
   5: }

 然後再撰寫errorMessageHandler的部份如下:

   1: public class errorMessageHandler
   2: {
   3:     private System.Collections.Hashtable hashTable;
   4:     public errorMessageHandler()
   5:     {
   6:         hashTable = new System.Collections.Hashtable();
   7:     }
   8:     public string getErrorMessage(string key)
   9:     {
  10:         string rtnVal = string.Empty;
  11:         createHashTable();
  12:         try
  13:         {
  14:             rtnVal =  hashTable[key].ToString();
  15:         }
  16:         finally
  17:         {
  18:             hashTable.Clear();
  19:         }
  20:         return rtnVal;
  21:     }
  22:     private void createHashTable()
  23:     {
  24:         hashTable.Clear();
  25:         hashTable.Add("10", "OXOXOXOXOXOXOXOX");
  26:         hashTable.Add("11", "OXOXOXOXOXOXOXOX!!");
  27:         hashTable.Add("12", "OXOXOXOXOXOXOXOX!!");
  28:         hashTable.Add("14", "OXOXOXOXOXOXOXOX!!");
  29:         hashTable.Add("15", "OXOXOXOXOXOXOXOX!!");
  30:         hashTable.Add("16", "OXOXOXOXOXOXOXOX!!");
  31:         hashTable.Add("17", "OXOXOXOXOXOXOXOX!!");
  32:         hashTable.Add("18", "OXOXOXOXOXOXOXOX!!");
  33:         hashTable.Add("19", "OXOXOXOXOXOXOXOX!!");
  34:         hashTable.Add("62", "OXOXOXOXOXOXOXOX!!");
  35:         hashTable.Add("31", "OXOXOXOXOXOXOXOX!!");
  36:         hashTable.Add("52", "OXOXOXOXOXOXOXOX!!");
  37:         hashTable.Add("60", "OXOXOXOXOXOXOXOX!!");
  38:         hashTable.Add("70", "OXOXOXOXOXOXOXOX!!");
  39:     }    
  40: }
 程式碼比if/else來處理是變大了很多,但是這程式的優點在於:在增加判斷式上的便利性及彈性增加了:其實createHashTable的部份可以可以改成讀檔來處理,如果訊息有修改時是可以不做到不用重新編譯程式。如果是使用Java來處理,可以利用相對應的Collection來處理(應該是用HashSet之類的)。
 何時使用呢?這個可能需要仔細評估一下:因為HashTable可能會占用很大部位的記憶體(尤其當HashTable相當巨大時),其實如果有恰當的記憶體管理或是Design pattern(例如用singleton來實作),對於記憶體的占用就會比較輕。總之呢,只要用到Collection,記憶體的管理就是要列入考量。
10月1日

[Visual Studio]從起始專案看VS的發展

 從Visual Studio的code initial就可以看出微軟對這個新版本的編輯器及核心有什麼大的延革。
 來看看進入.Net年代時的Visual Studio.Net有什麼特別的。
   1: using System;
   2:  
   3: namespace Test
   4: {
   5:     /// <summary>
   6:     /// Summary description for Class1.
   7:     /// </summary>
   8:     class Class1
   9:     {
  10:         /// <summary>
  11:         /// The main entry point for the application.
  12:         /// </summary>
  13:         [STAThread]
  14:         static void Main(string[] args)
  15:         {
  16:             //
  17:             // TODO: Add code to start application here
  18:             //
  19:         }
  20:     }
  21: }
 Yes,沒什麼特別的,using裡只有引入一個很普通的System而已,如果要用什麼東西,程式設計師必需一個一個把他using進來,這個版本的.Net其實最主要的變化在於翻天覆地的變化,也可以說是一個新的里程碑吧!
 進入Visual studio 2005之後呢?
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4:  
   5: namespace MyTest
   6: {
   7:     class Program
   8:     {
   9:         static void Main(string[] args)
  10:         {
  11:         }
  12:     }
  13: }
 從using段看得出來,這個版本最大的進展就是在泛型...其實也不知道是Java抄微軟,還是微軟抄Java,同時期的Java 5也推出了泛型,但是東西不太一樣,定義的物件類型也略有不同,但是這個東西在效能的表現上(尤其在平台有支援的情況下)表現極佳。
  比較令人在意的就是微軟直接把System.Text引入進來。在System.Text底下,其實還有一個東西在System.Text底下的一個namespace,就是RegularExpression,這個namespace也許對程式設計師來說是比較常用的,但在System.Text這個namespace底下究竟有什麼class呢?在2003底下,只有Encoder及Decoder,在2005下呢,也是有Encoder及Decoder,再加上一些Exception及Fallback的機制...我只能猜測這些東西也許平台在使用某些功能時,會使用到encoding及decoding,因此不得不將這個東西引入進來,但是應該是可以花點時間研究一下這個namespace底下究竟有什麼特別的東西及功能。
 進入Visual Studio 2008:
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace LinqTest
   7: {
   8:     class Program
   9:     {
  10:         static void Main(string[] args)
  11:         {
  12:         }
  13:     }
  14: }
 係地,又是Linq,微軟果然是拼了老命想把這個東西推行出來,從各項證據顯示,Linq將很有可能是未來兩三年的微軟新寵兒,我很想拿這個東西跟NHibernate比較看看效能上的差異,不過不提效能的差異,光是編輯器的支援,我想NHibernate就輸了一大截了吧!
 從這種小小的地方,就能看出微軟對自家產品的看法以及應該著焦的地方,如果是微軟的Programmer應該可以從這些地方看出未來應該努力的方向吧!
9月30日

[MS]不再自己玩自己的IE 8

 微軟用Internet Explorer 8.0告訴GoogleMozilla,要玩標準,它可以玩得比各家都還要凶,還要絕。
 微軟從有IE開始,就一直走著自己的路,就算真的有正式的規格,微軟仗著他財大業大,竟也可以視若無睹,但現在不知道是不是因為Google的Chrome以及Firefox已經威脅到IE的市占率,還是微軟真的開始苦開發者所苦,IE竟然反過來大量的支援W3C以及CSS的標準!這可嚇死像我這種吃過微軟苦頭的設計師:什麼?我不用再判斷使用者的瀏覽器,再去決定要跑什麼Java script了嗎?
 別的不提,光是Acid2的測試,就可以看出IE真的很盡心盡力的在和這個世界接軌,甚至可以發現,IE對現行的HTML 4的支援度,比Firefox或Chrome都還要高(微軟是宣稱80%左右),未來的HTML5草案,IE8(甚至IE7)都已經開始去支援了。
 
 以上三張圖由左至右分別為IE8、Firefox 3.0、Chrome,其實Chrome的Acid測試基本上是Pass的,但是應該是Pass*才對,因為當做瀏覽器的resize時,笑臉人的頭蓋骨就會飛飛飛飛飛飛起來了。
 除了支援性上的進步以外,在效能上也有長足的進步,在Javascript引擎的部份做了很大部份的修改:其實說IE對自家產品最佳化也不為過,微軟開始正式Web Application的發展性無可限量時,對自家的瀏覽器投入了極大心力在進行開發以及行銷,無論在安全性、效能、使用者界面及操作的友善性都比IE7提升不少,甚至對開發者來說,以往最缺乏的開發者工具都內建了,而且比其他競爭者的工具更好,更方便。
 但IE8卻有一個非常令人垢病,但卻又令人又愛又恨的功能:不提IE6,就針對IE7和Firefox來談,以前操作瀏覽器頁籤時,如果有某個頁籤Crash掉,則整個瀏覽器都會因此被迫重開,IE8提出了一個新的方法,叫做「Loosely-coupled Internet Explorer」...如果對Pattern有點了解,Loose coupled就是低度耦合,換句話說就算使用者開了兩萬個頁籤,其中一個頁籤掛了,也不會影響到其他一萬九千九百九十九個頁籤,很讚對不對,錯!每個頁籤都是同不同的Thread在操作,意思就是說,每個頁籤都各別使用一個執行緒的資源,我曾經試著去看資源管理員...Firefox開十個占用大概200MB的資源(我也不知道為什麼),而IE只開了三個,資源就已經快要破百。當然啦,我並沒有去深入研究IE8他在資源占用上和其他瀏覽器究竟有什麼不同,罷特,如果IE吃資源是用這種方法吃....我並不會對這功能感到高興。
 如果還有人對IE的印象是停留在IE6那個殘破,只是為了應付而做出來的瀏覽器的話,那可真的是大大的落伍了,事實上,我在操作IE7以及Firefox2或Firefox3上,其實得到的滿意度基本上已經都得到相當高的評價:喔拜託,還在說用IE中毒?不知道是誰手賤去Click不該點的連結,就算瀏覽器已經好心提醒了,還不是硬要點?IE8長足的進步,不但讓使用者有更好的使用經驗,以及與其他應用程式更接近的操作方式,對於更常使用瀏覽器的開發者,IE8提供了更強大的使用者工具(嗯,也許比Firebug更強大喔!),這種進步,除了刺激Mozilla以及Google提供更好更方便的軟體以外,其實得利的一方,還是使用者,如果Windows通知要升級到IE8,相信我,你有很多很多的理由告訴自己:「來裝個IE8吧!」
 反正還是能移除的...科科。
9月27日

[ASP.Net]Code behind將死?

 Code Behind技術一直都是.Net時期微軟在網頁技術中最大的應用,但隨著網頁架構的演進,以及對安全性考量的要求愈來愈高的情況下,Code behind是否完全符合所謂MVC架構(Model/View/Controller)的討論事實上是時有所聞。
 除此之外,Code behind這個技術代表著微軟所推出的網頁技術從原始的頁面與程式放在同一頁,轉變成為源碼與頁面設計分開。這種設計的優點在於,以往頁面設計以及商業或邏輯流程設計必需綁在一起的情況得以改善,程式設計人員與網頁設計人員將不需要針對同一個頁面做修改,而可以各別做操作。
 但是Code behind這種東西,他的架構其實還是有討論的空間,尤其在物件導向技術愈來愈純熟的情況下,MVC的架構也愈來愈完整。然後Java就推出了Struts來實作MVC架構。
 老實說,我覺得Struts非常神奇,但我不太會用。
 如果有寫過Struts,他的設計方式真的把Model和Controller拆得非常乾淨,在Controller裡沒有Model,而Model裡又與Controller的操作鬆散的連結,基本上就是由Controller來決定頁面的生成。這有什麼好處?除了符合所謂MVC的架構之外,也有所謂保護原始碼的機制在裡面,其實外界並不知道實際的頁面長像,只能看到副檔名為action的元件操作。
 Code behind不能進行MVC的架構設計嗎?可以,基本上這種架構只要經過適當的架構設計(如商業邏輯及頁面邏輯的分層)就可以完成,其實裡面最難的就是如何將Controller和Model做適當的切分,一個沒有太多實作經驗的程式設計師其實很難運用物件導向的操作原理來實作,更別提所謂MVC架構,甚至N-Tier的程式架構了。
 去年微軟推出了一個新的Framework,叫MVC Framework,也許可以這麼說:LINQ是.Net framework版圖裡最重要的一塊拼圖,這塊拼圖將主宰未來.Net framework發展的走向。
 
 不不!Code behind還會在,就像要直接在ASPX的頁面寫程式也是可以,但是這種架構是否會慢慢的被視作不入流的應用?沒人知道,但是從幾場微軟所舉辦的研討會,甚至Tech ED,LINQ都是不斷被提到的新技術,微軟甚至將它與SQL Server做非常緊密的結合。即使我們都知道Hibernate在Java的場子裡表現雖然不俗,但是卻有相當令人垢病的效能及設定問題,但微軟還是將LINQ用力的推展開來。而MVC架構有什麼問題呢?設定以及難以跨越的進入門檻,尤其是在Model的部份和Linq做結合之後,效能上的表現會不會讓強調大量運用的客戶卻步?
 我們拭目以待。
8月15日

[.Net]初探Data Service

 昨天參加了微軟老大哥所辦的Seminar,雖說是一整天的會程,但是兩場所討論的東西大不相同。
 先說說早上的議程:Sql Server 2008程式...超長的,反正就是微軟找個時間宣傳新上市的SQL Server 2008。其中多了很多對Programmer很好的新服務,有一些耳熟能詳的東西,像Linq、WPF、Sliverlight...之類的,不過有一個好東西,卻很少被提到,這東西叫Data Service。
 以往程式設計師在寫Ajax的時候,常會寫處理後端資料的頁面(或元件..Whatever),藉由Javascript來存取這些頁面,無論是利用Get或Post的方法,來完成一些不用換頁的存取動作。在Ajax漸漸被微軟重視之後(其實也是Sliverlight也有這方面的需求),微軟發展了一個新的專案,叫Astoria,這東西就是後來的Data Service。
 這個專案提供了Programmer一個方便存取資料的方法,透過URL的方式,向後端資料庫存取資料,無論是增、刪、改、查都可以處理。提供的資料格式,除了一般的XML之外,還提供了現在很紅的JSON(JavaScript Object Notation) ,這個服務可以說大大減少了程式師的工作負擔,可以把一部份的工作交給這個服務來完成....
  只要你夠相信它的話。
 其實這東西可以說是Web service的另一種型態,如果換個角度來想,這個服務可以讓專案更容易切分成N-Tier,Programmer可以把Data Layer留在Data Service層中,使用者的程式透過Data Service取得資料。簡單的說,前端的Programmer不需要知道後端資料的細節,後端的Programmer也不需要前端資料的取用方式,以往令人頭疼的ODBC、JDBC都可以把他丟到垃圾桶...他們不再是取用資料的唯一方法了。
 只要你夠相信它的話。
 其實這東西比Web Service更強大,但也意味著它更危險:只要是透過URL取得資料,就有資料曝露在外的安全性考量,我不確定微軟在這方面是否有其他的考量,不過有一點是必然的:只要是放在Web Service上的資料,某方面來說就是要有「這東西遟早會曝光」的準備,安全性永遠都是便利性的反項,就像效能也是便利的反項一樣,程式設計師只能在中間取得平衡。
 從Ajax全力支援、Linq到Data Service,看得出來微軟在這方面的巧思愈來愈多,找個時間來弄一個全套的2008來玩看看,也許會有什麼意外的收獲。
 
8月1日

[.Net]微軟,這到底是複雜,還是簡單呢?

 最近被指派了一個新的任務,負責維護一個簡單的Windows Service。
 仔細想想,這好像是我寫.Net以來的第一支Service,以往都是以Application(甚至是Web)為主,拿到了這程式,雖然是簡單的專案,不過仍是令我興高采烈不已。
 打開專案以後,我有點傻掉了:整個專案裡有兩個不同的Class,一個是Service,一個是ServiceInstall,Service顧名思義就是主要的服務,那...ServiceInstall呢?難不成還要去寫一個安裝的程式?
 沒關係,我自己開一個Service的Project看看,不開還好,一開真的看到自己的渺小...
  原圖
 還真的沒有Install...難道微軟背棄我們了嗎?我耐住性子,編寫了一個簡單的小程式測試Service。

    1 namespace myService

    2 {

    3     public partial class Service1 : ServiceBase

    4     {

    5         public Service1()

    6         {

    7             InitializeComponent();

    8         }

    9 

   10         protected override void OnStart(string[] args)

   11         {

   12             EventLog.WriteEntry("My simple service started."); 

   13         }

   14 

   15         protected override void OnStop()

   16         {

   17             // TODO: Add code here to perform any tear-down necessary to stop your service.

   18         }

   19     }

   20 }

 這程式的意思是在Service啟動時,會在事件檢視器裡寫入一行「My Simple service started」,寫完compile結束後...好像什麼事都沒發生,我天真的以為他就會出現在服務裡了...結果當然是沒有。
 拜大神之後,找到,原來是要用installUtil.exe這支公用程式將程式安裝進去,OK啊,來測試看看。
 

Microsoft (R) .NET Framework Installation Utility Version 2.0.50727.832
Copyright (c) Microsoft Corporation.  All rights reserved.

正在執行交易性的安裝。

正在開始安裝程式的安裝階段。
請參閱 C:\myService.exe 組件進度的記錄檔內容。
檔案是位於 C:\myService.InstallLog。
正在安裝組件 'C:\myService.exe'。
受影響的參數為:
   logtoconsole =
   assemblypath = C:\myService.exe
   logfile = C:\myService.InstallLog
在 C:\myService.exe 組件中找不到具有 RunInstallerAttribute.Yes 屬性的公用安裝程式。

安裝階段已經成功完成,正在開始認可階段。
請參閱 C:\myService.exe 組件進度的記錄檔內容。
檔案是位於 C:\myService.InstallLog。
正在認可組件 'C:\myService.exe'。
受影響的參數為:
   logtoconsole =
   assemblypath = C:\myService.exe
   logfile = C:\myService.InstallLog
在 C:\myService.exe 組件中找不到具有 RunInstallerAttribute.Yes 屬性的公用安裝程式。
由於找不到安裝程式,所以必須移除 InstallState 檔案。

已經成功完成認可階段。

已經完成交易性的安裝。

 哩係得共啥米小朋友...反正就是沒有寫進去就是了,少了一個RunInstallerAttribute的東西。
 好啦~這時候也只能摸索了。先把那個專案打開,然後再看看程式結構。果然是有一個RunInstaller的東西,把這個Keyword丟到Google後,找到一線光明。
 
 
 經過了千辛萬苦,我終於把Service安裝進服務裡了,只覺得一陣莫名其妙:難不成這個Service是可以安裝在Linux裡面?這樣到底是簡單還是複雜?以一個開發環境來說,這麼做只是讓初次接觸的開發者摸不著頭緒,這些可以簡化為三步驟Step by step簡單處理的"Windows 專屬服務開發",卻弄得有夠多事。
 好吧!至少我找到答案了。
7月16日

[.Net]參數化的SQL Command Part 2

 上一篇參數化的SQL Command,主要著眼在程式與Store procedure間的應用。那如果只是一般的SQL Command的話,其實還是可以利用Parameter的方式,向資料庫進行一般的操作。
 一般會這樣寫
 

    1     string connection = "Data Source=localhost;Initial Catalog=pubs;User Id=sa;Password=mysapasswd;";

    2     sqlConn = new SqlConnection();

    3     sqlConn.ConnectionString = connectionString;

    4     sqlConn.Open();

    5     SqlCommand sqlCommand = new SqlCommand();

    6     sqlCommand.Connection = sqlConn;

    7     sqlCommand.CommandType = CommandType.Text;

    8     sqlCommand.CommandText = "select * from demoTable where id = '" + myId + "'";

 這種寫法就是傳說中的「心驚膽跳寫法」,一般的SQL Injection是最愛這種寫法的Programmer了!先不論這種寫法帶給程式面有多少的破壞力,其實平台提供的參數來連結資料庫,安全性的確會大大的提升...但只能說是一般而言會大大的提升。
 那我們把這部份程式改成參數的方式來處理:主要就是考慮在參數的部份,要怎麼把資料丟進去。修改後的程式碼如下:
 

    1     string connectionString = "Data Source=localhost;Initial Catalog=pubs;User Id=sa;Password=mysapasswd;";

    2     sqlConn = new SqlConnection();

    3     sqlConn.ConnectionString = connectionString;

    4     sqlConn.Open();

    5     SqlCommand sqlCommand = new SqlCommand();

    6     sqlCommand.Connection = sqlConn;

    7     sqlCommand.CommandType = CommandType.Text;           

    8     sqlCommand.CommandText = "select * from demoTable where id = @myId";

    9     sqlCommand.Parameters.Add("@myId", SqlDbType.Char, 6).Value = myId;

 在這裡修改了兩行程式碼:第八行的sql指令,將原本連接的SQL Command變成參數化的指令,@myId這個參數就是未來程式要將指令放進去的參數空位,而第九行的Parameters.Add就是指定這個參數的各項資訊:長度,格式,以及參數來源。
 接下來的操作與原本的操作相同:無論是放進DataAdapter,或者是用DataReader來接都可以操作,甚至Select以外的操作,都可以透過參數化的方式來完成。
 
 最近很想轉型成為資料庫型的Programmer,希望那天可以到來。

[T-SQL]T-SQL凋零?

  在查一些T-SQL的學習資料時,查到這個網頁:Wither T-SQL。這個大鬍子提出了幾個論點,認為T-SQL遲早會在以下幾個技術的發展下逐漸凋零。
  • CLR與SQL Server整合。
  • LINQ
  • 缺乏創新的T-SQL。
  我想這個大鬍子的確是有在關注這個問題,不過這個問題我想應該從幾個角度再來討論。
 
  1.並不是只有.Net的程式才會使用SQL Server:以企業級的使用來說,的確SQL Server是比較常與.Net Framework搭配,因此常見的是VB.Net + SQL Server或是C# + SQL Server,但不可否認的是Java、PHP、RoR都會使用SQL Server做為後端的資料庫,如果因為新技術而認為T-SQL是凋零的語言,那真的言過其實了:我想MySQL不會因為Java有了Hibernate而放棄Store procedure的運用吧!
 
 2.版本支援:較舊的如ASP、VB 6.0等等也會與SQL Server搭配,微軟自己家都不支援向下相容了,更何況是對其他程式的支援?別的不提,光是LINQ對Framework的支援就只有到.Net 3.5版,如果因為LINQ就將T-SQL視為雞肋,那可真的是一種令人發噱的預言!
 
 3.效能、效能、效能:Hibernate最令人垢病的一點就是效能上的表現,由其在大型物件上的表現更是令人頭疼,在我參與的某個專案裡,就活生生血淋淋的看著自己的資料庫效能,被其他大量運用Hibernate+Spring的專案所影響,也因而必需進行經常性的效能調校。無論LINQ的發展多麼的令人驚訝,這種效能上的缺憾是沒辦法被原諒的;當使用者發現只是簡單的查詢也要花數十秒甚至數分鐘時,開發者就會懷念起Store procedure的快速了。而CLR的部份也必需要在.Net的平台上運作:只要在這種虛擬機器上運作,不管怎麼比,效能表現都比直接在SQL Server裡處理來得差。我想除非有更大量的數據證明CLR與LINQ的表現足以取代T-SQL在SQL Server裡的處理效能,否則這種事情我想是不太可能會發生。
 
 4.T-SQL還不夠innovation?:不可否認T-SQL所提供架構與OOP大相逕庭,甚至可以說不可能是物件導向的語言,但仔細看看Store procedure,他其實比較偏向程序化甚至指令化的語言,存在的目的也是為了效能。不可否認T-SQL是個需要仔細調效的語言,而且要足夠細心才能寫出輕快的程式。如果因為自己寫不出效能好的Store procedure就認為T-SQL應該丟到歷史的盡頭,我想此時需要的應該是一本T-SQL的Manual。別的不提,SQL Server 2005都將自己內部的函式大量使用Store procedure改寫,以增進效能,將Store procedure視為笨重的語言這種言論令我相當驚訝。
  (說Store procedure是笨重的語言,我真想看看.Net可以寫出多輕快的程式出來)
 
  雖然我最近才真的有在寫Store procedure(大學時期寫的不算的話),但是這些東西都是Store procedure存在的目的,別的不提,光是想像把資料從資料庫中取出,然後在平台上處理完再丟回資料庫中,這一段的事情如果可以直接在資料庫裡直接處理,不是應該是最好的方式嗎?所以老外的話有時候也不用盡信(台灣某報甚至稱作者Rod Paddock為「SQL的領袖」...囧),除非有更好的理由,否則SQL Server真的把Store procedure放棄掉這種言論我想聽聽就好了。
7月11日

[.Net]參數化的SQL command

  這不是新的東西,在.Net 1.1的年代,這東西就已經很風行了,只是懶惰且無知的我一直沒試著去把這東西搞清楚,這兩天花了點時間,把這東西做了一個測試。
 
  一切都是從這裡開始的:
 
    1 Create PROCEDURE dbo.insertDemo 
    2 (
    3     @id integer output,
    4     @name nvarchar(50)
    5 )
    6 AS
    7    insert into demoTable(name) values (@name)
    8     select @id = scope_identity()
    9     RETURN 0
  這是一支簡單的store procedure,它將寫入一筆資料到demoTable裡,並取得最新的id值,並回傳給呼叫的程式。demoTable的id是identity:他會自己增值,所以程式並不用去寫入id。
  因此這支store procedure總共會有一個輸入值,以及一個輸出值。
 
  Visual studio 2005支援store procedure的step debug,不過在測試時會發現,雖然@id是output,不過還是得塞一個值給他...不過並不影響程式實際的操作。
  開始寫程式吧!
 
    1         static void Main(string[] args)
    2         {
    3             string connection = "Data Source=localhost;Initial Catalog=pubs;User Id=sa;Password=mysapasswd;";
    4             SqlConnection sqlConn = new SqlConnection();
    5             sqlConn.ConnectionString = connection;
    6             sqlConn.Open();
    7             SqlCommand cmd = new SqlCommand();
    8             cmd.Connection = sqlConn;
    9             cmd.CommandType = CommandType.StoredProcedure;
   10             cmd.CommandText = "insertDemo";
   11 
   12             SqlParameter parameter = new SqlParameter("@name", SqlDbType.NVarChar, 50);
   13             parameter.Direction = ParameterDirection.Input;
   14             parameter.Value = "getDataByOutput";
   15             cmd.Parameters.Add(parameter);
   16 
   17             parameter = new SqlParameter("@id", SqlDbType.Int);
   18             parameter.Direction = ParameterDirection.Output;
   19             cmd.Parameters.Add(parameter);
   20 
   21             cmd.ExecuteNonQuery();
   22             int rtn = (int)cmd.Parameters["@id"].Value;
   23             sqlConn.Close();
   24         }
  這一段程式的重點在於後半段的SqlParameter的設定:程式將@name的方向設定為Input,即將資料輸入store procdeure中,而@id設定為output,則與store procedure的@id方向做對應,即store procedure所output出來的資料,將由此@id接收下來。
  如果我修改一下store procedure:
 
    1 ALTER PROCEDURE dbo.insertDemo 
    2 (
    3     @name nvarchar(50)
    4 )
    5 AS
    6     insert into demoTable(name) values (@name)
    7     RETURN @@identity
  我把原本的@id取消掉,不再用輸出的方式取得資料,而由比較常用的@@identity取得demoTable的identity,那輸出的方法也由原本的output改成Return,因此這個store procedure只會有一個輸入值,先前的輸出值由原先的output改經由Return取得。
 
  程式也要做相對應的調整:
 
    1         static void Main(string[] args)
    2         {
    3             string connection = "Data Source=localhost;Initial Catalog=pubs;User Id=sa;Password=mysapasswd;";
    4             SqlConnection sqlConn = new SqlConnection();
    5             sqlConn.ConnectionString = connection;
    6             sqlConn.Open();
    7             SqlCommand cmd = new SqlCommand();
    8             cmd.Connection = sqlConn;
    9             cmd.CommandType = CommandType.StoredProcedure;
   10             cmd.CommandText = "insertDemo";
   11 
   12             SqlParameter parameter = new SqlParameter("@name", SqlDbType.NVarChar, 50);
   13             parameter.Direction = ParameterDirection.Input;
   14             parameter.Value = "getDataByReturn";
   15             cmd.Parameters.Add(parameter);
   16 
   17             parameter = new SqlParameter("rtnVal", SqlDbType.Int);
   18             parameter.Direction = ParameterDirection.ReturnValue;
   19             cmd.Parameters.Add(parameter);
   20 
   21             cmd.ExecuteNonQuery();
   22             int rtn = (int)cmd.Parameters["rtnVal"].Value;
   23             sqlConn.Close();
   24         }
  注意到了嗎?原本的output部份的ParameterDirection就改成ReturnValue (程式第18行),以取得從store procedure所回傳的資料。比較需要注意的是,一般的程式return值都只會有一個而已,store procedure也不例外,所以如果需要return多個值回來,當然就是利用output的方式設定parameter,如果要取得的值比較簡單,用return的方法處理就好了。不過!建議的方案是取值的方式一率使用output,那return值的部份,就留給store procedure的try-catch來丟@@ERROR會比較恰當,程式的判讀上也會比較一致。
  好像很簡單的東西花了我兩天在研究,不過挺值得的。
5月30日

[Develop]Ajax學習心得

 有時候程式寫多了,寫久了,就會陷入一種迷思:「怎麼可能做得到」,但其實只要簡單的入門課程之後,有些疑慮就會煙消雲散了。(當然,隨後而來的問題就變成:「奇怪~」)
 我和Ajax的第一次接觸,就是在今年初的時候,算算寫的時間也不太長,大概就這兩個月左右,不過想想之前的感覺,其實對這東西是有一點抗拒的,主要的原因無他,就是對Java script的一份不安全及不信賴感。
 
 寫Web base的前輩們很了解,東西只要交給java script,伴隨而來的就是不可信賴的資料來源,所以資料在進了後端伺服器之後,通常還要再檢查一次,罷特!隨著大型的軟體廠商(如Google、Yahoo、Microsoft..)不斷的增加對Ajax的運用或支援後,不使用Javascript反而變得綁手綁腳。(所以可憐的Developer得再寫一個pure html版本的web page給那些不想使用Java script的user使用..)。
 
 當我開始寫網頁程式的時候,最常使用的方法就是「藏資料」,就是把資料在網頁loading的時候,把資料先「藏」在一個Textbox裡,然後再用Java script或其他的方式把資料從那個欄位裡抓出來 ,處理後直接就寫到要顯示的Label、Textbox裡面,這種處理方式當然比平常讓程式在後端->前端的處理來得快速,不過網頁的顯示速度就會被很大很大的網頁所影響了。
 網頁的Loading影響使用者的心情甚鉅,一個簡單的網頁要顯示10秒,那可是要他的命的!雖然資料庫的調教、網路的問題也是有影響,不過大量的資料處理速度是我等程式設計師可以改善的,減少網頁Loading造成速度上的衝擊,對使用者來說也會是一個很好的經驗。
 但是我還是「藏」資料。沒辦法,這種方法很好處理,但是太大量的資料或固定的資料就不適合用這種方法了。
 
 Ajax給我最大的便利就是我不用預取資料,只要透過事件的觸發,讓Java script再向另一個網頁取回資料就可以了。這其實是很棒的設計,先不要說什麼安全啊、可信賴啊之類的屁話的話,光是一個最常見的程式:郵遞區號,就可以透過Ajax的方式來處理,以往我看到的程式,都是放一個超級大的陣列在網頁裡待取,然後每個user一份這樣送出去,不過現在只要取到需要的一部份就可以了!透過XHR,將選取的地區透過Ajax向查詢頁面查回一組資料,再處理後重新畫一個出來就好了!真是太美妙的經驗了...(怎麼寫一寫像在證道...)
 
 最近在Spencer和Taco(依姓名排序)的協助之下,也調整了我的XHR初始程式:
 
 

function getXmlHttp()
{
  var xmlhttp;
  if (window.ActiveXObject) // if IE
  {
    try
    {
       xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch(e)
    {
      try
      {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch(e){ xmlhttp = false; }
    }
  }
  else
  { 
    if (window.XMLHttpRequest) // if Mozilla, Safari etc
    {
      try
      {
        xmlhttp = new XMLHttpRequest();       
      }
      catch (e){  xmlhttp = false; }
    }
    else
       xmlhttp = false;
  }
  return xmlhttp;
}
function getPersonData()
{
    var url = "composePersonXml.asp";
    var xmlHttp = getXmlHttp();
    xmlHttp.onreadystatechange = function()
    {
        var groupInnerHtml = "";
        if(xmlHttp.readyState == 4) {
            if(xmlHttp.status == 200) {
                getPersonUnderUnit(xmlHttp);
            }
        }
    };
    xmlHttp.open("GET", url,true);
    xmlHttp.send(null);
}

  當然,這種預設程式不是最好的寫法,我也在想最好的處理方式是怎麼樣,不過這個開頭很棒,以後應該可以好好利用這種方法。
  現在流行「執班正妹」
  
  下次見啦!
3月24日

是不是應該來學看看AJax??

 最近客戶又做了一個夢,雖然不是很大的夢,但是這個夢拖了太久,結果壓力全部都壓在我身上...
 其實是OK啦,不過就是改ASP的程式罷了!這年頭又不是沒人用ASP,怕什麼!
 
 當然也沒有很抖啦!(比起來,改WebSphere的Portlet我還比較抖一點)
 
 不過也還好身邊有很多Javascript的高手,在過一些關卡的時候總是挺順利的,最終還是把一些功能給補上了...真的挺神奇的,這些東西以我三年前那個笨笨的腦袋,一定想不到這些東西該怎麼處理,我真的沒有學過ASP,不過這一次下來,其實對我是有好無壞啦!以後再做ASP.Net的東西的時候,其實更能分辨前端處理和後端處理的差別,還有什麼時候要交給前端處理,什麼時候要交給後端處理,未來在設計上就更具彈性了。
 
 不過說是這樣說,還是得跟客戶說:「真的有夠難的...囧」
 像下面這支程式,就是高手指點下的產物:
   1:  <SCRIPT language="JavaScript">
   2:   var cal = new CalendarPopup();
   3:      function test(para)
   4:   {                        
   5:      var strOptList = document.getElementById("optionList"+para).value;
   6:      var OptList = strOptList.split(",");                        //原列表
   7:      var tn = document.getElementById("select_qty"+para).value;    //選項
   8:      if(OptList.length >= tn)
   9:      {    
  10:          stringP = ""
  11:          for(i=1;i<=tn;i++)
  12:          {
  13:              stringP += "<label>選項"+i+".</label><input name='q" + para + i + "' id='q" + para + i + "' type='text' size='50' value='" + OptList[i-1]+ "'><br>"
  14:          }
  15:          document.getElementById("DivExample"+para).innerHTML = stringP;
  16:      }
  17:      else
  18:      {
  19:          stringP = ""
  20:          for(i=1;i<=OptList.length;i++)
  21:          {
  22:              stringP += "<label>選項"+i+".</label><input name='q" + para + i + "' id='q" + para + i + "' type='text' size='50' value='" + OptList[i-1]+ "'><br>"                        }
  23:          for(i=OptList.length+1;i<=tn;i++)
  24:          {
  25:              stringP += "<label>選項"+i+".</label><input name='q" + para + i + "' id='q" + para + i + "' type='text' size='50'><br>"
  26:          }
  27:          document.getElementById("DivExample"+para).innerHTML = stringP;
  28:      }
  29:  }
  30:   </SCRIPT>    

  上面的程式有做小部的修改,簡單說呢就是去維護一個不定數量的選單,基本上我已經有感覺到AJax的有趣了。
 改天找本書來練練功吧!
1月21日

[C#]DataGuid操作

對於Visual Studio 的DataGuid真是又愛又恨。
愛的是他實在方便的狠,很多東西都不用寫程式他就自己搞定了。
恨的是他很多都是都不能寫程式控制,他媽的很多東西都不方便。(我在說什麼...)
 
今天補幾個最近寫好的東西好了:
(一)連結資料:
  DataGuid基本上是透過DataView來做資料連結,所以當你要把資料和DataGuid連結時,第一步就是先把資料傳乎便便,然後再把資料用DataView連結,然後再設定DataGuid使用這個DataView就可以了。
 很麻煩乎?其實還好:

DataTable dt = (DataTable) Session["dtDetail"];

DataView dvDG = new DataView(dt);

dgDetail.DataSource = dvDG;

dgDetail.DataBind();

 以上的程式碼,我先在前一頁把資料都整理好了,然再用Session的方式傳到第二頁,再用一個新的DataTable把這個Session的資料接出來,然後再傳到DataView,然後DataGuid。
(二)排序:
 排序一直是程式裡最麻煩的東西,因為程式裡可能要寫一大堆,今嘛!免!用DataGuid裡面的設定就可以搞定了。我查了一些資料,大多都是簡易的說明,就是用Visual studio的元件庫來拉,這種情況會造成缺乏彈性,但是如果只是單純的顯示資料那就直接用就好了,只是大多的情況都不是單純的情況。
 我們還是回到一開始的情況:資料傳便便,然後用DataView連DataGuid的方式。接下來我們要到aspx頁面,把Sort功能打開。
 

<asp:DataGrid id="dgDetail" style="Z-INDEX: 101; LEFT: 144px; POSITION: absolute; TOP: 112px" runat="server" BorderColor="Black" AllowPaging="True" AllowSorting="True">

 然後加上幾新的Tag給要排序的列:

<asp:BoundColumn DataField="AP" SortExpression="AP" HeaderText="系統">

 就好了嗎?代濟甘屋佳甘單。
 回到aspx.cs的頁面,我們需要加上觸發排序的功能,先調整InitializeComponent()

private void InitializeComponent()

{   

     this.Load += new System.EventHandler(this.Page_Load);

     this.dgDetail.SortCommand += new DataGridSortCommandEventHandler(dgDetail_SortCommand);

}

 

 這部份其實Visual Studio會幫你產出程式,你只要順著他的暗示就好了,當你完成觸發程序後,他應該會給你一個空的程式碼如下:

private void dgDetail_SortCommand(object source, DataGridSortCommandEventArgs e)

{

}

 其中的(DataGridSortCommandEventArgs e)就是觸發的程式,接下來要補上排序的程式。

private void dgDetail_SortCommand(object source, DataGridSortCommandEventArgs e)

{

     DataTable dt = (DataTable) Session["dtDetail"];

     DataView dvDG = new DataView(dt);

               

     dgDetail.DataSource = dvDG;

     dvDG.Sort = e.SortExpression;

     dgDetail.DataBind();

}

 簡單說呢,就是DataView重新再DataBind一次而已,加上一個SortExpression,就是我們前面加的SortExpression,很簡單,不過測的時候會發現,只能排一種樣子而已,不是升冪,就是降冪。這種排法不會讓人高興的。
 ok,我們從SortExpression開始處理起,當我們去看SortExpression之後,會發現他其實只是一個字串而已,我們實際去測SortExpression,如果加上ASC或DESC,的確也會跑出升冪和降冪的結果,所以我們想辦法把排序功能加上去吧!

private void dgDetail_SortCommand(object source, DataGridSortCommandEventArgs e)

{

     string sortKey = "";

     if(ViewState["SortKey"] == null)

           ViewState["SortKey"] = "DESC";

     if("DESC".Equals(ViewState["SortKey"].ToString()))

     {

           ViewState["SortKey"] = "ASC";

           sortKey = " Desc";

     }

     else if("ASC".Equals(ViewState["SortKey"].ToString()))

     {

           ViewState["SortKey"] = "DESC";

           sortKey = " Asc";

     }

     DataTable dt = (DataTable) Session["dtDetail"];

     DataView dvDG = new DataView(dt);

     dgDetail.DataSource = dvDG;

     dvDG.Sort = e.SortExpression + sortKey;

     dgDetail.DataBind(); 

}

  上面的程式就是加一個ViewState,記錄上一次排序是用升冪還是降冪,然後下一次就反過來就好了,簡單又方便,而且幾乎沒寫程式,真好!
(二)Event fire!
  這是碰到一個小問題的時候,查資料時老外用的單字,firing。
  延續上面的討論,當要執行排序時,會觸發SortCommand的事件,但是事件的觸發其實還是有順序性的。
  什麼意思,如果你的程式裡,有ItemCommand也有SortCommand,在.Net裡,當要執行排序時,會先觸發ItemCommand,再觸發SortCommand...I don't know why。
 
  那不就屎了?解決方法也不難,因為ItemCommand的DataGridCommandEventArgs,裡面的DataGridCommandEventArgs.Item.ItemIndex會等於-1,我目前是依這個來做判斷,只要exit ItemCommand,就能順利執行SortCommand了。
 
  I don't know why....<囧>
(三)字上色:
  這次這次遇到的一個小小的困難,如果DataGuid裡要處理某個字串的ForeColor要怎麼處理?
  在和幾個朋友討論之後,用以下的處理方式:

     dgDetail.DataSource = dvDG;

     dvDG.Sort = e.SortExpression + sortKey;

     dgDetail.DataBind();

     for(int i = 0 ; i < dt.Rows.Count ; i++)

     {

           if(!("正常".Equals(dgDetail.Items[i].Cells[4].Text)))

           {

                dgDetail.Items[i].Cells[4].ForeColor = Color.Red;

           }

     }

  簡單說呢,就是用兩個迴圈包起來,第一個迴圈,用DataTable的方式找出每一列的資料,再找出該欄的資料做判斷...其實沒那麼難啦~
(四)Between And
  這是一個長期的誤會。
  SQL Statement裡,有一個叫Between And的Key word,這個Key word其實應該不叫Between A And B,應該叫Between Min And Max,好啦~以前我不懂,現在我懂了。
 
  徵求不是Between Min And Max而是Between A And B的資料庫,目前看起來DB2和MySQL都是,等一下測看看SQL Server。其實既然用Between And,Between A And B比較合理不是嗎...不甘啜泣
12月31日

.Net及Java的Override處理方法

 最近寫了一個小程式在處理XML,由於是第一次使用大量的物件導向的設計方式,在處理上遇到了一些小小的困難,不過大多順利解決。
 比較值得紀錄的就是這個東西:Override。
 
 我們從Override開始談起好了,什麼是Override。
 當我們在運用一個物件的時候,其實有時候是操作到同一個method,但產出的結果是截然不同的,一般會有兩種方式解決,一個是多型(polymorphism),一個就是覆載(Override),多型的處理方式比較簡單,只要符合「任兩個方法沒有輸出入的衝突」就可以了,但是Override處理方式就比較不同了。
 
 我們如果有一個需要Override的方式,一般來說,希望你所覆載的方式是被標記成抽象的,這會造成什麼情況呢?就是一個有抽象方法的物件,最後也被迫必需被標記為抽象,如果是小程式或被分工良好的程式還好處理,如果雄雄跟人家說「歹勢,我要借用你的元件,但是有部份程式我要Override,請你提升他的規格成抽象物件吧!」
 
 要我就把你拖到會議室裡好好跟你談一談了。
 
 當然也不是不對,這是常見的情況(所以Interface和Abstract class很常見),但是如果急起來那還有空理你?跟你一起修改程式?所以各家平台還是有各平台的處理方式。
 
 以.Net來說,你可以把你要Override的method加上一個new修飾子,如:

           new public string getWay()

           {

                return this.way;

           }

 以Java來說,則在前面加上一個notation @Override 就可以了

           @Override

public String getWay()

           {

                return this.way;

           }

 不過不管是Java還是.Net,可以Override的情況就是以下幾個資料有需要相同的情況,才需要考慮用Override:
  • 回傳的型態
  • 函式名稱
  • 參數型態
  • 參數個數
 其實從這裡就看得出來,其實Override是很龜毛的情況下才會考慮使用,這次會用到這東西也是被自己龜毛到了才會去試這種東西。
 個人是認為Java的處理方式是很讚的方法,.net加了一個修飾子,其實沒有人知道那個東東是Override。但整體來說都是簡單的處理方法,只是MSDN寫得模模糊糊的...
 
 其實如果不想那麼麻煩,就想辦法用多型處理掉就好了,但是如果能不用多型當然是最好了,因為多型不是拿來這個地方用的。
12月19日

[UML]Robustness Diagram

 上一篇說了Use case diagram和Use case,其實一般的專案開發,從Use case出發是最簡單也最貼近使用者的一種開發方式,然而Use case其實還是在程序上面,如果要真正轉成系統開發,以現在的主流:物件導向的開發模式來說,真的要落實「從行為面需求找出物件」其實有一定的難度。
 其實最明顯的情況,就是某個行為究竟應該是屬於那個物件下比較貼切?在設計類別時會不會因此忘了應該將此行為列入考量?這些在類別的規劃時都必需一併考量,但是行為面還是在Use case裡面,類別圖我們都是靠著Use case來「想像」這些類別可以放在這個類別裡;用想像是有點誇張,不過在缺乏經驗之下,想像的確是初學者的第一步,在經過多次的經驗累積之後,想像就會變成經驗法則了,不過還是沒有任何可以證明「想像」或「經驗」是否合理?對我來說其實很難讓我信服,或者相信自己設計的東西就是合理的。
 Robustness Diagram的功用就是這樣,它其實很簡單,總共就只有三個元件:
 
            
 
 Boundary object是Interface,簡單說就是介面,系統和外界溝通的介面,一般來說像GUI、網頁都是Boundary object。
 Entity object又叫Domain,其實粗略來說不應該叫「類別」,我覺得在這裡稱作叫「個體」非常合適,這個物件其實他的功用就是將use case裡的行為溝通的目標個體歸納出來,在Robustness Diagram裡,Entity object是協助分析人員找出Class最大功臣,最後就會發現,其實所有的行為都是圍繞在某幾個Entity上面,最後這些Entity object就會形成domain,而這些domain就會變成class,在系統裡變成object來操作。
 Control object就是行為,整個系統裡會使用到的動作,其實還滿容易被理解的,因為在進行捕捉行為模式時相常順利的情況下,Control object就會變得相當重要:因為他是串起Boundary及Entity的物件,也是驗證Use case diagram行為描述是否合理的核心。
 其實除了以上幾種圖型之外,Robustness diagram還會用到Actor和Use case,這兩個圖形都是從Use case diagram借過來的,目的就是在證明說(1)Robustness diagram所歸納出來的東西是給Actor用的(2)圖形裡的操作除了針對Entity object之外,也會針對非此system scope的use case...尤其是在兩個use case在不同system scope的情況之下。有時候也會用到Class diagram的Class object,其實理由也跟上面一樣,如果有已經分析好的class object,Robustness diagram直接引用也是合理的。不過需要特別注意的是,使用到use case時,應該將那個圖形視作Boundary object,而class的話,很明顯的應該視作Entity object。
 接下來要說他們溝通的規則:Boundary和Entity都是個體,而Control為操作,所以在做行為描述時,我們會用SVO(就是S對O進行一個V的動作)的規則來描述,即:
  • Boundary  --- Control (一個網頁可以操作一個動作)
  • Control  --- Entity    (一個動作可以影響一個物件)
  • Control --- Control     (一個動作可以引發另一個動作)
  • Actor --- Boundary          (一個外部使用者可以操作一個網頁,在Robustness diagram裡,Actor one and only one合理的行為)
  • Actor --- Use case          (這是Use case的規則)
  • Control --- Class              (因為Class視作Entity,行為合法也合理)
 除了以上的行為,(應該)其他的行為都是不被Robustness diagram接受的。
 接下來我放一個我最近自己畫的Robuestness diagram供大家參考:
      http://russel.chang.googlepages.com/RobustnessDiagram.jpgJude 畫的)
 在完成Robustness diagram的行為捕捉後,就可以找出合理的(注意,合理的,不一定是正確的)物件,另外也可以驗證使用者的行為是否都在考量裡面,好處多多。既然捕捉到物件,接下來就可以進行循序圖、物件圖、活動圖,甚至狀態圖、合作圖、部署圖都可以依序產生!
 那...Robustness diagram咧?
 丟掉他吧!(啥!?)
 在我猜想中OMG沒有列入Robustness diagram的原因就是因為他的免洗性質實在太高!其實當使用者第一次將Use case轉成Class diagram之後,這雙免洗筷就可以丟掉了,完完全全的兔死狗烹。但是這張圖實在很好用,仍有很多人士在為他奔走,希望能列入UML標準圖形之內(也是因為這樣,有很多UML的廠商都有支援Robustness diagram的元件,但是未必會將此圖列入標準圖形,像Jude就是合併在Class Diagram內,而Visio...算了吧!)
 大概就是這樣啦!說得有點二二六六,不過精神大概就是這些,有興趣的人可以參考一些其他的網頁資料,應該會寫得比我好才對。
12月17日

[Develop]SA文件編寫經驗

最近被小小的逼著寫SA文件,其實寫這種文件對我來說並不是什麼難事,只是覺得寫這種東西很讓人抓狂。
第一,他沒有固定的格式,第二,它沒有一定的標準(雖然是有,但是那是一九八幾年的事情,後來的都沒什麼標準可言,都是約定成俗了)。
 
不過想想,這次的經驗還滿不錯的,做個小小的記錄
 
其實在寫系統分析文件之前,最好是有一份已經定好的RFPRequest For Proposal,需求建議書)件,這東西可以支撐整份系統分析文件可以往正確的方向前進。再來就是已經確定好的訪談紀錄、Use case文件,還有已經確定好的系統範圍,例如說要做到多大?可以接受的預算範圍?有沒有額外的支援?是否要和其他系統介接...之類的。
 
準備好以上文件之後,就是開始做使用案例的分析,正如同Airy所說的,其實Use case圖是整個UML的雞肋,但是有一個東西很重要,就是系統範圍,定出系統範圍之後,才能知道整個系統可以做多大,需要多少的人力。
 
其實Use case diagram基本上進SA文件裡的確是很明顯的發現他不是很重要,因為他的圖型可能頂多就是這樣:
    http://conferences.codegear.com/article/images/32151/image005.gif
看這張圖真的很無聊,所以重要的東西應該是對Use case的描述,包含他的案例使用者、案例摘要、條件、例外處理、以及其他資訊等,能夠把這個活動描述得愈清楚愈好,舉例來說:
 
  • 使用案例代碼:UC_ALG_01
  • 案例名稱:用戶申請
  • 案例主要使用者:應用程式人員、審查人員
  • 案例次要使用者:平台維運人員
  • 案例摘要:平台維運人員為使用者申請稽核紀錄中心用戶帳號
  • 前置條件:應用程式使用者具日誌儲存需求
  • 活動方式:(1) 應用程式人員填寫用戶申請單
    (2) 填寫完後,送審查人員審核申請單
    (3) 通過後,交由平台維運人員開設帳號,並設定家目錄。
    (4) 應用程式人員驗證稽核紀錄中心所核發之帳號、密碼、家目錄權限設定,測試上傳日誌檔是否正常。
  •  案例產出:
    (1) 使用者帳號、密碼:與稽核紀錄中心連線用
    (2) 系統代碼:該應用系統之系統代碼
    (3) 日誌檔名:待上傳之日誌檔檔名
  •  備註:
    (1) 申請作業為人工作業
    (2) 帳號、密碼管理細則請參閱附錄:帳號密碼管理細則。
    (3) 表單審查人員為專案承辦人及科長、資訊平台承辦人及科長。
    (4) 稽核紀錄中心用戶範圍,以資訊中心所提供的應用系統服務為日誌收集標的。
    (5) 若非此範圍之日誌亦需納入控管,亦可透過申請手續申請。
大概要描述的東西就是這樣,如果能愈詳細愈好, 盡可能把談的東西都付諸文字描述,然後再附件裡把這些東西附上去。

接下來就是用Robustness Diagram把Use case diagram轉成Sequence diagram,並將Class diagram轉出來。在這裡我不打算介紹Robustness Diagram,這張圖我上個月才學到,發現OMG沒把這張圖列進UML標準圖形真是太可惜了!這個東西在介入Use case Diagram和Class Diagram是很好用的東西。這個星期如果有空一定介紹一下。

 

接下來就跟一般人的做法有點不同了:我一直覺得在系統分析階段使用到活動圖是很奇怪的事情,其實在系統分析階段,使用sequence diagram會比使用active diagram來得合適,除非在製作use case文件的過程中,已經可以完全掌握所有的例外或錯誤的情況,但這種情況其實並不多見,大多的情況都是在設計的時候,才會考量到異常的情況要怎麼處理,而比較大的錯誤情況,其實用註解的方式說明就可以表達完全,利用sequence diagram將主要的活動流程向使用者說明會是比較適宜的做法。
 
其實還有很多東西可以在分析階段做陳述,只是需不需要做到那麼細而已,如果跟$$$有關,又想表現的比較專業一點,就愈細愈好吧!
 

11月21日

[Ant]記憶體參數調整

其實這個問題我一直想處理想很久了,只是一直找不到時間處理(理由啦~其實是每次看到錯誤訊息就想快點重開)。
錯誤訊息如下

[piBJCServer|com.PowerIntegral.BatchJobCenter.Server.piIIOPClient@14f0076|Power

Integral|1000|piCopyist_java.properties piBJC localhost 10050 D:/tpc/piCopyist_Java/bat/piCopyist_java.bat    2007.11.21^(3)^9:13]

Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space

大致上的意思就是記憶體被吃光了,但是怎麼可能?當然有可能,畢竟這東西是放在JVM上跑的。
好吧!那就把記憶體放多一點給他吧!在Ant上補上參數:

<target name="runReceiver" >

<java classname="tw.gov.tpc.nds.mailService.topic.WSTopicReceiver">

          <classpath refid="run.classpath" />

          <jvmarg value="-Xms256m"/>

          <jvmarg value="-Xmx256m"/>

</java>

</target>

代濟甘屋佳甘單?

D:\tpc\piCopyist_Java>ant runSender

Buildfile: build.xml

 

runSender:

     [java] JVM args ignored when same JVM is used.

     [java] log4j:WARN No appenders could be found for logger (org.springframewo

rk.beans.factory.xml.XmlBeanDefinitionReader).

     [java] log4j:WARN Please initialize the log4j system properly.

     [java] Topic name is jms/Topic

 

BUILD SUCCESSFUL

Total time: 4 seconds

大致查了一下Google上有的資料,其實只要加一個fork的參數就可以了。

<target name="runReceiver" >

<java classname="tw.gov.tpc.nds.mailService.topic.WSTopicReceiver" fork="true">

          <classpath refid="run.classpath" />

          <jvmarg value="-Xms256m"/>

          <jvmarg value="-Xmx256m"/>

</java>

</target>

再run一次。

D:\tpc\piCopyist_Java>ant runSender

Buildfile: build.xml

 

runSender:

     [java] log4j:WARN No appenders could be found for logger (org.springframewo

rk.beans.factory.xml.XmlBeanDefinitionReader).

     [java] log4j:WARN Please initialize the log4j system properly.

     [java] Topic name is jms/Topic

 

BUILD SUCCESSFUL

Total time: 4 seconds

搞定。
11月16日

什麼叫「抽象化」

(很久沒有寫這種心得了...:D)
 
來!各位物件導向開發的同仁們,物件導向的四支柱子是什麼?
 
超重要的啦!四支柱子就是封裝(encapsulation)、繼承(inheritance)、多型(polymorphism)、抽象(abstraction)。
 
也許在看一些專書的時候,會發現物件導向的柱子只有前面三支,不過我認為前面三支如果沒有抽象當成基柱,整個物件導向的設計就沒辦法變得精巧神奇,尤其是繼承和多型,沒有抽象化的加持,就看不到物件導向神奇的地方。
 
什麼叫抽象化?簡單的說就是「概念」,就是「精神」,說明白一點就是如果你試圖將抽象化的東西實體化,那個東西基本上是不能用的。
 
例如說:一台在台三線上奔馳的Yamaha SR400,這是一個被完全實體化的物件(廢話,都在奔馳了),那這東西的抽象化就變成什麼?
 
一條路,一台車子。
 
有人說:哩地共啥小朋友?
......嘸力宏哩來供跨賣!
 
雖然這只是無聊的陳述,但帶給物件導向的威力可不是三言兩語可以描述的,簡單說呢,就是用最簡單的物件,把使用者想描述的世界畫出來。「抽象化」這個詞本身就是從哲學裡拿來用的,用意就是從物體裡抽出他的概念,例如說剛才說的「一台在台三線上奔馳的Yamaha SR400」,就會變成「在路上的一台車」,如此一來又有什麼好處?我不知道台三線是什麼,但是我知道那是一條路了,我也不知道Yamaha SR400是什麼,但是我知道那是一台車子了。接下來我們就會談及什麼路?什麼車?甚至以什麼速度,車子的顏色?天氣如何?等等這些後話了。
 
雖然說抽象化這麼強大,但是大家多半不提,因為它實在很難啊!一般在做分析的時候,很難去知道那些Properties是保留的,那些Method是這個物件一定要保留的,最後就拉拉雜雜的留下很多東西在第一層,繼承或引用這個物件的人,就只好掛著一堆垃圾在裡面。
 
好啦,那我們來抽象化天線寶寶吧!
(你好!)
9月20日

C#命名規則

退伍後,在軟體開發上的第一課就是『命名規則』,雖然很不甘心,但是這的確很重要,也的確沒人教過我。
 
經過一些時間的更迭後,我對這件事慢慢成為習慣,但也更想把他弄得更像一回事,於是這幾天開始反過頭來找一些當初被交付要讀的文件。
 
 
它們的文件很詳細,還有舉例,算是相當值得一讀的文件,雖然是For C#,但事實上應該是給一個大型專案用的。文件下載位置在這裡,或者點右邊的工具欄,『The IDesign C# Coding Standard, for development guidelines and best practices.』
 
 
這個不用多講了,絕對專業的官方文件,但是我還沒時間看就是了,有機會看完再寫心得上來,不過照MSDN的經驗,應該又是囉囉嗦嗦一堆吧。
 
 
比上面兩份囉囉嗦嗦的文件簡單多了,只有三頁,想貼在牆上當Memo很適合。文件下載位置在這裡,或點下方VS.NET Naming Conventions下載。
 
這東西在系統發展上的確是好的,仔細想想,我好像也學過這個東西呢,不知道從那裡學來的就是了疑惑

Refactory Factory

我沒有故意玩文字遊戲。
 
最近為了某個很大的單位要變成更大的單位,把之前寫的轉檔程式拿出來重寫。其實是剛好有時間可以做這件事,也是因為這算是欠著沒做的事情。
 
一開始的原因是因為很久以前的Programmer在寫這段的時候沒有仔細寫,就直接把資料倒到我們這裡的資料庫裡,但是仔細看了之後,才發現問題非常多,為了不要讓窗戶繼續破下去,即早把有問題的程式改過來是我的第一目標,但似乎有些人不這麼認為,但身為風暴核心的我當然不能讓事情放任不管(因為很快就能看到風暴襲來啊!),因此寫了第一版的同步程式,但是沒有物件化。
 
這支程式很快就上線了,也很順利的執行,但是問題來了:
 一、有問題很難查找。
 二、找到問題後需要全面換版。
 三、寫的很爛,有夠爛,只有大一程度,我出社會第一支程式就是寫這樣,現在還寫這樣?
基於以上理由,我把程式全面改版。
 
大致上的做法,就是花一天把程式的流程分析完之後,再把可元件化的部份抽出來,大概就可以變成四大塊。
 http://russel.chang.googlepages.com/factory.jpg
這樣做比之前直接包成一大包好處理多了...而且比較專業(的感覺)。
SyncPlatform透過Factory來呼叫Attdba以及Platform兩個資料庫,各元件間互相獨立,維護起來比較簡單,以後就不用煩惱某個部份要動,所有的程式都要動這個問題了。
 
但是有個比較不痛不癢的問題:究~竟~Factory要用Interface還是用Abstract Class呢?當然,GoF的書裡除了Abstract Factory外,並沒有Factory這個Pattern,Abstract Factory當然是用Abstract Class,那為什麼我會一直有Interface ->Factory這個印象呢?應該是Spring Framework的原因吧!如果我們回過頭來看Interface和Abstract Class的差異,這個東西大概要用兩個星期吧!先不論兩個的差別,我先用比較熟悉(但也不夠熟)的Interface完成這個小案子,Interface和Abstract Class間的問題下次有機會聊。
 
ok,透過Factory來管理Interface以及Interface implement,SyncPlatform透過Factory來呼叫Implement,執行後沒有問題,就上線了...咦?好平淡的結束。
 
其實這次的改版更動很大呢,只是似乎只有在小隔間的我有點興奮而已。
 
2007/9/14 上午 11:49:27 Russel™ - LOHAS狐狸 Crux - 迷錢了 SyncFactory syncFactory = new sync.SyncPlatformFactory(); AccessAttdb accessAttdb = syncFactory.getAccessAttdb();
2007/9/14 上午 11:49:37 Russel™ - LOHAS狐狸 Crux - 迷錢了 SyncFactory syncFactory = new sync.SyncPlatformFactory(); AccessAttdb accessAttdb = syncFactory.getAccessAttdb();
2007/9/14 上午 11:49:51 Russel™ - LOHAS狐狸 Crux - 迷錢了 這樣寫應該接近正統的寫法了吧?
2007/9/14 上午 11:50:17 Crux - 迷錢了 Russel™ - LOHAS狐狸 Ya, 一整個讚..
2007/9/14 上午 11:50:23 Russel™ - LOHAS狐狸 Crux - 迷錢了 (Y)
2007/9/14 上午 11:50:30 Russel™ - LOHAS狐狸 Crux - 迷錢了 智慧++;
2007/9/14 上午 11:51:15 Crux - 迷錢了 Russel™ - LOHAS狐狸 呵呵
2007/9/14 上午 11:57:16 Russel™ - LOHAS狐狸 Crux - 迷錢了 真的可以work呢..XD
2007/9/14 上午 11:58:55 Crux - 迷錢了 Russel™ - LOHAS狐狸 有沒有一整個神奇的感覺? 我第一次寫就是這樣的感覺.. XD
2007/9/14 上午 11:59:34 Russel™ - LOHAS狐狸 Crux - 迷錢了 有一點吶..我一直在等exception出來...XD
2007/9/14 下午 12:01:31 Crux - 迷錢了 Russel™ - LOHAS狐狸 請問你是不相信spring, M$, 還是自己... :P
 
感謝大小姐的全力協助。
 
下一版要做的更動,是查看看卡窗的情況,或者寫一個視覺化的同步程式來玩玩(那批次中心咧..XD)