2018年7月17日 星期二

存取目前應用程式預設組態 app.config 的 AppSettingsSection 資料

有些時候,我們在進行 C# 程式設計的時候,需要產生一些物件,但是,需要由哪個具體實作類別來產生,並不希望在設計時期來指定,而是在應用程式執行的時候,依據當時應用程式的設定參數,來進行決定要由哪個類別來產生特定介面的物件。在這篇文章中,我們將會設計一個 靜態工廠模式 Static Factory Pattern 類別,建立起一個鬆散耦合的程式。
當我們呼叫靜態工廠方法的 StaticFactory.GetMessage() 方法的時候,會依據 app.config 這個設定檔案內容定義的資料,產生出一個型別為 IMessage 的具體實作類別物件;在這個範例中,我們要產生一個可以發送訊息通知的物件,將所們指定的訊息發送出去,我們設計了介面 IMessage,並且有兩個類別 SMSMessage (使用簡訊方式發送) 與 EMailMessage (使用電子郵件方式發送) 皆實作這個介面,因此,我們在主程式端,透過靜態工廠模式類別,幫助我們產生一個 IMessage 的實作物件,讓我們可以傳送出訊息出去。
不過,我們在靜態工廠模式類別中,並沒有指定要由哪個類別來產生這個物件,而是我們會讀取 app.config 這個 XML 檔案的 appSettings 裡面的定義內容,找到 IMessage 的設定值,這個設定文字的內容是會指定類別的在組件中定義名稱,我們使用這個字串與 Type.GetType 方法,幫助我們產生出指定的型別物件,透過 Activator.CreateInstance 方法,可以產生出該類別的執行個體了
在這裡,我們建立一個 .NET Framework 主控制台應用專案 AccessConfigurationManager
C Sharp / C#
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AccessConfigurationManager
{
    public interface IMessage
    {
        void Send(string message);
    }
    public class SMSMessage : IMessage
    {
        public void Send(string message)
        {
            Console.WriteLine($"簡訊已經送出 : {message}");
        }
    }
    public class EMailMessage : IMessage
    {
        public void Send(string message)
        {
            Console.WriteLine($"郵件已經送出 : {message}");
        }
    }
    public class StaticFactory
    {
        public static IMessage GetMessage()
        {
            // 這裡要加入 System.Configuration 組件參考
            NameValueCollection appSettings = ConfigurationManager.AppSettings;
            string fooValue = appSettings["IMessage"] ;
            Type fooType = Type.GetType(fooValue);
            IMessage fooObject = Activator.CreateInstance(fooType) as IMessage;
            return fooObject;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            #region 取得指定完整的類型名稱
            var foo = typeof(SMSMessage);
            var bar = foo.AssemblyQualifiedName.ToString();
            Console.WriteLine(bar);
            #endregion

            IMessage messageObject = StaticFactory.GetMessage();
            messageObject.Send("我已經動態產生具體實作物件了");

            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }
}
緊接著,我們在該專案中,找到根目錄下的 app.config 檔案,將其打開,並且在 Configuration 節點內,建立一個 appSettings 節點,我們將會在這個節點內,建立一個 IMessage 鍵值,他的值為 AccessConfigurationManager.SMSMessage, AccessConfigurationManager 。這個字串將會表示在 AccessConfigurationManager 組件內的 AccessConfigurationManager.SMSMessage 類別。若您想要得到某個類別的完整組件識別字串,可以參考這篇文章 指定完整的類型名稱 與底下程式碼用法。
C Sharp / C#
#region 取得指定完整的類型名稱
var foo = typeof(SMSMessage);
var bar = foo.AssemblyQualifiedName.ToString();
Console.WriteLine(bar);
#endregion
底下是我們修正後的 app.config 設定內容
app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>
  <appSettings>
    <add key="IMessage" value="AccessConfigurationManager.SMSMessage, AccessConfigurationManager"/>
  </appSettings>
</configuration>
現在,可以執行這個測試專案,我們將會看到我們產生了類別 SMSMessage 這個類別的執行個體
Console
AccessConfigurationManager.SMSMessage, AccessConfigurationManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
簡訊已經送出 : 我已經動態產生具體實作物件了
Press any key for continuing...
假設現在客戶有變更需求產生,所有的訊息通知,將不再使用簡訊方式發送,而要改用電子郵件方式來發送;在以往,我們需要修改我們的原始程式碼,但是這樣做,有可能會產生更多的 bug (尤其是在大型、多人開發的專案上)。不過,經過我們這樣的設計模式,我們僅需要修正 app.config 這個設定檔案中的 IMessage 值,重新執行一次
app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
  </startup>
  <appSettings>
    <add key="IMessage" value="AccessConfigurationManager.EMailMessage, AccessConfigurationManager"/>
  </appSettings>
</configuration>
現在,我們將會看到我們產生了類別 EMailMessage 這個類別的執行個體
Console
AccessConfigurationManager.SMSMessage, AccessConfigurationManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
郵件已經送出 : 我已經動態產生具體實作物件了
Press any key for continuing...

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程

2018年7月13日 星期五

一個違反 ISP Interface Segregation Principle 介面隔離原則 的範例說明

ISP Interface Segregation Principle 介面隔離原則 也是對於很多想要了解 SOLID 物件導向程式設計觀念與技能的人,相當困惑的。有人會說,要是我,才不會設計出這樣的程式碼,但是,現實情況是,產生這樣的問題,真的很常見,而且,會造成開發者的困擾,這怎麼說呢?
羅馬不是一天造成的,程式不是很快就可以寫出來的,當相同的程式經過多人的手,並且有著修改時間壓力下,往往會造成很多不可思議的程式碼,畢竟,這些程式碼都是人寫出來的。在底下的範例中,開發人員想要開發出個辦公室事務機器,因此,定義了許多功能,也造就了 IBusinessPrinters 這個介面,緊接著實作出 AllInOnePrinter 這個具體類別;可是,客戶想要開發出更具有價格競爭力的商品,因此,就想到只留下列印功能,把其他功能都移除,設計出一個超時尚的 SimplePrinter 印表機,另外,又想要移除傳真功能而已,造就出讓中小企業有能夠購買的 CopyPrinter 拷貝列印機。從底下的類別宣告,您看出了甚麼問題嗎?
C Sharp / C#
public interface IBusinessPrinters
{
    void Scan();
    void Print();
    void Copy();
    void FaxSending();
    void FaxReceiving();
}
public class AllInOnePrinter : IBusinessPrinters
{
    public void Copy() { Console.WriteLine("拷貝中"); }
    public void FaxReceiving() { Console.WriteLine("傳真接收中"); }
    public void FaxSending() { Console.WriteLine("傳真發送中"); }
    public void Print() { Console.WriteLine("列印中"); }
    public void Scan() { Console.WriteLine("掃描中"); }
}
C Sharp / C#
public class SimplePrinter : IBusinessPrinters
{
    public void Copy() { Console.WriteLine("錯誤!! 無此功能"); }
    public void FaxReceiving() { Console.WriteLine("錯誤!! 無此功能"); }
    public void FaxSending() { Console.WriteLine("錯誤!! 無此功能"); }
    public void Print() { Console.WriteLine("列印中"); }
    public void Scan() { Console.WriteLine("錯誤!! 無此功能"); }
}
public class CopyPrinter : IBusinessPrinters
{
    public void Copy() { Console.WriteLine("拷貝中"); }
    public void FaxReceiving() { Console.WriteLine("錯誤!! 無此功能"); }
    public void FaxSending() { Console.WriteLine("錯誤!! 無此功能"); }
    public void Print() { Console.WriteLine("列印中"); }
    public void Scan() { Console.WriteLine("掃描中"); }
}

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程

2018年7月12日 星期四

一個違反 LSP Liskov Substitution Principle 里氏替換原則 的範例說明

有很多人對於 LSP Liskov Substitution Principle 里氏替換原則 所講述的內容,看得很模糊,有些時候是學習者對於物件導向程式設計觀念不慎清楚、開發經歷中,也甚少經常設計類別繼承的程式碼,不過,也有人說,我都有設計類別繼承,要是我,才不會有這樣的問題;可是,這是個原則,講的是當您遵從這個原則之後,就不會設計出有行為異常的子類別,有些時候,您也可能需要維護別人開發的舊專案,這個專案也許不知道經過幾個人來維護過了,往往有需求變更的時候,為了客戶時效要求,手頭上有很多專案要開發,就直接繼承類別,產生新的子類別來設計出滿足變更需求的程式,當然,經過一段時間之後,這個專案上就會產生違反 LSP 的問題;不過,說實在的,若這個專案從頭到尾都是您自己開發,也是有可能產生違反 LSP 的問題。一旦程式碼出現違反 LSP 的原則,就會產生出許多意想不到的問題,而這些問題,很多時候是在設計與除錯時候,無法立即發現到的 (更多關於 LSP 的介紹,網路上已經有海量的文章,請各位自行去搜尋、研究)
現在,讓我們來看看底下的範例,您能夠看出它存在著甚麼問題嗎?
另外,他是否有遵從 LSP 原則嗎?
不論答案是肯定或者是否定,請您要提出的論述觀點,這樣,才能夠知道,您是否真正明瞭甚麼是 LSP Liskov Substitution Principle 里氏替換原則
C Sharp / C#
class 鳥
{
    public virtual void 飛() { Console.WriteLine("鳥在飛"); }
    public virtual void 吃() { Console.WriteLine("鳥在吃"); }
}
class 老鷹 : 鳥
{
    public override void 飛() { Console.WriteLine("老鷹在飛"); }
    public override void 吃() { Console.WriteLine("老鷹在吃"); }
}
class 鴕鳥 : 鳥
{
    public override void 飛() { throw new NotSupportedException("鴕鳥不能飛"); }
    public override void 吃() { Console.WriteLine("老鷹在吃"); }
}
底下是使用上面類別的範例程式碼
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        List<鳥> birds = new List<鳥>();
        birds.Add(new 鳥());
        birds.Add(new 老鷹());
        birds.Add(new 鴕鳥());
        BirdsFly(birds);
    }
    static void BirdsFly(List<鳥> birdList)
    {
        foreach (var item in birdList)
        {
            item.飛();
        }
    }
}

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程



