2008年9月30日 星期二

OOAD(4): 需求清單 (Requirement lists)與 use case 的發展過程

 

  1. 由客戶取得原始需求。如下之A.B.C
    • A
    • B
    • C
  2. 由原始需求發展 use case。如下之 a, b, c , d
    • a
    • b
    • c
    • d
  3. 再發展 alternate path。如下之 b.1, b.2
    • a
    • b
      • b.1
      • b.2
    • c
    • d
  4. 根據上一步的 use case,與原始需求比對,看看需求是否完整。如果不完整,就向客戶確認並取得更完整的需求
  5. 重複2到4,直到需求完整為止。

OOAD(3): Use Case 的三個部份

一個好的 Use Case,必須有三個部份

Clear Value

  1. 每個 use case 必須對於系統有清楚且單一的價值。
  2. 如果該 use case對於系統沒有價值,就沒有什麼用處。
  3. 一個 use case只能有一個目的(goal)。一個目的(goal)可以有多個 use case,原因是Alternate path。

Start and Stop

每個 use case必須有非常明顯的起點與終點。某個東西(Actor)必須開始這個 use case,而在某個狀況下結束這個流程。

External Initiator

每個 use case必須被系統外的「外來初始者」來起動。通常是個「人」。

2008年9月29日 星期一

OOAD(2): 檢查物件的責任

在OOAD中,物件的應當負什麼責任是相當重要的。
因此,在寫完的程式中,要如何一個object中找出不適當的責任呢?

  1. 由物件方法(method),屬性(property)來找。例如飛機.起飛(plane.TakeOff),飛機.降落(plane.Land)是正確的。但飛機.驗票(plane.VerifyTacket)明顯的是放錯了責任。
  2. 無用的屬性(property)。如果一個物件的某個屬性常常是 null,這很可能代表該屬性不應屬於此物件。

雖然是老掉牙的原則,但要在每個物件中遵守卻也是相當難的了。

OOAD(1):物件方法的命名

物件(object, an instance of class)方法(method)的命名,應該是

  1. 動詞:如飛機.起飛 (plane.TakeOff)
  2. 動詞+受詞:如飛機.加油 (plane.AddOil)

OOAD(0): 建立好的軟體之三步驟

最近在讀一本書。
以此當作筆記

建立好的軟體之三步驟

  1. 確定軟體如客戶所期望
  2. 使用物件導向基本原則來增加彈性
  3. 可維護、可重用的設計

第一步驟即規格,或 functionality。在軟體建置的過程中,這是最重要,也是我們的目的。即使其他兩個步驟寫的再好,不是客戶要的,也都沒有用了。所以規格必須放在第一步。

2008年9月24日 星期三

以 javascript 列印 PDF 檔

收到客戶這樣的需求,原本以為是相當簡單的一件任務。

失敗範例:







當按 print 時,會正常地跳起列印話框。但列出的結果,iframe 內的內容空白。
image

範例:






當按 print 時,會跳出 arcobat pdf reader 專用的列印對話框。

image

範例下載

2008年9月19日 星期五

PowerShell 學習(2)

Get-PSDrive 的結果,可以看到 PowerShell 的 drives。好玩的是它將 registry 也當成 drive 了
image

輸入 cd hklm: 後,再 dir,就真的將 hklm 當成 drive 了
image

而且,還可以建目錄 (md),刪除目錄 (rd)。使用 regedit32 來看結果時,還真的有出現值呢。

PowerShell 學習(1)

get-command 取得所有的 command 說明
get-help get-service 取得 get-service 這個 cmdlet 的說明
get-help get-service -detailed 取得 get-service 這個 cmdlet 的詳細說明
get-service | where-object {$.Status -eq "Running"} 取得狀態等於Running的服務
get-service | where-object {$_.Name -eq "dns"} | stop-service 1 取得所有的服務
2 其中名稱為 dns
3 停止符合條件的服務
get-process | get-member 取得 get-process 回傳物件的member
get-PSDrive 取得 PS的 drives

