Visual Basic OOP Part 2 -- 繼承與介面之靈光一閃

之前之Allen大大聊天時,有討論到「繼承與介面」一些內容,雖然我之前有寫過「繼承Inherit、介面Interface的使用時機及建議」一文,但昨晚在帶著大小寶貝散步時,突然靈光一閃,又有新的認知。

首先,我們要了解在 .NET Framework只能單一繼承,也就是每一個類別(Class)只能繼承一個基礎類別(BaseClass),但深度沒有限制。

A Class --> B Class --> C Class --> D Class
             --> E Class --> F Class
                                --> G Class

但介面可以有多重繼承,也就一個類別可同時繼承多個介面,以 List類別為例,

Public Class List(Of T) _
 Implements IList(Of T), ICollection(Of T),  _
 IEnumerable(Of T), IList, ICollection, IEnumerable

它同時實作了六個介面,

  1. IList(Of T)
  2. ICollection(Of T)
  3. IEnumerable(Of T)
  4. IList
  5. ICollection
  6. IEnumerable

那我靈光一閃什麼呢?

我們需要的組織或架構是「樹狀, Tree」,那就可以規劃使用繼承。繼承的方向單純,走的是「Top-Down」。例如,我們看一下「人的科學分類」,這就很合適使用繼承。

我們需要的織組或架構是「網狀, Net」,那就可以規劃使用介面。介面是多方向,走的是「Matrix,矩陣」、「放射狀」。



以上就是我小小一閃的心得。

Visual Basic OOP Part 2 -- 封裝補充

經過Allen大大的指教,又對OOP多了一分了解。以下是Allen指教的內容:



封裝,雖然有講到將演算法隱藏起來,但重點並不是因為編譯了,看不到,而是因為將它宣告成private,所以,同一支dll projrct的各class裡,也許叫用不到另一class的某資訊,重點不在於因為它編譯過了,而是因為它被宣告成private,另外, 封裝的另一個解釋,是class裡,也許有某個行為日後一定會一直改變,那麼,便可以將這行為抽離開,讓它單獨變成一個class,如此一來,主要的class就不必一直被變更code,"將可能變動的因素抽離走,也叫封裝"。


學徒與師父的功力果然不一樣。

Entity Framework 4.1 新功能介紹資源

剛看到外國一本 Entity Framework 書作者文章,作者很清楚的整理一系統的文章及影片,文章所說的內容基本上跟影片是一樣的,整理的很好,留下來當個記錄。( 國內什麼時候才有EF的專書! )


EF 系列文章:

  • WPF使用Entity Framework做資料綁定
  • ASP.NET MVC 3使用Entity Framework 4.1的Code First
  • ASP.NET MVC 3使用Entity Framework 4.1的Model First
  • ASP.NET MVC 3使用Entity Framework 4.1的Database First
就算看不懂文章( 英文 ),我還是建議看他的影片,就算聽不懂他在說什麼,看他怎麼做,他實作的步驟還算簡單。

julie lerman 個人網站上也有許多Entity Framework的資源,如果有在學習或使用Entity Framework的人可加入參考及留意。

雖然大家都在談Entity Framework 4.1的Code First,有興趣的人還是可以了解一下。我想尤其是有在開發"手機"方面應用程式的人。基本上,"我"資料庫的毒中的太深,所以以Database First為主,如果寫一些測試Code,臨時需要Model,應該會考慮使Model First。

ASP.NET MVC 3 新HTML Helper筆記(1) -- WebGrid Class

事情永遠是自己想太多。 -- By KKBruce
我在三月時寫了 ASP.NET MVC 3概觀正體中文版及五月時寫了 ASP.NET MVC 3 RTM Tools Update for CHT,算是把整個 ASP.NET MVC 3 所有更新的內容整個灠覽一篇,但其中一直有一點一直讓我心癢癢的,就是在ASP.NET MVC 3概觀正體中文版裡關於 Razor 提供新的 HTML Helper (Chart, WebGrid, Crypto, WebImage, WebMail),為什麼好東西不和好朋友分享呢?因為相關文章都把這些 ASP.NET MVC 3 新 Helper 和 Razor 放在一起使用,造成我的誤解,以為要使用這些新 Helper 必須要和 Razor 一起才能使用,後來發現我錯了。

在查詢 ASP.NET MVC 3參考 (英文) (ASP.NET MVC 3 Reference) 時,發現了一些好玩的事,怎麼我找不到任何關於 ASP.NET MVC 3 新增的 HTML Helper 的相關類別,ASP.NET MVC 3 只新增了一個 System.Web.Mvc.Razor 類別,其他四個與ASP.NET MVC 2 一樣,經查詢,這些 ASP.NET MVC 3 的新 HTML Helper 是在 System.Web.Helper 類別,而且此類別是位於 Extensions 之下,而非 Frameowrk,如果你專案是開 ASP.NET MVC 3 專案,預設是會加入參考。


都已經預設加入參考,那我可不可以在非 Razor 下使用它呢?答案當然是 No problem。

MVC 3 New HTML Helper 1 -- WebGrid

在MVC 3之前(不就 MVC 2呢?),如果我們要對一個表格(或說 Model)要做一些進階的效果,例如,我們想做分頁,或除了分頁還是做分頁的Ajax效果…?網路上都有範例可以參考,例如使用jqGrid,或使用Martijn Boland實作的 MvcPaging函式庫,或大陸有位杨涛所實作 MvcPager分页控件等,都是很棒的解決方案。

現在我們能有還能使用 ASP.NET MVC 3 所提供的 WebGrid。我們使用預設的 HomeController 的 Index Action來示範,我們先清除Index Action的程式碼及刪除Index.aspx這個View。

Function Index() As ActionResult

    Dim nw As New NorthwindEntities
    Dim customers = nw.Customers().ToList()

    Return View(customers)
End Function

Index()動作很簡單,使用Entiry Framework來傳入Customer資料表。

然後我們使用強型別(Customers)來建置 Index.aspx 這個View,請直接拉到最後面

<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage(Of IEnumerable (Of MvcWebGrid.Customers))" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>Index</h2>

<p>
    <%: Html.ActionLink("Create New", "Create") %>
</p>
<table>
    <tr>
        <th>
            CompanyName
        </th>
        <th>
            ContactName
        </th>
        <th>
            ContactTitle
        </th>
        <th>
            Address
        </th>
        <th>
            City
        </th>
        <th>
            Region
        </th>
        <th>
            PostalCode
        </th>
        <th>
            Country
        </th>
        <th>
            Phone
        </th>
        <th>
            Fax
        </th>
        <th></th>
    </tr>

