SlideShare a Scribd company logo
淺談C#物件導向與
Design Pattern
Brian Chou – 2023.08.11
Agenda
1. 物件導向基礎
2. SOLID原則
3. 常見設計模式
4. 推薦資源
5. 總結
3
物件導向基礎
1
Why>How>What
類別(Class)與物件
1. 類別(Class): 有相同屬性(Property)和方法(Method)的
物件的抽象集合
2. 物件(Object): 由類別(Class)創建出來的實體
3. 物件導向程式設計(OOP):
開發者使用類別(Class)來定義物件的特徵(屬性)和行為
(方法),從而實現模組化、可重用性、可擴展性和易於
維護的程式碼結構。
4
欄位(Field), 屬性(Property), 方法(Method)
1. 通常欄位(Field)為private
屬性(Property)為public
2. 屬性set存取器以value指派值
3. 屬性可加入邏輯檢核
4. 唯讀屬性{get;} 唯寫屬性 {set;}
5. 一般的屬性如下
public string AccountName { get; set; }
5
Field
Property
Method
建構式/函式/子/值 (Constructor)
1. 用來對Class進行實體化(以new調用),與Class同名、無返回值、無需void
2. 未定義,則系統預設空的建構式,有定義,則預設建構式失效
6
依情況決定是否需要
無法建立,必須帶參數
宣告 實體化
命名規範
區域變數,以小駝峰命名法 ( camelCase )
private欄位(Field),以底線+小駝峰命名法 ( _camelCase )
介面(Interface),以I為首+Pascal命名法 ( IPascalCase )
列舉(Enum),以Pascal命名法+Enum結尾 (PascalCaseEnum)
其餘以Pascal命名法 ( PascalCase )
縮寫單字避免全大寫 (ex: Id, HtmlId)
7
基本規範
1. Class/Field/Property應為名詞,Method應為動詞
2. 不要有無意義的命名(e.g., a, b, c)
3. 使用XML註解 ( /// <summary>)
4. 盡量使用強型別
5. 使用Enum來避免magic number
6. 其他參考資料: [如何提升系統品質-Day16]Code Convention
8
自行參考微軟官方文件
1. 屬性概觀:
https://learn.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/properties#properties-overview
2. Access Modifiers (存取修飾子/詞):
https://learn.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/access-modifiers
3. 建構函式
https://learn.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/constructors
4. 常見的 C# 編碼慣例
https://learn.microsoft.com/zh-tw/dotnet/csharp/fundamentals/coding-style/coding-conventions
5. 微軟官方文件切換語言,將網址的 zh-tw  en-us
9
封裝 (Encapsulation)
1. 設計Class時,將實作細節部份包裝/隱藏,控制屬性的存取,保護數
據的完整性和Class的封閉性。
2. 存取修飾子 public / private / protected …
e.g., public Method / Property, private Method / Field…
3. 優點:
➢良好的封裝能夠減少耦合。
➢類別內部的實現可以自由地修改。
➢類別具有清晰的對外界面。
10
繼承 (Inheritance)
物件的繼承代表一種 ”is-a” 的關係 (鴿子是鳥類,企鵝是鳥類)
➢子類別擁有父類別非 private 的屬性和方法。
➢子類別具有自己的屬性和方法,可以擴展父類別的屬性與方法。
➢子類別可以重寫(override)父類別的virtual 與 abstract Method。
11
public class BaseController : Controller
{
// 實作驗證機制、Log機制…
}
public class HomeController : BaseController
{
}
繼承 (Inheritance)
優點:
➢重用性
➢擴展性
缺點:
➢耦合度增加: 一旦父類改變,影響所有子類
➢多層繼承關係複雜
➢違反單一職責原則
12
多型 (polymorphism)
1. 不同物件執行相同動作,透過自己程式碼來執行
2. virtual關鍵字:
➢ 在父類別中標示,允許子類別覆寫此屬性或方法 (想覆寫就覆寫)
3. override關鍵字
➢ 在子類別中,覆寫父類別屬性或方法
4. base關鍵字:
➢ 呼叫父類別建構式
➢ 呼叫父類別方法
5. this關鍵字:
➢ 指的是 目前這個類別的實體
13
圖/取自蜘蛛人官方臉書
class Animal
{
public string Name { get; set; }
public virtual void Speak() => Console.WriteLine("動物發出聲音...");
}
class Dog : Animal
{
public override void Speak() => Console.WriteLine("狗在汪汪叫!");
}
class Cat : Animal
{
public override void Speak() => Console.WriteLine("貓在喵喵叫!");
}
//---------------------------------------------------------------------------------------------
class Program
{
static void Main()
{
Animal animalDog = new Dog();
Animal animalCat = new Cat();
// 呼叫 Speak 方法,實際呼叫的是對應子類的 Speak 方法
animalDog.Speak(); // 輸出:狗在汪汪叫!
animalCat.Speak(); // 輸出:貓在喵喵叫!
}
} 14
多型範例1
(virtual / override)
class Animal
{
public string Name { get; set; }
public virtual void Speak() => Console.WriteLine("動物發出聲音...");
}
class Dog : Animal
{
public override void Speak() => Console.WriteLine("狗在汪汪叫!");
public void GetName() => Console.WriteLine("我是小黑");
}
//---------------------------------------------------------------------------------------------
class Program
{
static void Main()
{
Animal animalDog = new Dog();
animalDog.Speak(); // 輸出:狗在汪汪叫!
animalDog.GetName();
}
}
子類以父類的身分出現時,子類特有的屬性及方法不可使用
15
 子類特有Method
public class AccountBase
{
protected string accountNumber;
protected decimal balance;
public AccountBase(string accountNumber, decimal initialBalance)
{
this.accountNumber = accountNumber; //傳入參數與欄位同名,使用this
this.balance = initialBalance;
}
public virtual void Deposit(decimal amount)
{
balance += amount;
Console.WriteLine($"存入{amount}元,帳戶餘額{balance}元");
}
public virtual void Withdraw(decimal amount)
{
if (amount <= balance)
{
balance -= amount;
Console.WriteLine($"領出{amount}元,帳戶餘額{balance}元");
}
else { Console.WriteLine("餘額不足!"); }
}
}
多型範例2-1
(base / this)
16
/// <summary>
/// 高利帳戶
/// </summary>
public class SavingsAccount : AccountBase
{
private decimal _interestRate;
public SavingsAccount(string accountNumber, decimal initialBalance, decimal interestRate)
: base(accountNumber, initialBalance) // 使用base呼叫父類建構式
{
this._interestRate = interestRate;
}
public override void Deposit(decimal amount)
{
base.Deposit(amount); // 使用base呼叫父類Method
CalculateInterest();
}
private void CalculateInterest()
{
decimal interest = balance * (_interestRate / 100);
balance += interest;
Console.WriteLine($"增加利息{interest}元,帳戶餘額{balance}元");
}
}
多型範例2-2
(base / this)
17
class Program
{
static void Main()
{
//SavingsAccount建立之帳戶
SavingsAccount savingsAccount = new SavingsAccount("S-0001", 0, 2.5m);
savingsAccount.Deposit(500);
savingsAccount.Withdraw(200);
Console.WriteLine();
//AccountBase建立之帳戶
AccountBase account = new AccountBase("A-0001", 0);
account.Deposit(5000);
account.Withdraw(3000);
Console.Read();
}
}
多型範例2-3
(base / this)
18
abstract class Shape //抽象類別
{
public abstract int GetArea(); //抽象方法
}
class Square : Shape
{
private int _side;
public Square(int n) => _side = n;
public override int GetArea() => _side * _side; //子類別必須override 抽象方法
}
//--------------------------------------------------------------------------------------
class Program {
static void Main()
{
Square sq = new Square(13);
Console.WriteLine($"Area of the square = {sq.GetArea()}"); // Area of the square = 169
}}
//--------------------------------------------------------------------------------------
Shape shape = new Shape();
抽象類別 (Abstract Class)
使用 abstract表示,該Class只是作為其他Class的基底,不自行實體化
19
抽象類別 (Abstract Class)
1. 半抽象的型別,可包含抽象方法(沒實作) 與 非抽象方法(有實作)
2. 抽象類別無法實體化(new),可定義建構式
3. 抽象方法無法宣告主體(直接分號結尾)
public abstract int GetArea(); //抽象方法
4. 抽象方法不可使用static 或 virtual 修飾詞
5. 子類別必須override抽象方法及抽象屬性
6. 子類別只可繼承一個(抽象)類別 (不可有多重基底類別)
7. 官方文件: 連結
20
介面 (Interface) 以C#7.3為例
21
1. 完全抽象的型別
2. 介面無法實體化(new)、不可有建構式
3. 介面成員: 屬性、方法、事件、索引,沒有欄位(Field)
4. ★介面只能定義規格,且不包含實作
( 將規格與實作分開,解決繼承造成的問題 )
5. ★介面成員不可有存取修飾子(public, private…),介面方法一律為公開層級
6. ★介面方法無法宣告主體(直接分號結尾)
7. C# 8開始支援 預設介面實作
資料來源: 微軟官方文件
.Net 版本 C#預設版本
.NET 7.x C# 11
.NET 6.x C# 10
.NET 5.x C# 9
.NET Core 3.x C# 8
.NET Framework C# 7.3
介面 (Interface)
22
8. Class繼承介面,則要實作介面的所有成員
9. Class只能繼承一個父類別,但可以實作(繼承)多個介面
10.官方文件: 連結
介面範例1 – Class implement multiple interfaces
public interface IShape
{
int GetArea(); //介面定義規格,且不包含實作
}
public interface IColor
{
string Color { get; set; } //定義Property
void SetColor(string color); //介面定義規格,且不包含實作
}
public class Square : IShape, IColor
{
private int _side;
public string Color { get; set; } //必須實作IColor介面的Property
public Square(int side, string color)
{
_side = side;
Color = color;
}
public int GetArea() => _side * _side; //必須實作IShape介面的Method
public void SetColor(string color) => Color = color; //必須實作IColor介面的Method
} 23
//---------------------------------------------------------------------------------------------
class Program
{
static void Main()
{
Square sq = new Square(13, "Black");
Console.WriteLine($"Area={sq.GetArea()},Color={sq.Color}"); //Area=169,Color=Black
sq.SetColor("White");
Console.WriteLine($“Area = {sq.GetArea()},Color = {sq.Color}”); //Area=169,Color=White
Console.ReadLine();
}
}
24
介面範例2 - EncryptHelper
//----------------------------------------【Interface】-----------------------------------------
public interface IEncryptHelper
{
EncryptResponse MD5Hash(string plaintext);
EncryptResponse AESEncrypt(string plaintext, string key);
}
//----------------------------------【Data Model - Response】-----------------------------------
public class EncryptResponse
{
/// <summary>
/// AES加密的初始向量(initialization vector)
/// </summary>
public string IV { get; set; } = null;
/// <summary>
/// Hash的鹽(Salt)
/// </summary>
public string Salt { get; set; } = null;
/// <summary>
/// AES加密的初始向量
/// </summary>
public string Ciphertext { get; set; } = null;
} 25
//--------------------------------【Helper Class – 實作MD5Hash】--------------------------------
public class EncryptHelper : IEncryptHelper
{
public EncryptResponse MD5Hash(string plaintext)
{
//1.隨機產生Salt並合併
EncryptResponse result = new EncryptResponse();
byte[] saltBytes = new byte[16];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create()){
rng.GetBytes(saltBytes); }
result.Salt = Convert.ToBase64String(saltBytes);
byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext ?? string.Empty);
byte[] combinedBytes = new byte[plainBytes.Length + saltBytes.Length];
Buffer.BlockCopy(plainBytes, 0, combinedBytes, 0, plainBytes.Length);
Buffer.BlockCopy(saltBytes, 0, combinedBytes, plainBytes.Length, saltBytes.Length);
//2.進行MD5 Hash雜湊演算法
using (MD5 md5 = MD5.Create())
{
StringBuilder sb = new StringBuilder();
foreach (var item in md5.ComputeHash(combinedBytes)) {
sb.AppendFormat("{0:x2}", item); }
result.Ciphertext = sb.ToString();
return result;
}
} 26
//------------------------------【Helper Class – 實作AESEncrypt】-------------------------------
public EncryptResponse AESEncrypt(string plaintext, string key)
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext);
SHA256 sha256 = SHA256.Create();
MD5 md5 = MD5.Create();
using (Aes aes = Aes.Create())
{
aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
aes.IV = md5.ComputeHash(Encoding.UTF8.GetBytes(key));
using (MemoryStream memoryStream = new MemoryStream())
{
memoryStream.Write(aes.IV, 0, aes.IV.Length);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(),
CryptoStreamMode.Write)) {
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
cryptoStream.FlushFinalBlock();
}
return new EncryptResponse() { IV = Convert.ToBase64String(aes.IV),
Ciphertext = Convert.ToBase64String(memoryStream.ToArray())
};
}
}
}
} 27
//------------------------------------- 【Console主程式】-------------------------------------
public class Program
{
private static IEncryptHelper _helper = new EncryptHelper();
private static void Main(string[] args)
{
Console.Write("Input Plaintext:");
string plaintext = Console.ReadLine();
EncryptResponse md5Result = _helper.MD5Hash(plaintext);
EncryptResponse aesResult = _helper.AESEncrypt(plaintext, "TEST_Key");
Console.WriteLine($"【MD5】n Salt:{md5Result.Salt}n Ciphertext:{md5Result.Ciphertext}n");
Console.WriteLine($"【AES】n IV:{aesResult.IV}n Ciphertext:{aesResult.Ciphertext}n");
Console.ReadLine();
}
}
28
PS. 隨機產生鹽,故每次值不同
可再拆分Class去繼承介面,
並在主程式使用依賴注入(DI)的方式
重點!!!
1. 類別(Class) 是對物件的抽象,
抽象類別(Abstract) 是對類別的抽象,
介面(Interface) 是對行為的抽象。
2. 抽象類別(Abstract) 是從子類別發現共同,泛化出父類別,再
讓子類別繼承父類別。
3. 介面(Interface) 是根本不知道子類別,還不知道如何實現方法,
而預先定義。
29
泛型 (Generic Type)
1. 類型參數概念,讓您設計類別和方法時,延遲一或多個類型的
規格,直到程式碼宣告和具現化該類別或方法為止。
2. 在方法的後面以<T>括住待定義的型別參數
3. 常用泛型:
➢使用泛型 List<T> 類別,而非ArrayList類別
➢使用泛型 Dictionary<TKey, TValue>類別,而非HashTable類別
➢更多內容請參考官方文件: System.Collections.Generic 命名空間
30
泛型優點
1. 型別安全: 編譯時強制確保程式碼的型別安全性
2. 重用性和可擴展性: 泛型介面、類別、方法、事件和委派
3. 效能優化: 執行時會根據實際使用的型別進行優化,相比於非泛
型的程式碼,可以減少額外的型別轉換的開銷 (Boxing)
31
泛型方法範例 (Model)
設計一方法
讓程式輸入List<T>
就可以產生Excel檔案
(PS.此處使用ClosedXML套件)
32
33
泛型方法範例 (Helper的泛型方法)
34
泛型方法範例 (呼叫程式)
Recap
1. Class / Field / Property / Method
2. Constructor
3. Coding Standard
4. 封裝 (public / private / protected)
5. 繼承 (is-a的關係)
6. 多型 (virtual / override / base / this)
7. Abstract / Interface
8. 泛型
35
36
SOLID原則
2
Why>How>What
單一職責原則 (Single responsibility principle)
★ 以一個類別而言,應該只有一個引起它變
化的原因
★ 高內聚:Class成員應該緊密相關(同一職
責),提高可讀性和維護性。
★ 低耦合:不同Class之間應該盡可能獨立,
減少它們之間的依賴關係。(減少改A錯B)
37
開放封閉原則 (Open-Closed Principle)
★ 對於擴展是開放的,對於更改是封閉的
★ 抽象化 (abstract class, interface)
★ 補充:
WiKi Class Diagram (UML-類別圖)
38
里氏替換原則 (Liskov Substitution Principle)
★ 子類別應該能夠完全替代父類別,而不引起錯誤
★ 子類別可以擴展父類別的行為,但不應該修改其父類別
的預期行為
★ 繼承
39
介面隔離原則 (Interface Segregation Principle)
★ 用戶端不應該被迫依賴於它們不使用的介面
★ 介面的單一職責
40
vs.
依賴反轉原則 (Dependency inversion principle)
★ 高層模組不應該依賴低層模組,兩者都應該依賴抽象介面
High-level modules should not import anything from low-level modules.
Both should depend on abstractions (e.g., interfaces).
★ 抽象介面不應該依賴具體實現,具體實現應該依賴抽象介面
Abstractions should not depend on details.
Details (concrete implementations) should depend on abstractions.
★ 「應依賴於抽象而不是一個實體」
41
依賴於實體
42
ReportGenClient
PdfReportGenerator
實體
依賴
需求變動時
1. 若PDF全部改為CSV,則要修改依賴的實體
2. 高階模組(大功能) 調用各個 低階模組(小功能)
底下依賴的模組越多,改動的頻率就會提高
43
依賴於抽象
44
ReportGenClient PdfReportGenerator
依賴
<<Interface>>
IReportGeneraror
不是高階模組去依賴低階模組。
而是高階模組提出它需要的功能,低階模組去實作出這些
功能、達成高階模組的目標。
依賴注入(DI)
45
常見設計模式
3
Why>How>What
練習: 計算機功能 (ver.1)
1. 使用Console主控台
2. 包含「加減乘除」功能
3. 基本的防呆功能
46
Client程式碼
業務邏輯的封裝
1. 使用Console主控台
2. 包含「加減乘除」功能
3. 基本的防呆功能
4. 讓其他專案(MVC, 類別庫…)可以使用
Ctrl + C / Ctrl + V
5. 業務邏輯和操作介面分開
47
業務邏輯的封裝 (ver.2)
➢ 若有其他專案要使用,呼叫Operation類別即可
48
Client程式碼
加入新功能mod運算
1. 使用Console主控台
2. 包含「加減乘除」功能
3. 基本的防呆功能
4. 業務邏輯和操作介面分開
5. 加入新功能
6. 修改Operation類別switch case?
(動到整個業務邏輯)
49
利用繼承降低耦合(ver.3)
50
51
簡單工廠運算類別
52
由工廠負責實體化,透過多型實現Method取得結果
Client程式碼
簡單工廠(Simple Factory)
53
54
定義一個建立物件的介面,
將物件的創建與使用分離
工廠方法模式(Factory Method)
55
工廠介面
56
(工廠方法) Client程式碼
57
58
特點 簡單工廠模式 工廠方法模式
目的
將物件的創建集中在一個class,
使Client端無需關心具體產品的創建細節。
將物件的創建延遲到子類中實現,
使得創建產品的具體過程可以獨立變化。
主要角色 簡單工廠(Simple Factory)
抽象產品(Product)、具體產品(Concrete Product)、
抽象工廠(Factory)、具體工廠(Concrete Factory)
創建過程
簡單工廠負責根據傳入參數或條件判斷創
建具體產品對象。
抽象工廠聲明一個工廠方法,
由具體工廠實現該方法來創建具體產品。
擴展性
當需要新增產品時,需修改簡單工廠的代
碼,不符合開放封閉原則。
新增產品時,只需要新增相應的具體工廠,不需修改
已有代碼,符合開放封閉原則。
適用場景
創建的產品相對簡單,且客戶端僅需知道
產品的類型即可。
創建的產品種類較多,且需要進行擴展,以及需要客
戶端能選擇創建的具體產品。
59
推薦資源
4
推薦資源
1. 【書】大話設計模式(作者:程杰) 連結
2. 設計模式refactoringguru 簡中網站 英文網站
3. 技術部落格:
安德魯的部落格
黑暗執行緒
The Will Will Web
.NET Walker
60
圖/取自博客來
61
總結
5
行為型模式
Behavioral patterns
合成類別或物件到大型的結構
結構型模式
Structural patterns
類別與物件如何互動,以及各
自的責任
創建型模式
Creational patterns
將client從需要實體
化的物件鬆綁出來
62
To be continued
圖/取自refactoring.guru官網
有趣(?)分享
1. 去問ChatGPT以下內容
現在你是一位 C# 專家,負責評估
來進行測驗的考生,你會以每次一
題交互問答的方式進行 共 10 題,
問題難度必須要有辦法評估出考
生的等級 並在最後提供你的答案,
評分與評語,接下來在考生告訴你
他的名字後測驗開始
63
2. Shopify Meeting Cost Calculator
From Shopify
64
若你一輩子都待在後座,就永遠無法學會開車
Thank you for listening.