2008年9月18日 星期四

XDocument 與 XElement 的一個不同處

使用 XElement 或 XDocument 來載入 xml 時,到底有什麼不同的地方呢?

範例

 

      string xml = @"el 1el 2el 3";
      XElement el = XElement.Parse(xml);
      var q = from e in el.Elements()
              select e;
      Console.WriteLine("Load in XElement");
      foreach (var item in q)
      {
        Console.WriteLine(item);
      }
      XDocument doc = XDocument.Parse(xml);
      q = from e in doc.Elements()
          select e;
      Console.WriteLine("Load in XDocument");
      foreach (var item in q)
      {
        Console.WriteLine(item);
      }
結果如下
Load in XElement
el 1
el 2
el 3
Load in XDocument

  
  el 1
  el 2
  el 3
由此可看出,使用 XElement 的 Elements() 時,不會讀取 xml comment,會將該 r 當作 element 的current node
而使用 XDocument 的 Elements()時,會將 xml comment 也當成 node。會將整份文件當成 current document,所以使用 Elements()方法時,就會讀到 r 這一個子節點了。

Linq to xml 的強制轉換好處

範例

以下範例,說明將 date 的attribute 取出後,轉成 datetime 的過程。注意 invoice 這個 element 可能沒有date這個attribute
      string xml = @"";
      XElement el = XElement.Parse(xml);
      string strDate = el.Element("invoice").Attribute("date").Value;
      DateTime? date = null;
      if (!string.IsNullOrEmpty(strDate))
        date = DateTime.Parse(strDate);
      Console.WriteLine(date);
這樣一來,程式碼就顯的有些長而不易維護。 因此,在linq to xml 的設計時,有考慮到這一點。

使用強制轉換的範例

      string xml = @"";
      XElement el = XElement.Parse(xml);
      
      DateTime? date = (DateTime?) el.Element("invoice").Attribute("date");
      
      Console.WriteLine(date);
注意到可直接使用 DateTime? date = (DateTime?) … 來取得可能為 null 的 attribute。
這樣的方法可適用於 element

2008年9月16日 星期二

MSBuild and MSTest

Visual Studio 已經幫我們做了很多事情。但我們希望更進一步地做到組建自動化及測試自動化。

簡單的方法,就是下排程

cd Dir_Containing_Sln_File
msbuild solutionfile.sln /t:Rebuild /p:Configuration=Debug > build-log.txt

cd Dir_Containing_Sln_File
MSTest /testcontainer:testProject1\bin\Debug\test.dll /runconfig:localtestrun.testrunconfig > test-log.txt

呈現 ? 號

客戶詢問了一個問題:為何資料庫存入客戶公司抬頭,顯示出來卻是 ?XXX公司。

問題出在這個?號,是不是真的問號。

一般來說,前端作業人員在操作時,當然不會笨到輸入?號。
在查詢介面(SSMS)查出?號,不一定真的是問題,而是該字碼無法顯示字型,因此只能顯示?來代表。

例如,使用者輸入了一個使用者電腦中造字檔才有的字,但該特別的字卻只存在於該使用者的電腦。

那要如何判斷是不是特別的字呢?看起來與「?」是一模一樣的.

select '煊眾', cast('煊眾' as varbinary)
select N'煊眾', cast(N'煊眾' as varbinary)
select cast(0x3FB2B3 as varchar(20))
select cast(0x4A713E77 as nvarchar(20))

查詢的結果如下

image

由上面的例子知道,當無法儲存資料時,SQL Server 會將該字儲存成「?」。 也就是「煊」字會儲存成「?」(0x3F)

利用這個方法,發現了原來是資料庫設計時就設計錯誤了。中文字必須設計成 nchar(50) 或 nvarchar。而一旦使用 char(50) 或 varchar,就會發生儲存成?(0x3F)的問題。