<% For Each item In Model %>
    <% Dim currentItem = item %>
    <tr>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.CompanyName) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.ContactName) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.ContactTitle) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.Address) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.City) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.Region) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.PostalCode) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.Country) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.Phone) %>
        </td>
        <td>
            <%: Html.DisplayFor(Function(modelItem) currentItem.Fax) %>
        </td>
        <td>
            <%: Html.ActionLink("Edit", "Edit", New With {.id = currentItem.CustomerID}) %> |
            <%: Html.ActionLink("Details", "Details", New With {.id = currentItem.CustomerID}) %> |
            <%: Html.ActionLink("Delete", "Delete", New With {.id = currentItem.CustomerID}) %>
        </td>
    </tr>
<% Next %>

</table>

<h2>WebGrid (有分頁及排序)</h2>
<p>
<%: New WebGrid(Model).GetHtml()%>
</p>
</asp:Content>

我們使用List Scaffold建置出Index.aspx這個View,在最後面新增一行「<%: New WebGrid(Model).GetHtml()%>」,讓List Scaffold 所產生效果與 WebGrid 產生的效果比較一下,


上半部分,原始的 List 是使用 For Each來一筆一筆跑出資料,利用 79行程式碼跑出一個超長的Table。下半部分,WebGrid 產生的 Table ,預設有含有「排序及分頁」兩大功能,而且全部只有一行程式加一個參數,有沒有感動到。

WebGrid 能做的不只有這樣,我們針對傳入的Model及Html控制到很細項目,例如,針對Model的控制,

例如,

<%: New WebGrid(Model, defaultSort:="ContactName", rowsPerPage:=5).GetHtml()%>

預設我想針對「ContactName」來排序,且每一分頁使用 5 筆資料。另外,由於預設參數很多,建議可以使用「指名參數(:=, 冒號加等號)」的寫法來設定。能不能有分頁,使用「CanPage」,能不能排序,使用「canSort」,實在很方便。


再來,我們再針對HTML (table) 來進行設置,


這邊我們改寫一下Code,先產生WebGrid的執行個體,這樣才有辨法傳入GetHtml()方法當參數:

<% Dim wg = New WebGrid(Model, defaultSort:="CustomerID", rowsPerPage:=5)%>
<%: wg.GetHtml(columns:=wg.Columns(
                        wg.Column("Address", "地址"),
                        wg.Column("City", "城市"),
                        wg.Column("CompanyName", "公司")))%>

我們看一下結果,


我們希望產生三個Column,而且對應欄位顯示中文。那麼我們可不可以產生CURD呢?還是那一句話「No problem!」,不然就像桌子少一支腳,怎麼行。

<% Dim wg = New WebGrid(source:=Model, defaultSort:="CustomerID", rowsPerPage:=5)%>
<%: wg.GetHtml(columns:=wg.Columns(
                           wg.Column(format:=Function(item) Html.ActionLink("編輯", "Edit", New With {.id = item.CustomerID})),
                           wg.Column(format:=Function(item) Html.ActionLink("刪除", "Delete", Nothing,
                                                                            New With {.onclick =
                                                                                      String.Format("deleteRecord('CustomerID', '{0}')", item.CustomerID),
                                                                                      .href = "JavaScript:void(0)"})),
                           wg.Column("Address", "地址"),
                           wg.Column("City", "城市"),
                           wg.Column("CompanyName", "公司"))
                        )%>

我們產生兩個新欄位,我們透過匿名函式來產生對應的Link,編輯及刪除所產生對應的HTML Code,

<!-- 編輯 -->
<a href="/Home/Edit/ALFKI">編輯</a>
<!-- 刪除 -->
<a onclick="deleteRecord('CustomerID', 'ALFKI')" href="JavaScript:void(0)">刪除</a>

對照HTML Code來看,就比較能了解我們設定那些參數的用意。再來,我們在進行分頁時,可以看得出來是使用 Postback 的方法,如果要使用 Ajax 的方法,WebGrid也是支援的,

<div id="DivCustomer">
<% Dim wg = New WebGrid(source:=Model, defaultSort:="CustomerID", rowsPerPage:=5, ajaxUpdateContainerId:="DivCustomer")%>
<%: wg.GetHtml(columns:=wg.Columns(
                           wg.Column(format:=Function(item) Html.ActionLink("編輯", "Edit", New With {.id = item.CustomerID})),
                           wg.Column(format:=Function(item) Html.ActionLink("刪除", "Delete", Nothing,
                                                                            New With {.onclick =
                                                                                      String.Format("deleteRecord('CustomerID', '{0}')", item.CustomerID),
                                                                                      .href = "JavaScript:void(0)"})),
                           wg.Column("Address", "地址"),
                           wg.Column("City", "城市"),
                           wg.Column("CompanyName", "公司"))
                        )%>

我們在第一行的程式碼內指定屬性「ajaxUpdateContainerId:="DivCustomer"」,意用是我們要使用Ajax,更新到「HTML元素id為『DivCustomer』內」,這裡有兩點需注意,
  1. 需把WebGrid包在指定的HTML元素內;
  2. 需引用jQuery;

最後我們來討論「分頁」效能問題,其實也不用討論,直接看它所傳入SQL Server的T-SQL:

SELECT 
[Extent1].[CustomerID] AS [CustomerID], 
[Extent1].[CompanyName] AS [CompanyName], 
[Extent1].[ContactName] AS [ContactName], 
[Extent1].[ContactTitle] AS [ContactTitle], 
[Extent1].[Address] AS [Address], 
[Extent1].[City] AS [City], 
[Extent1].[Region] AS [Region], 
[Extent1].[PostalCode] AS [PostalCode], 
[Extent1].[Country] AS [Country], 
[Extent1].[Phone] AS [Phone], 
[Extent1].[Fax] AS [Fax]
FROM [dbo].[Customers] AS [Extent1]

這不是件好事,它不會先做分頁再回傳,而是每一次回傳所有資料再進行分頁及排序等動作。( 因為你原始的 Action 裡的資料,本來就沒進行分頁 )

這時候你有兩種解決方法,一是前面所提使用Martijn Boland實作的 MvcPaging函式庫,兩是參考這篇「Efficient Paging with WebGrid Web Helper - ASP.NET MVC 3」所提供的方法。

解決 WebGrid 分頁問題 -- 使用MvcPaging函式庫

  1. 引用MvcPaging專案,然後編譯;
  2. 在HomeController.vb裡 Imports MvcPaging
  3. 在HomeController.vb 加上page 參數,撰寫分頁程式,
  4. Index.aspx裡Inherits的Of IEnumerable改為Of MvcPaging.IPagedList
  5. 在Index.aspx撰寫分頁程式