More Related Content

PDF
Java SE 7 技術手冊第五章草稿 - 何謂封裝?
PDF
Java物件導向
PPTX
物件導向設計原則:SOLID + DI
DOC
物件導向程式設計課程講義(98 ges hi版)
PDF
twMVC#27 | C# 7.0 新功能介紹
PPTX
Study research in April
PPTX
重構—改善既有程式的設計(chapter 7)
PDF
Java 開發者的函數式程式設計
Java SE 7 技術手冊第五章草稿 - 何謂封裝?
Java物件導向
物件導向設計原則:SOLID + DI
物件導向程式設計課程講義(98 ges hi版)
twMVC#27 | C# 7.0 新功能介紹
Study research in April
重構—改善既有程式的設計(chapter 7)
Java 開發者的函數式程式設計

Similar to 淺談C#物件導向與DesignPattern.pdf (20)

PPT
PPTX
2016.8.1 Design Pattern Eric
PDF
Java SE 7 技術手冊投影片第 05 章 - 物件封裝
PPT
SCJP ch12
PPT
教學投影片01_Vb2005
PDF
物件導向系統分析與設計(建國)
PPT
SCJP ch17
PDF
JCConf 2023 - 深入淺出 Java 21 功能
PPT
SCJP ch09
PDF
JavaScript 快速跳坑指南
PPTX
Code guidelines
PPT
Java SE 8 技術手冊第 4 章 - 認識物件
PDF
CH04:認識物件
PDF
lambda/closure – JavaScript、Python、Scala 到 Java SE 7
PDF
由一个简单的程序谈起――之三(精华)
PPTX
Metro Style Apps from C++ Developers' View
PPT
SCJP ch11
PDF
Java SE 7 技術手冊投影片第 04 章 - 認識物件
2016.8.1 Design Pattern Eric
Java SE 7 技術手冊投影片第 05 章 - 物件封裝
SCJP ch12
教學投影片01_Vb2005
物件導向系統分析與設計(建國)
SCJP ch17
JCConf 2023 - 深入淺出 Java 21 功能
SCJP ch09
JavaScript 快速跳坑指南
Code guidelines
Java SE 8 技術手冊第 4 章 - 認識物件
CH04:認識物件
lambda/closure – JavaScript、Python、Scala 到 Java SE 7
由一个简单的程序谈起――之三(精华)
Metro Style Apps from C++ Developers' View
SCJP ch11
Java SE 7 技術手冊投影片第 04 章 - 認識物件
Ad

淺談C#物件導向與DesignPattern.pdf