2008年9月10日 星期三

DebugView

DebugView 是一個好用的 Debug 軟體。
在程式撰寫時,常常被教導要寫

Debug.Write("My debug inforation");

但是,除了在 Visual Studio 內可以看到 debug info 外,其他似乎一無用處了

在執行 .net 程式之前先啟動 DebugView,就可以看到 debug infomation 了

範例

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    for (int i = 0; i < 1000; i++)
    {
      Stopwatch w = new Stopwatch();
      w.Start();
      TestMethod();
      w.Stop();
      Debug.WriteLine(string.Format("count {0} elapsed {1} msec", i, w.ElapsedMilliseconds));
    }
  }

  private static void TestMethod()
  {
    string[] aColl = File.ReadAllLines(@"c:\a.txt");
    string[] bColl = File.ReadAllLines(@"c:\b.txt");

    var q = from a in aColl
            where !bColl.Contains(a)
            select a;

    foreach (var item in q)
    {
      Console.WriteLine(item);
    }
  }
}

結果如下圖
 image

2008/09/16

在Vista 上執行時,若未使用Run as administrator,會出現如下的錯誤

image

此錯誤將導致無法讀取asp.net 的 debug information

另外,除了必須 Run as administrator,也必須勾選 "Capture Global Win32" 這個選項,才能看到 asp.net 的 debug information

image

2008年9月1日 星期一

Linq to XML: Write NameSpace

Linq to XML 的寫作非常方便。以往要組一個 xml ,都必須從 XmlDocument 開始,現在使用 Linq to XML 就不用了。

範例1:

例如,下列程式碼

      
	//code 1
	XElement el = new XElement("r",
        new XElement("invoice",
          new XElement("invoiceNumber", "AB12345678"),
          new XElement("invoiceDate", DateTime.Now)
          )
        );
產生的 xml 結果如下
<r>
  <invoice>
    <invoiceNumber>AB12345678</invoiceNumber>
    <invoiceDate>2008-09-01T10:08:22.9666505+08:00</invoiceDate>
  </invoice>
</r>

這樣的方式極為簡便。

範例2:


如果要產生 namespace 呢? 以下的程式碼會產生 default namespace

      //code 2
      XNamespace ns = "http://www.compnay.com.tw/Invoice";
      XElement el = new XElement(ns + "r",
        new XElement(ns + "invoice",
          new XElement(ns + "invoiceNumber", "AB12345678"),
          new XElement(ns + "invoiceDate", DateTime.Now)
          ));
產生如下的 xml
<r xmlns="http://www.compnay.com.tw/Invoice">
  <invoice>
    <invoiceNumber>AB12345678</invoiceNumber>
    <invoiceDate>2008-09-01T10:16:48.2077244+08:00</invoiceDate>
  </invoice>
</r>

範例3:

也可以使用如下的寫法,但效能會差一些,原因 runtime 要計算用了哪些 namespace。
      //code 3
      XNamespace ns = "http://www.compnay.com.tw/Invoice";
      XElement el = new XElement("{http://www.compnay.com.tw/Invoice}r",
        new XElement("{http://www.compnay.com.tw/Invoice}invoice",
          new XElement("{http://www.compnay.com.tw/Invoice}invoiceNumber", "AB12345678"),
          new XElement("{http://www.compnay.com.tw/Invoice}invoiceDate", DateTime.Now)
          )
        );
結果是一樣的
<r xmlns="http://www.compnay.com.tw/Invoice">
  <invoice>
    <invoiceNumber>AB12345678</invoiceNumber>
    <invoiceDate>2008-09-01T10:20:56.8615067+08:00</invoiceDate>
  </invoice>
</r>

這樣的寫法雖然較容易了解,但較不容易修改,我個人不建議這樣的寫法。

範例4:


另外,注意到雖然我們沒有說要寫成 default namespace,但Linq to XML 會「自動」地視狀況修改。
我們可以使用 XAttribute 來使用 namesapce prefix。如下程式碼。

      //code 4 
      XNamespace ns = "http://www.compnay.com.tw/Invoice";
      XElement el = new XElement("r",
        new XAttribute(XNamespace.Xmlns + "i", "http://www.compnay.com.tw/Invoice"),
        new XElement("invoice",
          new XElement("invoiceNumber", "AB12345678"),
          new XElement("invoiceDate", DateTime.Now)
          )
        );
結果如下。注意到這樣的方法,可以很方便地產生了一個 namespace prefix i,但卻沒有任何節點使用到這個 prefix
<r xmlns:i="http://www.compnay.com.tw/Invoice">
  <invoice>
    <invoiceNumber>AB12345678</invoiceNumber>
    <invoiceDate>2008-09-01T10:27:11.773833+08:00</invoiceDate>
  </invoice>
</r>

範例5:

使用下面的程式碼,就不難了解 xml namespace 的特殊

      //code 5
      XNamespace ns = "http://www.compnay.com.tw/Invoice";
      XElement el = new XElement("r",
        new XAttribute(XNamespace.Xmlns + "i", "http://www.compnay.com.tw/Invoice"),
        new XElement("{i}invoice",
          new XElement("invoiceNumber", "AB12345678"),
          new XElement("invoiceDate", DateTime.Now)
          )
        );
產生的xml 如下。xml:i="http:....." 這個 namespace 宣告了,卻沒有任何節點使用。i 是一個 namespace 的值,是inovice節點的 default namespace。invoiceNumber 與 invoiceDate的 namespace 卻是 ""。一個簡單的xml,卻有三個 namespace,基本上是錯誤的設計。
<r xmlns:i="http://www.compnay.com.tw/Invoice">
  <invoice xmlns="i">
    <invoiceNumber xmlns="">AB12345678</invoiceNumber>
    <invoiceDate xmlns="">2008-09-01T11:02:47.1886207+08:00</invoiceDate>
  </invoice>
</r>

範例6:

如果要像 code 2,產生相同的結果,但不要每一個節點使用 ns + "xxx"這樣的方式可不可以呢?如下的程式碼會發生錯誤

    //code 6, fails
      XNamespace ns = "http://www.compnay.com.tw/Invoice";
      XElement el = new XElement("r",
        new XAttribute("xmlns", "http://www.compnay.com.tw/Invoice"),
        new XElement("invoice",
          new XElement("invoiceNumber", "AB12345678"),
          new XElement("invoiceDate", DateTime.Now)
          )
          );

錯誤的訊息為 Unhandled Exception: System.Xml.XmlException: The prefix '' cannot be redefined from '' to 'http://www.compnay.com.tw/Invoice' within the same start element tag. 看來,沒有一個好的方法,將其他的節點批次地設為 default namespace了。必須每一個節點都如 code 2 一般,將每一個節點,一個個設 namespace。也沒有辦法設哪一個 namespace 為 default namespace。

範例7:


最後的範例,是 prefix namespace, 請與範例2 對照

//code 7 
      XNamespace ns = "http://www.compnay.com.tw/Invoice";
      XElement el = new XElement(ns + "r",
        new XAttribute(XNamespace.Xmlns + "i", ns),
        new XElement(ns + "invoice",
          new XElement(ns + "invoiceNumber", "AB12345678"),
          new XElement(ns + "invoiceDate", DateTime.Now)
          )
          );
產生的 xml 結果如下
<i:r xmlns:i="http://www.compnay.com.tw/Invoice">
  <i:invoice>
    <i:invoiceNumber>AB12345678</i:invoiceNumber>
    <i:invoiceDate>2008-09-02T08:52:11.4240786+08:00</i:invoiceDate>
  </i:invoice>
</i:r>

Share with Facebook