2018年7月11日 星期三

對於學習 SOLID 五個原則 Principle 的感想

因為工作上的需要,因此,我特別安排時間來進行 SOLID OOD 物件導向程式設計的研究與學習,我想,我應該是與大家一樣,開始都是從 Google 上來搜尋 SOLID SRP OCP LSP ISP DIP Principle 等關鍵字,看看網路上的人怎麼說,也須我的資質不好,搜尋到一大票的文章,怎麼看,怎麼不懂,最重要的是,當我好不容易看懂,例如 Single Responsibility Principle SRP ,接著看到更多的文章所說明的內容、圖片、解釋、範例,就更加的模糊與不清楚他到底是要做甚麼用到、可以幫助我做甚麼事情、我該如何將我的程式碼撰寫或重構成具有 SRP 精神的程式碼,這也包括了不同作者所舉例的程式碼,並不是我孰悉的 C#,這讓我在閱讀那些程式碼的過程中,也是備感艱辛;過程中,我也產生了許多的疑問,再次以 SRP 為例,為什麼每個文章作者,對於所舉例的範例程式碼,緊接著就說這段程式碼具有三個責任,所以,我們要把這三個責任進行分離開,我的老天鵝呀,我是真的不知道為什麼是三個責任,不是兩個,也不是五個。
好吧,既然廣泛涉獵的許多網路上的 SOLID 的文章,那麼,只好痛下決心,花點索費,到 Amazon 去買 Uncle Bob 寫的兩本書來看看 (會去買 Uncle Bob 的書,這是因為 SOLID 是 Uncle Bob 根據不同人提出的分享,綜合出這五個原則) ,也花了點時間,找出 Uncle Bob 當初所提出的 SOLID 五篇文章,既然東西都到手之,就要開始花點時間把這些最初的資料給他閱讀一番。不過,最後,我還是想辦法把當初提出這些原則人寫的文章、書,也給他購買或者找出來,並且從中細細體味出箇中奧妙之所在。
因此,對於想要把 SOLID 弄清楚的人,在此提出幾個建議

