2016年5月8日 星期日

使用16進位(hexadecimals)的時機與好處


寫程式的時候,有各種理由使用 16 進位。
其中一個理由是提醒工程師 - 我們要處理bits(用電腦世界的觀點),而不是處理數字(用人類世界的觀點)。使用 16 進位代表我們要處理相對低階的問題 (例如:記憶體資料的型式是必須被考慮)。

以下2個例子說明 16 進位使用時機:
[範例1]
 使用 bit shift (x << y) 比使用"乘法"更為清楚:
 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GB
 建立一個 16 KB 的array: var buffer = new byte[16 << 10]

[範例2]
 在 enum 當中, 項目是從 0 開始,而且依序加 1:
 使用 16 進位可以避免'負數'產生
 使用 16 進位可以使用bitwise AND (&) and OR (|) operators

 相關連結:
Why are flag enums usually defined with hexadecimal values: stackoverflow
使用16進位的好處是?? ptt

2016年5月4日 星期三

Implementation Patterns - Kent Beck (Chapter 6 State (狀態))


根據書上的建議,狀態的管理應該:
  1. 把相似的狀態放一起
  2. 把不同的狀態分離
要判斷兩個狀態(或是變數)的相似性,可以用下列方式:
  1. 他們在同一個 method 被用到
  2. 他們出現和消滅的時間相同
就以 MaterdiskAutoTool 的設計來當作例子看看設計...

[Case 1]:
MasterdiskAutoTool 會讀取 CM_Options.xml,來作為建立 Steps 的判斷。設計如下:


可以發現,Form 直接讀取 CM_Options.xml,然後再把 CM_Options.xml 的內容用參數的方式傳遞給其他的class (紅色框框的地方, 以及每個 Step)。
這種設計有下列缺點:
1.) 參數的傳遞路徑太長了,參數也太多了
2.) 這些參數透過Step的constructor傳進去變成step的全域變數,然後被Step裡面的 method 所使用,造成 method 裡面會有'全域變數'與'區域變數'交雜的情況,不容易閱讀
3.) 本質來說,Step 物件本身並不需要擁有這些參數,只有 Step 的 method 需要這些參數。'全域變數'與'區域變數'在此 method 的生命週期不一樣 (違反狀態管理原則)
4.) 因為Step 擁有CM_Options.xml的參數,當Step要增減這些參數時,必須修改很多地方  (:ex: constructor, 全域變數...)
5.) 不容易寫unit test (因為要準備太多參數當作input)

[Case 2]:
比較好的做法,是把讀取CM_Options.xml 的內容交給某個CMOptions class 處理 (橘色框框),然後物件向CMOptions class 要內容。
也就是說,CMOptions class只會出現在Step裡面的 method,Step 不會有CM_Options.xml 的內容變成全域變數 (生命週期一樣)。

修改後的設計如下:


這種設計可以避免上列的缺點,而且還有下列優點:
1.) CMOptions class只只需要出現在Step裡面的 method,Step不會有CM_Options.xml 的內容是全域變數 (因此 method 內的變數生命週期一樣)
2.) 一旦 CM_Options.xml 有變化,則不需要改變 MasterdiskAutoTool 的架構

[Case 3]:
由上面Case 2可以發現,幾乎所有Steps都必須使用 CMOptions class,造成關係太複雜。
可以再進一步改善一下: 讓 BaseStep 使用 CMOptions class,這樣每個Steps 就可以擁有 CMOptions class,免除每個Step自己使用CMOptions class。






Event 的意義

一個物件可以有三種東西:
  1. Property (ex: public string Name = "Peter";)
  2. Method (ex: public void Convert() {...} )
  3. Event (ex: public event EventHandler Click;)
一個物件擁有event, 它便可以通知其他物件"某件事情已經發生了"!
其中,一個 event 可以有多個 handlers 。 利用 event - handler 語法,我們可以建立一個 Notification system 。[註1] [註2]

若是要利用 event - handler 設計一個 Notification 機制,必須先確定下列3件事:
1. 誰有event,並且如何raise這個event
2. 誰handle這個event (也就是誰實作 callback function)
3. 兩者如何連結

以下用 Button 與 Form 做個例子:
1. Button 有 Clicked event,並且 user 透過 UI 來 raise 這個 event
2. Form 會 handle這個 Clicked event
3. Form 的 Constructor 實作把 button.Clicked 與 callback function 作連結 ( += )



再舉個 Queue 與 Producer 的例子:
當 Queue 有空位的時候,Queue物件 '通知' Producer物件進行 enqueue。(Queue 發出Event 給 Producer)
仿照 Button 與 Form 的設計如下:



1. Queue 有 NotifyToEnqueu 的 event,並且實作 OnNotifyToEnqueue() 讓 user (client code) 來raise 這個event,如下:

        public event EventHandler NotifyToEnqueue;

        protected virtual void OnNotifyToEnqueue()
        {
            if (NotifyToEnqueue != null) // null means no subscribe
            {
                NotifyToEnqueue(this, EventArgs.Empty); // raise event
                
                // Or, you can write like:
                // NotifyToEnqueue.Invoke(thisEventArgs.Empty);
            }
        }

2. Producer 會 handle 這個 NotifyToEnqueu event,如下:

        private void Queue_NotifyToEnqueue(object sender, EventArgs e)
        {
            m_MyQueue.Enqueue("aaa");
        }

3. Producer 的 Constructor 實作把 queue.NotifyToEnqueue 與 Queue_NotifyToEnqueue 作連結 (+= ), 如下:

public Producer(MyQueue queue)
        {
            m_MyQueue = queue;
            m_MyQueue.NotifyToEnqueue += Queue_NotifyToEnqueue; // subscribe
        }

所以,當呼叫 OnNotifyToEnqueue() 的時候, Producer物件就會進行 enqueue

[註1]
event 這個物件(ex: NotifyToEnqueue)的 invocations list 可用來儲存多種方法 (ex: Queue_NotifyToEnqueue方法)
要使用這些方法,只要呼叫 Invoke() 即可。 ex: NotifyToEnqueue.Invoke();

[註2]
An Event declaration adds a layer of abstraction and protection on the delegate instance.
This protection prevents clients of the delegate from resetting the delegate and its invocation list
and only allows adding or removing targets from the invocation list.

2016年5月1日 星期日

PlatformTarget 不同而造成的 Build warning

[Problem description]
在 Build C# 的過程當中,當某個 Project A 有 reference 到 Project B,如果這兩個 Projects 的 PlatformTarget 不同,則 MS-Build 結果有可能出現下列 Warning 訊息: (此 warning 不會出現在 VS-Build )