HomeController.vb
Function Index(page As Integer?) As ActionResult
    Dim nw As New NorthwindEntities
    'Dim customers = nw.Customers().ToList()
    Dim customers = nw.Customers().OrderBy(Function(c) c.CustomerID).ToPagedList(If(page >= 1, page - 1, 0), 5)

    Return View(customers)
End Function

Index.aspx:在原WebGrid程式碼之加上
<%= Html.Pager(Model.PageSize,Model.PageNumber ,Model.TotalItemCount) %>

我們利用MvcPaging來分頁,因為MvcPaging設定的分頁數量與我們設定WebGrid的「rowsPerPage:=5」一樣,所以我們只是單純的把WebGrid當成一個顯示用的物件,另外ajaxUpdateContainerId設定也可以移除了,因為我們不是使用 WebGrid 來進行分頁。

解決 WebGrid 分頁問題 -- 使用Malcolm Sheridan MVP提供的方案(修正運作無誤版)

原網頁上提供的 jQuery 無法正常運作,我修正了一下,在 Chrome 11.x, Firefox 4.x, IE 9 都能正常運作。

  1. 在 HomeController.vb 匯入 System.Web.Helpers
  2. 新增兩個Action,一個用來檢示,一個用來處理分頁要求
  3. 撰寫 jQuery 分頁程式碼
  4. 在檢示View中,引用 jQuery 程式碼。
建立兩個Action,撰寫相關程式碼,WebGridPager 產生 View ( WebGridPager.aspx )
''' <summary>
''' 檢示結果
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Function WebGridPager() As ActionResult
    Return View()
End Function

''' <summary>
''' 處理分頁
''' </summary>
''' <param name="page">第幾頁</param>
''' <returns>JSON物件</returns>
''' <remarks></remarks>
Function EfficientPaging(page As Integer?) As ActionResult
    ' 使用 Northwind 資料庫
    Dim nw As New NorthwindEntities
    Dim skip As Integer = If(page.HasValue, page.Value - 1, 0)
    ' 以 Customers 資料表為例
    Dim data = nw.Customers().OrderBy(Function(c) c.CustomerID).Skip(skip * 5).Take(5).ToList()
    ' 傳入資料
    Dim grid As New WebGrid(data)
    ' 產生Table Tag
    Dim htmlstring = grid.GetHtml(tableStyle:="webGrid",
                                  headerStyle:="header",
                                  alternatingRowStyle:="alt",
                                  htmlAttributes:=New With {.id = "DataTable"})

    ' 回傳JSON物件
    ' 無安全性問題,可使用 HTTPGET
    Return Json(New With {.Data = htmlstring.ToHtmlString(),
                          .Count = nw.Customers.Count() / 5},
                JsonRequestBehavior.AllowGet)
End Function

我們新增一個 Pager.js ,撰寫相關 jQuery Code (修正原程式碼分頁無法運作的問題)

$(function () {
    $.getJSON('/Home/EfficientPaging', null, function (html) {
        // 把JSON取得動態 WebGrid 的HTML加入body
        $('body').append(html.Data);

        // 建立 footer
        var footer = createFooter(html.Count);
        
        // 動態為分頁 link 加上 click 事件       
        $('#DataTable tfoot a').live('click', function (e) {
            e.preventDefault();

            // 取得文字,當成 page 參數回傳
            var data = {
                page: $(this).text()
            };

            // 當有 click 時,重新取得分頁資料
            $.getJSON('/Home/EfficientPaging', data, function (html) {
                // 先將舊Table移除
                $('#DataTable').remove();
                // 把JSON取得動態 WebGrid 的HTML加入body
                $('body').append(html.Data);

                // 預設Table沒有tfoot,先加上
                $('#DataTable thead').after('');
                // 加入link到tfoot
                $('#DataTable tfoot').append(footer);
            });
        });
    });
});

function createFooter(count) {
    
    // 預設Table沒有tfoot,先加上
    $('#DataTable thead').after('');

    // 產生link
    var footer = "";
    for (var i = 1; i < (count + 1); i++) {
        footer = footer + '<a href="#">' + i + '</a>&nbsp;';
    }

    // 加入link到tfoot
    $('#DataTable tfoot').append(footer);

    return footer;    
}   

在 WebGridPager.aspx 的 View 中加入引用 Pager.js (需 jQuery,如果 MasterPage 已經引用,只需引用 Pager.js ) 即可。

<body>
  <script src="../../Scripts/jquery-1.6.1.min.js" type="text/javascript"></script>
  <script src="../../Scripts/Pager.js" type="text/javascript"></script>
</body>

那上兩種方法都是使用到 LINQ 的 Skip() 與 Take() 來進行分頁,些種分頁方法能很有效率的只取回相關的記錄,你以 5 筆為一頁,它一次就只會拿回 5 筆,這讓我們可以考慮把 WebGrid 使用在大量資料處理上。

WebGrid 參數

1 Source WebGrid指定資料來源。
2 columnNames Source中欄位的集合。
3 defaultSort 預設序排欄位名稱
4 rowsPerPage 每頁幾筆資料
5 canPage 是否允許分頁,預設true
6 canSort 是否允許排序,預設true
7 ajaxUpdateContainerId 指定Ajax更新的HTML元素ID
8 fieldNamePrefix 給WebGrid產生的參數欄位加前綴字串,例如,未加時"?sort=CustomerID&sortdir=ASC",加一個前綴"grid_"後,"?grid_sort=CustomerID&grid_sortdir=ASC”
9 pageFieldName 自定義分頁QueryString參數名稱
10 sortFieldName 自定義排序QueryString參數名稱
11 sortDirectionFieldName 自定義排序QueryString參數名稱

參考


增強Visual Studio中Visual Basic的Refactoring功能

在Visual Studio中的Visual Basic一直少了樣功能,而這個功能在C#中是很基礎的功能,就是Refactor (重構),而且很好玩的是,Visual Studio也建議Visual Basic使用者去安裝第三方軟體來加強Refactor的功能,那他們建議是的那一套呢?原來是鼎鼎大名的 DevExpress,不過他們的套裝軟體價格不低。

後來找後一會兒,原來,DevExpress也有出 Express 版本軟體 (不太好找),就像 SQL Server Express / Visual Studio Express 一樣的意思,你可以在 IDE Productivity 分類下(看網址 Visual_Studio_Add-in 很明顯吧),找到一個 Free Editions 連結,你能下載到三套 Express 免費版本的擴充工具,基中的「Free Coding Assistance Add-in」就是我們要的 CodeRush Xpress,它是CodeRush的子集,但提供了我們所需要的基本功能:


(引用自DevExpress)

加強的特色:
  1. 選取加強
    五個選擇功能的加強。
  2. 程式碼巡覽加強
    七個程式碼巡覽的加強。
  3. 宣告加強
    許多宣加方面的加強。
重構的加強 ( C# and VB ):
  1. 變更參數宣告
  2. 陳述式
  3. 宣告和初始化
  4. 運算式
  5. Lambda運算式和匿名函式
  6. 迴圈和段落
  7. 屬性和欄位
  8. 移動方法
  9. 資源檔與字串
  10. 型別
  11. 能見度
DevExpress 都有提供詳細文件,請自行參考。

參考:

LINQ Dynamic Query

無意中看到關於LINQ動態查詢的文章:Dynamic LINQ LibraryDynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)

它提到LINQ在使用動態查詢時一些狀況,例如,當我們在 Northwind 的 Products 依 CategoryId = 5 來進行各種條件的查詢時,程式會變的很麻煩。而且重點在,如果需求條件改變時,你就必須重新改寫一次程式碼,這樣很不方便。

最後它提出一個好用的解決辦法,而且此辦法在你安裝Visual Studio時就已經送給你了。它利用Visual Studio裡的VB / C#範例程式碼裡的一支程式來擴充LINQ。

VB / C# 範例路徑:C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\1028 (Win7 x64) 之下,有VC2010Samples.zip及VBSamples.zip。我解開VBSamples.zip後,找到 *\VB Samples\Language Samples\LINQ Samples\DynamicQuery,以可以找到這支Dynamic.vb範例程式。

你直接引用到Visual Basic專案會有問題,我修正內容:



下載後直接引用即可擴充LINQ的功能。在 *\VB Samples\Language Samples\LINQ Samples\DynamicQuery\Documentation 下有一份 Dynamic Expression API.htm,如果你聽要進深入了解解使用Dynamic Query LINQ,建議細讀之。

自行撰寫ASP.NET MVC中路由(Routing)的條件約束

在ASP.NET MVC中我們可以針對 routes.MapRoute() 方法加上限制條件。

步驟1:新增一個Class,然後實作IRouteConstraint 介面,IRouteConstraint 介面只定義一個Match() 方法