OOP 物件導向程式設計

若您對於 OOP 的四個特性不慎了解,甚至沒有研究過 OOP 四個特性,我覺得您應該要先去了解與孰悉 OOP 的四個特性:抽象、封裝、繼承、多型

弄清楚這些名詞的意義

當您在進行 SOLID 學習過程中,需要處處存著兒時好奇心態,保持您的疑問,雖然,當時您可能無法理解或明瞭其中含意,不過,這對於您日後學習上,會有著明顯的好處;若平常都有做善事,有著好機緣,碰到真正會的有緣人(千萬不要去問自以為是,處處都是我認為、我想的、我平常都是這樣做的的人去問,這樣,您會走火入魔的),把你蒐集起來的問題,向他們請教看看。
首先,您需要了解 甚麼事 Principle 原則,因為, SOLID 是由五個 Principle 所組成,若您不瞭解何謂 Principle 的意義,那麼,就算您學會了這五個 Principle,您覺得對你會有甚麼幫助呢?
緊接著,要把每個原則的每個英文字與其解釋(我是參考 Uncle Bob書中對於這五個原則的描述),挑出主要的名詞,了解他們存在的意義與目的 (千萬不要使用您本身所學的經歷來看待這些名詞,畢竟,這些原則是早在 20 年前所提出來的,那個時候,可是沒有 C# / Java 這樣的語言存在,還是那句話,不要憑感覺來認為自己就是一切),這樣會有助於您去閱讀書中、文章中之各原則的論述。

SRP Single Responsibility Principle 單一責任原則

A class should have only one reason to change
請了解甚麼事個 Responsibility 責任,不過,在其他文章中,您可能會看到有些內容會使用 Module 模組來取代 Class 類別,您可以嘗試體會與思考看看,為什麼會有 Module 這個名詞出現,這會對於您之後學習過程中有莫大的幫助;另外,也需要甚麼是 Change 變更需求,為什麼會有它,他會造成甚麼影響,如何改善因為 Change 所產生出來的問題。總之,凡事抱著疑問來學習。

OCP Open Closed Principle 開放封閉原則

Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification
OCP 開放封閉原則,是我們進行專案開發過程中,最為重要與經常會面對的問題,所以,務必要花些時間來精通才好。
在這裡,請要了解這些詞彙的意義與精神: Open / Closed / Software Entity / Extension / Modification 。對於後面兩者,請要去了解,就是目標對象指的是誰,也就是誰要開放,誰要封閉,究竟怎麼開放,怎麼封閉。
在您觀看 OCP 內容的時候,會看到很多文章舉的例子,都是使用 Interface 介面,來做為重構成為符合 OCP 的作法,請千萬不要畫地自限,以為,想要符合 OCP 的精神,就只能使用 介面 來解決,有興趣的人,可以去嘗試看看,要做出符合 OCP 精神的程式碼,至少有六七種以上的技術可以做到。

LSP Liskov Substitution Principle 里氏替換原則

Subtypes must be substitutable for their base types
這個原則主要的重點就是替換這個名詞,您也許會覺得很奇怪,這到底是甚麼原則,為什麼要放在 SOLID 這五個原則內,他有甚麼重要性?在想要了解這個原則,您應該要先從他的背景來研究,那就是 OOP 裡面的繼承與多型,也就是說,若您對於這兩個名詞也一知半解,那麼,這個原則所提到的內容,您是無法體會出來的。

ISP Interface Segregation Principle 介面隔離原則

Clients should not be forced to depend upon interfaces that they don't use
這也是個大家覺得好莫名的原則,介面與隔離,是這個原則要探討的重點,還是那句話,若您在開發程式設計過程中,幾乎沒有自己設計過介面,或者不太了解介面的應用與特色,那麼,您會學得很辛苦;另外,就是一個問題,為什麼會產生一個具有那麼多方法宣告的介面,也許,您會說:要是我自己開發,就不會有這樣的問題?可是,請不要用自己的經歷、角度來看這些原則,在一個專案開發與設計過程中,往往會有多人一起進行開發,當然,任何人都可以去修改介面的宣告,所以,您應該已經知道為什麼會產生一個超級胖的介面了。因此,了解這樣的介面會產生的副作用,並且規避這樣的現象產生,也就是這個原則的精神了。

DIP Dependency Inversion Principle 相依反轉原則

High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions
這個原則需要用到兩句話來說明,因此,我們需要了解這兩句話中的各個腳色與扮演目的:高階模組、低階模組、模組、抽象、明細、相依。喔喔,這五個原則中,看樣子是這個原則中出現了最多的名詞,我個人覺得最重要的詞彙就是 相依 Dependency ,了解何謂相依、為什麼會相依、相依會造成甚麼樣的問題、怎樣才是一個好的相依,這樣,也許您就已經對於這個原則了解一半了。不過,若您的程式設計與開發生涯中,幾乎沒有用到過或者很少用到過相依性注入這樣的設計模式,那麼,您可能無法深刻體會出這個原則想要幫您解決甚麼問題。

打通任都二脈

若您經過研究,有了解與孰悉 OOP,對於這五個原則的各個用到的詞彙也明瞭,甚至有看過這五個原則當初的背景文章,而且也看過各種原則的範例程式碼,那麼,您已經完成了第一步,接下來,您將會需要
  • 找出與練習撰寫出 違反原則的程式碼,自己嘗試重構這個程式碼,使其符合原則
  • 嘗試自行分析,若一個程式碼,符合 OCP,是否就符合 LSP,反過來說,符合 LSP ,是否就是符合 OCP;另外一個是,若程式碼符合 OCP,是否就符合 DIP,反過來說,符合 DIP ,是否就是符合 OCP,嘗試挑戰看看這樣的論述是否都是為真,還是不是,若不是,您可以舉出這樣的例子呢?

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程