"warning MSB3270: There was a mismatch between the processor architecture of the project being build "MSIL" and the processor architecture of the reference "[internal C# dll]", "x86"."


[Root cause]
這是因為 兩個 Projects 的 Debug platform (或是 Release platform) 不相同所造成。如下圖所示:
MasterDiskAutoTool.Test.csproj 是一個 Unit test project,它會 reference 到 AutoTool.csproj。
然而,MasterDiskAutoTool.Test.csproj 的 release build platform 為 Any CPU,與 AutoTool.csproj 的 release build platform 的 x86 不相同。
因此,MS-Build 的結果有可能產生 Warning



[Solution]
把 MasterDiskAutoTool.Test.csproj 的 release build platform 改為 x86 (與 AutoTool.csproj 一樣),然後 MS-Build 就沒有Warning 了。
如下圖所示:




**PS: 另外一個因為Platforms 不同所造成的執行時期錯誤,請參閱 這裡


相關連結:
VS compile warning - “mismatch between processor architecture” stack overflow

2016年4月28日 星期四

Functional requirement & Non-Functional requirement

要寫好一份'軟體需求規格文件' (SRS) 不容易。 '使用者需求'是它的Input,'軟體設計文件' 是它的Output。
[User Requirements] --> [SRS] --> [SW Design (High Level Design + Detailed Level Design)]

什麼樣的SRS才是好的? 下列四點是個不錯的判斷依據:
1. 提供回饋給user。最好的回饋方式就是讓User看到! 比如UI、Output files, Tools
2. 用軟體的觀點來看問題,把問題分解成幾個SW components。What (SW components, Tools) should be enhanced/changed/updated/removed/created...
3. SRS是SW design 的Input。SRS 只需定義'規格' (What),它是一份合約。實作方式則定義在SW Design (How)。
4. SRS 必須用'外顯'、'可量測' 的角度來撰寫,好讓Tester判斷正確性。

看了許多的 SRS, 節錄出下列幾個重要方向:
Functional requirement: 定義系統要做的事 (What the system does)
Business Rules
External Interfaces (ex: UI, output files...)
Audit Tracking (ex: Logging)
Transaction corrections, adjustments and cancellations
Administrative functions
Authentication
Authorization levels
Certification Requirements
Reporting Requirements
Historical Data
Legal or Regulatory Requirements

Non-functional requirements: 定義系統的能力 (What the system's capability - the power to do it)
Reliability - 可以依據既定規格而持續運行的能力
Serviceability
Availability
Performance – for example Response Time, Throughput, Utilization, Static Volumetric
Scalability -  To handle a growing amount of work (系統處理工作量的成長)
Capacity - 系統可以產出(或提供服務)的最大量
Recoverability
Maintainability: 維持原有的形式,萬一發生錯誤也可以回復到原有的形式
Security
Regulatory
Manageability
Environmental
Data Integrity
Usability
Interoperability


Non-functional 影響層面有:
  • Reliability: Improvement in number of hits [hits/ syst yr] (Hit 次數)
  • Serviceability: Improvement on Maintenance and in MTTR (diagnostics, access, repair and/or recovery) repairing of system [hrs] (維修, 回復 時數)
  • Availability: Improvement in hours of downtime both USD (Unscheduled) as well as SD (Scheduled) [hrs/syst yr] (Downtime 時數)
  • Manufacturability: Improvement in CT (Cycle Time) or A-time in production [hrs/ syst]
  • Safety: Improvement of probability of incidents occurring
  • Usability: Functional improvement of machine for operator
  • Machine Performance: Non-functional improvement of customer requirements



2016年4月18日 星期一

The Benefits of Isolating Applications (隔離應用程式的優點)

本文先說明應用程式'隔離'(isolation)的概念, 接著再說明.NET 實作'隔離'的方式。

[應用程式'隔離'的概念]
在過去,Process boundaries 被用來達到 app 隔離的效果。App 必須被隔離是因為'記憶體位址' (memory address) 是 process 相關 (process-relative)。也就是說,這個 process 使用的 memory address 不能被另一個 process 使用。
因為 memory address 不能共用,因此,兩個 processes 之間也不能直接互相呼叫,也就是不能直接存取程式碼 (必須使用 proxies, 例如 Inter-process communication)。

而現在,managed code 在被執行之前,必須通過'權限驗證'(permission verification)才能被執行 (除非  administrator 已經授權 skip此驗證 )。權限驗證主要是驗證下列兩件事情:
  1. 程式碼是否可能嘗試存取無效的memory address
  2. 程式碼是否可能無法正常運行
如果上述的驗證通過了,此 managed code 稱為 型別安全 (type-safe) [** 注意: 這裡指的 type safe,指的是memory type safety (參閱 這裡),而不是廣義的type safe定義 (例如 integer 與 string 的型別)]。這種'權限驗證'提供了 CLR 具有與過去Process boundaries 相同的app 隔離效果,而且效率更好!

上述的說明,概念上解釋了隔離的重要性。接下來,說明.NET 如何實作隔離

[.NET實作'隔離'的方式]
在.NET中,使用 AppDomain 可以讓 CLR 提供 app 的隔離效果。CLR 允許單一個process裡面執行多個AppDomains,也就是說,單一個processe可以執行多個 apps,如此便增加了 server 端的 scalability。

AppDomain 具有下列優點:
  1). 在某一應用程式中的錯誤不會影響其他應用程式。由於 type-safe 程式碼不會導致記憶體錯誤,所以使用 AppDomain 可確保在某一Domain中執行的程式碼不會影響處理序(process)裡面的其他應用程式。

  2). 可以停止個別應用程式而不需停止整個處理序(process)。使用 AppDomain 可以卸載在單一個 app 中執行的程式碼。[** 注意: 這裡指的是 "卸載一個完整的AppDomain",而不是卸載一個DLL) (卸載AppDomain的方法, 參閱 這裡 ]

  3). 可以防止不同 AppDomain 中對於物件的呼叫。在不同定義域之間傳遞的物件必須用'複製方式傳遞'或由 'Proxy' 存取。
    3-1). 如果物件是複製的,那麼對該物件的呼叫就是區域呼叫。也就是說,呼叫端(caller)和被參考的物件是在同一個AppDomain。
    3-2). 如果物件是透過 Proxy 存取,那麼對物件的呼叫便是遠端呼叫(call remotely)。在這種情況下,呼叫端(caller)和被參考的物件(referenced object)是在不同的應用程式定義域中。跨定義域呼叫(Cross-domain call)是使用與兩個處理序(process)或兩部電腦之間呼叫相同的'遠端呼叫基礎結構'(remote call infrastructure)。因此,所參考之物件的中繼資料 (Metadata) 必須在這兩個AppDomain 中都能使用,才能讓該方法呼叫被 JIT 適當地編譯。如果caller 無權存取所呼叫之物件的中繼資料(Metadata),則編譯可能會失敗,並擲回型別 System.IO.FileNotFound 的例外狀況。

4). 程式碼的行為範圍(behavior scope)是由該應用程式決定。換言之,AppDomain會提供 app 版本原則、它所存取的遠端組件的位置,以及有關哪裡可以載入組件的資訊。



相關連結:
The Benefits of Isolating Applications: MSDN

2016年4月17日 星期日

Regex Tester (Regular Expressions 線上debug pattern)

發現一個不錯的網站, 可以直接在線上 debug 與 test Regular Expressions的 pattern,而且它還有解釋 pattern 的每個意義:

  regular expressions 101



先關連結:
Regular Expression的學習筆記