YearMonthDayRouteConstraint.vb
Public Class YearRouteConstraint
    Implements IRouteConstraint

    ''' <summary>
    ''' 實作IRouteConstraint介面Match方法
    ''' </summary>
    ''' <param name="httpContext">包含有關個別 HTTP 要求的 HTTP 專屬資訊。</param>
    ''' <param name="route">提供用以定義路徑和取得路徑相關資訊的屬性及方法。</param>
    ''' <param name="parameterName">索引鍵/值組之不區分大小寫的集合</param>
    ''' <param name="values"></param>
    ''' <param name="routeDirection">表示 ASP.NET 路由是正在處理來自用戶端的 URL 還是正在產生 URL。</param>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Function Match(httpContext As System.Web.HttpContextBase,
                          route As System.Web.Routing.Route,
                          parameterName As String,
                          values As System.Web.Routing.RouteValueDictionary,
                          routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match

        ' IncomingRequest, 正在處理來自用戶端的 URL。
        ' InvariantCulture, 取得與文化特性無關的 (不變的) System.Globalization.CultureInfo。
        If (routeDirection = Routing.RouteDirection.IncomingRequest) AndAlso
           (parameterName.ToLower(System.Globalization.CultureInfo.InvariantCulture) = "year") Then
            Try
                Dim year As Integer = Convert.ToInt32(values("year"))
                If (year >= 1990) AndAlso (year <= 2100) Then
                    Return True
                Else
                    Return False
                End If
            Catch ex As Exception
                Return False
            End Try
        End If

        Return False
    End Function
End Class

Public Class MonthRouteConstraint
    Implements IRouteConstraint

    Public Function Match(httpContext As System.Web.HttpContextBase, route As System.Web.Routing.Route, parameterName As String, values As System.Web.Routing.RouteValueDictionary, routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match
        If (routeDirection = Routing.RouteDirection.IncomingRequest) AndAlso
           (parameterName.ToLower(System.Globalization.CultureInfo.InvariantCulture) = "month") Then
            Try
                Dim month As Integer = Convert.ToInt32(values("month"))
                If (month >= 1) AndAlso (month <= 12) Then
                    Return True
                Else
                    Return False
                End If
            Catch ex As Exception
                Return False
            End Try
        End If

        Return False
    End Function

End Class

Public Class DayRouteConstraint
    Implements IRouteConstraint

    Public Function Match(httpContext As System.Web.HttpContextBase, route As System.Web.Routing.Route, parameterName As String, values As System.Web.Routing.RouteValueDictionary, routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match
        If (routeDirection = Routing.RouteDirection.IncomingRequest) AndAlso
           (parameterName.ToLower(System.Globalization.CultureInfo.InvariantCulture) = "day") Then
            Try
                Dim month As Integer = Convert.ToInt32(values("month"))
                Dim day As Integer = Convert.ToInt32(values("day"))

                If (day < 1) Then
                    Return False
                End If

                Select Case month
                    Case 1, 3, 5, 7, 8, 10, 12
                        If (day <= 31) Then
                            Return True
                        End If
                    Case 2
                        If (day <= 28) Then
                            Return True
                        End If
                    Case 4, 6, 9, 11
                        If (day <= 30) Then
                            Return True
                        End If
                End Select
            Catch ex As Exception
                Return False
            End Try
        End If

        Return False
    End Function

End Class

YearMonthDayRouteConstraint.vb內含三個Class,都實作了IRouteConstraint 介面,程式很簡單,我們限制年( year )必須在1990 ~ 2100之間,月( month )必須在 1 ~ 12之間,日( day )有分大小月及二月的判斷。

步驟二:接下來我們在 Global.asax 加上一條路由規則,

routes.MapRoute("Archive",
                "Archive/{year}/{month}/{day}",
                New With {.contrller = "Archive", 
                          .action = "Index",
                          .year = UrlParameter.Optional,
                          .month = UrlParameter.Optional,
                          .day = UrlParameter.Optional},
                New With {.year = New YearRouteConstraint,
                          .month = New MonthRouteConstraint,
                          .day = New DayRouteConstraint})

重點在最後一個參數,我們傳入的路由規則的限制條件:

/Archive/2011/5/18 --> OK
/Archive/2011/2/29 --> Bad

此程式還有改進的空間,例如二月的潤月計算,會有29天,2012/2/29應該是要對的。除了寫Class之外,我們也可以直接使用RegEx 來設計限制條件,例如:

routes.MapRoute("Archive2",
                "Archive/{year}/{month}/{day}",
                New With {.contrller = "Archive",
                          .action = "Index",
                          .year = UrlParameter.Optional,
                          .month = UrlParameter.Optional,
                          .day = UrlParameter.Optional},
                New With {.year = "\d{4}",
                          .month = "\d{2}",
                          .day = "\d{2}"})

如果提供的是字串,路由會將字串視為規則運算式,並且呼叫 Regex 類別的 IsMatch 方法,藉此檢查參數值是否有效。這邊我們規則很簡單,year只要是4位數數字,month及day只要是2位數數字。

以上我們簡單示範如何幫路由設計、撰寫及使用限制條件,這不只是限制,也是安全性的提升。

ASP.NET MVC 3 RTM Tools Update for CHT

ASP.NET MVC 3 RTM Tools Update ( for CHT 版本)
  1. 必須先安裝ASP.NET MVC 3 RTM
  2. 首先請安裝 AspNetMVC3ToolsUpdateSetup.exe,然後再運行 AspNetMVC3ToolsUpdateSetup_CHT.exe 語言包安裝程式。
更新內容可參考 scottgu blog,主要有:
  1. Entity Framework 4.1 (包含Entity Framework Code First)正式版。在所有Mvc專案設定為預設參考。
  2. 内建的data scaffolding support支持Visual Studio的Add->Controller對話框。以Controller為單位,經過data scaffolding來產生所有View樣版。
  3. 新的HTML5 專案樣板。也同時引用Modernizr 1.7 Javacript Library。Modernizr可簡單檢測HTML 5在瀏覽器相容性。
  4. Intranet 專案樣板,使用Windows認證來驗證使用者。(Internet 專案使用Forms認證)。
  5. 新板本jQuery core, jQuery UI, jQuery Validation。(最近jQuery跑很快,手動或使用CDN更新應該更快些。)

Controller (Scaffold):

Entity Framework 4.1, 預設參考:

jQuery及Modernizr:

Intranet 專案及HTML 5:

轉貼--Visual Basic My 物件免費資料下載

Visual Basic My 物件 -下載

這是呂高旭老師提供書中章節的全文電子檔,My物件開發WinForm的作用比較大,如果你在WinFrom下常與System.IO 類別相處,那就不要錯過這是My 物件。

Visual Basic OOP Part 2 -- 多型補充

我們使用MSDN的範例來說明,以下圖片是使用繼承多型介面多型兩篇文章的程式碼修改而來,你可以對照程式碼來看,會更容易了解,先講解圖片。

繼承多型


  1. 我們在類別(BaseTax)中定義了一個可覆寫的方法Calculate(),此方法用來計算稅額。我們產生一個衍生類別(CityTax),繼承自BaseTax,然後覆寫它的Calculate()方法。
  2. 再來定義一個計算結果的方法,第一個參數是的重點,它的型別是BaseTax,然後在程式碼內呼叫它的Calculate()方法。
  3. 在主程式中,我們呼叫CalcResult()方法,第二段我們帶入的是CityTax型別,此時CalcResult()方法中的Item.Calculate()會自動去呼叫CityTax.Calculate()方法,而不是BaseTax.Calculate()方法。

這樣設計的好處是,你可以不斷加入繼承BaseTax類別的衍生類別,而不需要更動CalcResult()方法中的任何程式碼,Item.Calculate()不需要知道物件所屬的類別,只要該類別衍生自BaseTax。

介面多型


  1. 定義Shap(形狀)介面計算面積的方法。(介面無法實作Implementation)
  2. Ractangle類別實作Shap介面方法;Triangle類別實作Shap介面方法。
  3. 定義計算結果的方法ShapArea,第一個參數是重點,它的型別是Shap,然後在程式碼內呼叫它的方法item.CalculateArea()。
  4. 主程式呼叫ShapArea(),依傳入的型別不同(r 或 t),會各別呼叫Rectangle.CalculateArea()及Triangle.CalculateArea()方法。

多型定義

我們先看MSDN在Visual Basic中定義的多型
「多型」指的是您可以有多個交替使用的類別,即使每個類別是以不同的方式來實作相同的屬性或方法。多型對於物件導向程式設計來說相當重要,因為它允許您使用名稱相同的項目,而不需考慮當時使用物件的型別為何。例如,指定一個 "Car" 的基底類別,程式設計人員就可以使用多型為任何數目的衍生類別 (Derived Class) 定義不同的 "StartEngine" 方法。名為 "DieselCar" 的衍生類別所具有的 "StartEngine" 方法,可能與基底類別中名稱相同的方法完全不同。其他程序或方法可利用完全相同的方式來使用衍生類別的 "StartEngine" 方法,而不需考慮當時使用的 "Car" 物件是何種型別。
別外,MSDN在C #中定義的多型
透過繼承,類別可以當做多種型別使用,包括可以用來當做本身的型別、任何基底型別,或實作介面的任何介面型別。這稱為多型。在 C# 中,每種型別都是多型的。型別可以用來當做本身的型別或 Object 執行個體,因為任何型別都會自動將 Object 視為基底型別。

多型不但對衍生類別來說很重要,對於基底類別也十分重要。事實上,只要使用基底類別,就可以使用已轉換成基底類別型別的衍生類別物件。基底類別的設計人員可以預期在衍生型別中,基底類別的各方面有可能會改變。例如,假設有一個基底類別為汽車,那麼其所包含的行為就可能視汽車是休旅車或敞篷車而有不同。基底類別可以將這些類別成員標示成虛擬,讓代表敞篷車和休旅車的衍生類別覆寫該行為。

如果你看懂上面兩張圖及原始程式碼,這段MSDN解釋你應該就能看得懂。而且你能發現,多型與繼承有密不可分的關係。

Visual Basic OOP Part 2 -- 封裝補充

經過Allen大大的指教,又對OOP多了一分了解。以下是Allen指教的內容:

封裝,雖然有講到將演算法隱藏起來,但重點並不是因為編譯了,看不到,而是因為將它宣告成private,所以,同一支dll projrct的各class裡,也許叫用不到另一class的某資訊,重點不在於因為它編譯過了,而是因為它被宣告成private,另外, 封裝的另一個解釋,是class裡,也許有某個行為日後一定會一直改變,那麼,便可以將這行為抽離開,讓它單獨變成一個class,如此一來,主要的class就不必一直被變更code,"將可能變動的因素抽離走,也叫封裝"。

學徒與師父的功力果然不一樣。

童言童語--這本有單字嗎?

故事

今天婆婆在陪大寶貝閱讀時,突然大寶貝拿著書問婆婆說:「媽咪,這本有單字嗎?」然後婆婆說:「沒有。」大寶貝就狂歡式的狂叫狂躍,說Ya~Ya~Ya!

解說

曾經介紹過一套幼福編號2034系列的書,這系列的書英文內容不深,很適合朗讀給小朋友聽,但小朋友因為聽不懂、無聊,所以每次只要讀到英文的部份,大寶貝就會說:「下一本!」雖然我們不喜歡用「強迫」來減低她對書或學習的興趣,但每次都「下一本」也不是辦法?

所以我們和她溝通,並不是每一本都要讀英文部份,但也是要有2到3成必須聽我們念英文故事和讀單字。我們會選幾本最近少讀的讀本來朗讀。

Visual Basic OOP Part 2 -- 介面

介面(Interface)就像類別,可定義屬性、方法和事件集。但與類別不同的是,介面並不提供實作。介面是由類別實作,並定義為與類別不同的實體。介面就代表著一種合約,因為實作介面的類別必須完全依介面的定義來實作這個介面的各個方面。介面不能包含任何實作程式碼或與實作程式碼相關的陳述式

介面比較像我們常說的「原則」,它只給你一個骨架、一個大方向、一個目標、一個指示。它比較像老闆,站在台上喊衝、衝、衝,但下面怎麼業務怎麼達成業績就憑本事。一樣的口號、方針、目標,有人可以賺到年薪千萬、百萬,也有人只有數萬。

從程式面來看,先定義IAsset(資產)介面

Interface IAsset
    ' 完成變更事件
    Event ComittedChange(ByVal Success As Boolean)
    ' 部門屬性
    Property Division() As String
    ' 取得方法
    Function GetID() As Integer
End Interface

然後我們透過類別來實作此介面,

Class PersonComputer
    Implements IAsset

    Public Event ComittedChange(ByVal Success As Boolean) _
       Implements IAsset.ComittedChange

    ' 實作部門屬性
    Private divisionValue As String
    Public Property Division() As String Implements IAsset.Division
        Get
            Return divisionValue
        End Get
        Set(ByVal value As String)
            divisionValue = value
            ' 當我修改部門,引發事件
            RaiseEvent ComittedChange(True)
        End Set
    End Property

    ' 回傳資產ID
    Private IDValue As Integer
    Public Function GetID() As Integer Implements IAsset.GetID
        Return IDValue & "PC"
    End Function
    
    ' 建構式
    Public Sub New(ByVal Division As String, ByVal ID As Integer)
        Me.divisionValue = Division
        Me.IDValue = ID
    End Sub
End Class

Class ServerComputer
    Implements IAsset

    Public Event ComittedChange(ByVal Success As Boolean) _
       Implements IAsset.ComittedChange

    ' 實作部門屬性
    Private divisionValue As String
    Public Property Division() As String Implements IAsset.Division
        Get
            Return divisionValue
        End Get
        Set(ByVal value As String)
            divisionValue = value
            ' 當我修改部門,引發事件
            RaiseEvent ComittedChange(True)
        End Set
    End Property

    ' 回傳資產ID
    Private IDValue As Integer
    Public Function GetID() As Integer Implements IAsset.GetID
        Return IDValue & "Server"
    End Function
    
    ' 建構式
    Public Sub New(ByVal Division As String, ByVal ID As Integer)
        Me.divisionValue = Division
        Me.IDValue = ID
    End Sub
End Class

透過資產這個介面,我們實作了兩個類別,個人電腦類別及伺服器類別。只要實作介面的類別,就必須照介面的原則走,該有的屬性、方法、事件,一個也不能少。但實作出來內容程式碼,就依各個類別的需求下去實作。例如,我們在回傳ID時,個人電腦將在ID後面加上PC,伺服器則加上Server,當我看到ID時,我就能立即知道這台是個人電腦還是伺服器。

最前面有提到,介面就代表著一種合約,合約不可能是事後才來訂,都是事先規劃好,而且是大方向的規劃。舉例來說,一份網站開發的合約,只會談大方向、主功能、次功能、進度的獎懲、驗收標準,頂多再限定平台及開發語言等。不太可能把程式怎麼寫,變數怎麼定…等實作細節全部寫進去「合約」。我們只要照合約來走,實作出你所需要的網站即可。

Visual Basic OOP Part 2 -- 類別與物件

  • 類別(Class)說的是物件的「型別」
  • 物件則是類別之可使用的「執行個體」
以上很清楚說明,我們撰寫的Class最終是要當成型別(Type)使用。但Class無法直接使用,我們將Class建立成Object,因此,建立物件的動作稱為「執行個體化」。

再以藍圖作比喻,若Class是藍圖,Object就是根據藍圖所蓋的建築物。藍圖是一個計畫一張圖,它無法住人,它不會增值、它無法投資,它無法買賣,但當它建好之後,它就是實實在在的透天、高樓、豪宅,這棟建築物會擁有藍圖上的一切。同一份藍圖,可以建A棟、B棟、第一期、第二期,每一棟、每一期都是活生生個體,這個建造過程就是稱執行個體化。

所以Class會描述組成Object的屬性、欄位、方法和事件,和藍圖描述建築物組成項目的道理相同。就像藍圖可用來建造許多建築物一樣,藍圖來用描述的「結構」,單一Class也是用 來建立任何所需數量的Object。而且就像藍圖會定義使用建築物的人可使用建物的哪些部分,Class也可透過封裝控制使用者可存取的Object項目。

Public Class Person
    ' 共同屬性
    Property FirstName() As String
    Property LastName() As String
    Property Sexy() As Boolean
    Property Birthday() As DateTime

    ' 共同方法
    Public Overridable Function Age() As String
        Return (Now.Year - _Birthday.Year + 1).ToString()
    End Function
End Class

不管你是自行撰寫或是使用.NET Framework提供的類別,最終都是要拿來當型別使用。

Dim Bruce As New Person

在.NET Framework是使用「New 關鍵字」來進行執行個體化。執行個體化之後 Bruce 物件可擁有 Person 類別的屬性、方法。

Dim Bruce As New Person
Bruce.FirstName = "KK"
Bruce.LastName = "Bruce"
' ...

現在你可以開始可以使用它,存取 Bruce 物件的屬性、執行 Bruce 物件的方法,而且全部都是透過「.」(點符號)。

比IIF 方法更好的選擇,IF 運算子

在Visual Basic中,如果是「二選一」的情況,一般而言,我們會選用「IIf 方法」。但今天看到一段程式語法:

Public Sub Linq10()
    Dim numbers As Integer() = {5, 4, 1, 3, 9, 8, 6, 7, 2, 0}
    Dim stringNames As String() = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}

    Dim digitOddEvens = From num In numbers _
                        Select Digit = stringNames(num), Even = ((num Mod 2) = 0)

    For Each d In digitOddEvens
        ' 注意,此處使用的是「If」,而不是「IIF」
        Console.WriteLine("The digit " & d.Digit & " is " & If(d.Even, "even", "odd"))
    Next
End Sub 

程式執行起來,怪了,一切正常。

查了一下MSDN,原來這是「If 運算子」,它提供比IIF 方法更有效率的處理方式,且使用方式與IIF 方法可以說一模一樣。依MSDN上的文件,If 運算子是從Visual Studio 2008開始提供,也就是Visual Basic 9 / 2008才有提供此運算子。

如果依MSDN上的說明,這個If 運算子可是比IIF 方法更好的選擇。

Visual Basic OOP Part 2 -- 封裝、繼承、多型

物件導向程式設計三大元素

  • 「封裝」指的是將一組相關的屬性、方法和其他成員,視為單一單位物件
  • 「繼承」則是描述依據現有類別來建立新類別的能力
  • 「多型」指的是您可以有多個交替使用的類別,即使每個類別是以不同的方式來實作相同的屬性或方法。

封裝, Encapsulation

當你寫下一段「Dim Name AS String = "KKBruce is handsome."」的程式碼之後,你知道String類別總共有多少屬性、方法?還有擴充方法?如果這些屬性、方法、擴充方法全部要你手動實作,我想光一個String類別,就能把你搞瘋了。

而封裝,就幫我們把所有相關實作(例如,字串相關功能)整理起來成為一個一個的類別(Class)。所以當我們建立此類別的執行個體(Name 物件)時,就能得到String類別所有的屬性、方法。我們能透過「Name.Length()」得知此字串的長度,透過「Name.IndexOf("Bruce")」找尋字串出現的位置。

透過封裝,你不用重覆造輪子,除非你有需求,或是你有信自能寫出比各種Framework所封裝類別更棒的類別庫,不然像.NET Framework所封裝的類別庫,應該可以解決你決大部分的需求。當然,我們還是有機會自己動手設計一些自己的類別,例如在ASP.NET MVC中,你就整天與Class相處。封裝還有一個好處,可以讓你隱藏整個的實作細節,這種作法稱為「資料隱藏」(Data Hiding)。

你不知道「Name.Length()」或「Name.IndexOf("Bruce")」是如何實作的,你只管用,只管結果。那這些類別的修改、變更「你或使用者」完全不用管,舉個例子,System.Text.StringBuilder 類別MISLab200測試的結果,在Visual Studio 2005與Visual Studio 2008進行比較,Visual Studio 2008快了5.45倍,也就是快了545%。如果你跟我說此類別完全沒有改寫,我是不太相信,但他們改寫了什麼,讓效能大幅提升,你不知道,我也不知道,但結果是一樣的。

所以,不管是使用別人封裝好的類別,還是你封裝給別人用的類別,重點有二:
  1. 不用重覆造輪子
  2. 資料隱藏

Public Class Student

    ' 建構式
    Sub New()
        _name = "無名氏"
        _Id = "S2010000000"
    End Sub

    ' 成員
    Private _name As String
    Private _id As String

    ' 屬性
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(value As String)
            _name = value
        End Set
    End Property
    Public Property Id() As String
        Get
            Return _id
        End Get
        Set(value As String)
            _id = value
        End Set
    End Property

    ' 方法
    Public Function GetResults(id As String) As DataSet
        ' 實作方法
    End Function

    Public Sub SetClass(id As String)
        ' 實作方法
    End Sub
End Class

以Class為單位來封裝「建構式、成員、屬性、方法」,記得,不管Class怎麼寫,就是寫這四個東西。

繼承, Inheritance

當類別越寫來越多時,你就能發現,有些類別有著「上下關係」,或者我想要改寫或擴充類別,但此類別已經被引用、使用至多處,直接的修改有其風險,這時我們就很適合使用繼承。繼承關係有可能邊寫邊發現,邊寫邊發現,一般我們會使用抽象類別,也可能是一開始就規劃出來,一般我們會使用介面。抽象類別和介面的使用時機,我已經在介紹過了。

例如,學校有老師、有學生,所以你會有Class Teacher及Class Student,但你能發現他們都有共同的性質,他們都是人,有名、有姓、有性別、有生日…所以我們就可以透過另一個類別Class Person來集中這些共有的性質,然後讓Class Teacher及Class Student來繼承Class Person的這些性質。

被繼承的類別稱為「基底類別」(Base Class),而繼承的類別即稱為「衍生類別」(Derived Class)。另.NET Framework的語言不支援多重繼承,也就是說,您只能為衍生類別指定一個基底類別。

Public Class Person
    ' 共同屬性
    Property FirstName() As String
    Property LastName() As String
    Property Sexy() As Boolean
    Property Birthday() As DateTime

    ' 共同方法
    Public Overridable Function Age() As String
        Return (Now.Year - _Birthday.Year + 1).ToString()
    End Function
End Class

Public Class Teacher
    Inherits Person

    ' 擴充或改寫基底類別
    Public Overrides Function ToString() As String
        Return "Hello, My Name is " & MyBase.FirstName & MyBase.LastName & _
               "I'm your teacher."
    End Function
End Class

Public Class Student
    Inherits Person

    ' 擴充或改寫基底類別
    Public Overrides Function ToString() As String
        Return "Hello, My Name is " & MyBase.FirstName & MyBase.LastName & _
               ", " & MyBase.Age() & "yeas ago."
    End Function
End Class

透過繼承,我們能適當的切割類別,能在衍生類別中重複使用擴充修改基底類別中定義的行為,又不破壞原先基底類別設計。而另外抽象類別及介面的使用,又是另一門學問稱「Design Patterrns/設計樣式/設計模式」。

多型, Polymorphism

多型又稱「同名異式」,也就是說,它能提供「具有不同功能名稱完全相同之方法或屬性」。


經Allen指點,我放錯範例子,以下範例為「多載」。

多載


System.String類別為例,它提供了8種同名稱,但依照你傳入參數不同,而有不作用的的建構式。


另外常用的方法,如Split(),也提供了6種,同名稱方法,但依照傳入參數不同而不同功能的方法。


這樣我們就很好理解多型多載。多型多載就是依型別的不同,即可找到對該的方法。這種的好處是,我們不必為相同方法之不同型別去進行無意義的命名,例如,Split_Char()、Split_CharInt32()、Split_Char_StrOpt()…。多型多載讓我們不管在使用方法或實作相關程式碼都降低複雜度。

封裝+繼承+多型=物件導向程式設計

由封裝、繼承、多型三者,架構出一整個物件導向程式設計,當然,每個部分還可以細切出很多理論細節,例如,多型還可以細切出「由繼承實作多型」或「由介面實作多型」,不過這不是我們目前討論的重點,我們先清楚封裝,封裝了什麼東西;繼承,.NET Framework只能單向繼承,形成一個Tree架構;多型,提供同一名稱不同實作功能。

清楚了,我們就接下去談千古話題:「類別與物件」。

育嬰假- (3)1111人力銀行之邀請



From:http://udn.com/NEWS/NATIONAL/NAT5/6319965.shtml

人生總有第一次,全家人的TV留影記錄。

感謝1111人力銀行的邀請,還有記者大哥看得起,讓在旁邊的奶爸我也有上電視秒數。^_^

我想關於育嬰假的一些想法,我在找個時間整理出來。

Visual Basic OOP Part 2 -- 前言

之前我寫過「Visual Basic 物件導向程式設計入門」共八篇文章,我想辦法使用比較簡單白話來說明「物件導向」這個理論,但在實作上,還是必須和「程式語言(VB, C#, Java ...)」來結合,才會更有意義。

所有我想要再延伸之前的入門八篇,並且使用在.NET Framework裡正式的定義、用詞等,再談一次物件導向程式設計。各位也知道,文言文理論的東西看了想睡是正常,通常正式的心法就是那麼的「文言」。

般若婆羅蜜多心經 -- 齊豫


心經,我相信大家都聽過,但能通的有幾人?

通了你就成為師父,精了你就成為大師。

而這種物件導向程式設計(Object-oriented programming, OOP)、design pattern(設計樣式)…等理論的東西就像心經一樣,看似簡單,但當你越是深入,你就會越發現它越不簡單,而且練的越深,你所需要的內功心法的基礎越是重要,不然很容易內傷加外傷,傷心、傷腦、傷身。

我們在物件導向程式設計裡談的內容會一直不斷重覆,「封裝」、「繼承」、「多型」、「抽象」、「介面」…,但試著從不一樣的方向來談,我覺得這樣很好,就像讀書一樣,一個東西可能卡了很久,看了很多書或參考資料,就是搞不懂,但可能因為某作者的一句「不一樣的解釋」就讓你通了。所以通常我看到有人由不同方向來談同一件事時,我會很有興趣,雖然不一定能通,但最少能多一分了解。

希望能透過不一樣的方向,再談談這個物件導向程式設計重要內容,雖然你可以不了解它,但你應該很難不使用它,當你有在程式裡使用「.」(點符號),不論是Visual Basic、C#或其他程式語言,你大概已經在使用物件導向程式設計,一個點符號後面是需要很多理論來支持,使用它又不了解它,這很矛盾。

當物件導向程式設計成為你的基礎內功時,你在.NET Framework的學習路上會比其他人快樂許多。

育嬰假 - (2)孩子的快樂天堂

快樂天堂MV


感動不留下記錄,明早起來又忘的一清二楚。

婆婆的育嬰假已經快結束了,這幾個月來,她常常擔心要回去上班的問題,人家請育嬰假心裡想的是快點回去上班,婆婆想的是要如何才能不回去上班,因為我的孩子實在太可愛,一個傻傻天兵型,一個牙牙學語超可愛。婆婆有想過,想要找孩子來帶,因為她本身也有保母證照,但礙於現在還是育嬰假期間,無法加入保母系統。

這一年以來的育嬰假,除了讓我擁有一個非常美好的回憶外。另一件讓我高興的事是,還好我是宜蘭人,我們在三星還有個家。我在「你的家鄉有多美」介紹過我的家鄉三星,而這一年的育嬰假,每個月婆婆與二位寶貝,都會有一周的時間回去宜蘭三星住,讓阿嬤教順@_@。

我喜歡三星的生活,我女兒也非常喜歡,早上起床,看到的不是白牆、不是TV,而是一片青山,院子、馬路…你想怎麼跑就怎麼跑,而不是一整天被關在房子裡。每次她們一回去三星,晚上下班回到家,和婆婆通電話時,就可以聽到我那天兵一號又胡搞些什麼,可愛二號又進度了那些。我可以連笑近一個小時,聽她們今天發生了什麼有趣的事,其實已經笑到臉都僵了,還是捨不得掛電話。我喜歡她每天玩的髒髒的,這樣才有鄉下的味道。

在生老大和老二時,其實新竹市有生育補助,但我二個女兒的戶藉,我個人非常堅持放在宜蘭,我要她們當個宜蘭人,雖然她們會在新竹長大,但我要她們知道,三星是妳們的家鄉,是一個快樂天堂。她們每次回三星都玩到瘋了,我媽還問婆婆說,這樣會不會給小孩玩太瘋?而婆婆也回答的好:「這樣她們才會有童年!

新竹是收,宜蘭是放。我喜歡讓她們的生活有收有放。也還好有這樣的生活,我相信她們在很小的時候就已經知道什麼是思念。最近一次,他們回去宜蘭後的星期一,晚上已經十一點多,我已經手機關機就寢,但家中的網路電腦響起,接起電話,電話那頭婆婆說:「妳女兒說想爸比,而且想到哭出來!」透過電話我跟大寶貝「秀秀」一會兒,她才乖乖去睡。

如果沒有育嬰假,我們根本就不可能有這樣的生活,對她們而言,每個月有一周的快樂假期,對我而言,結了婚生了孩子還能有這短短一周一個人的生活,是非常不錯,每每都能加深我對家的感情,很標準的小別勝新婚。

雖然因為生活,我無法讓妳們在宜蘭三星從小玩到大,讓妳們成為鄉下野孩子,但每當聽到妳們在三星玩的多快樂時,我心裡想的都是,還好我在三星有個家。

最後,感謝我媽,辛苦妳了,希望孫子的笑聲能替代我的對不起,長年在外,我沒能照顧到妳什麼日子,這一年婆婆的育嬰假,能比較有時間回去陪你,算是幫我贖點罪。

I love you, 母親節快樂。