Day 9 我們學會使用 Method(方法),把一段程式邏輯整理成可以重複使用的功能。
例如:
decimal CalculateTotal(
decimal price,
int quantity)
{
return price * quantity;
}
但是目前一筆 Order 的資料仍然是分開的:
string product = "TXF";
decimal price = 250.5m;
int quantity = 5;
實際上:
product
price
quantity
都在描述同一件事情:
一筆 Order。
如果資料越來越多:
string product = "TXF";
decimal price = 250.5m;
int quantity = 5;
string side = "Buy";
string status = "Pending";
Variable 也會越來越零散。
因此今天要解決的問題是:
如何把屬於同一個概念的資料與功能組織在一起?
這就是:
Class(類別)
new
Class的運作關係可以這樣看:
Class
→ 定義一種 Type
↓
new
↓
Object / Instance
→ 實際建立出來的物件
目前一筆 Order:
string product = "TXF";
decimal price = 250.5m;
int quantity = 5;
程式看到的是三個獨立的 Variable:
product
price
quantity
但我們真正想表達的是:
Order
├── Product
├── Price
└── Quantity
也就是:
這些資料其實都屬於同一筆 Order。
因此可以建立:
class Order
{
public string Product = "";
public decimal Price;
public int Quantity;
}
現在:
Product
Price
Quantity
不再只是三個零散的資料,而是共同屬於:
Order
所以 Class 第一個重要用途就是:
把屬於同一個概念的資料與行為組織在一起。
前面我們使用過:
int quantity = 5;
decimal price = 250.5m;
string product = "TXF";
其中:
int
decimal
string
都是 Type(型別)。
例如:
int quantity;
可以拆成:
int
→ Type
quantity
→ Variable
現在我們建立:
class Order
{
}
代表:
我們自己定義了一種新的 Type:
Order。
因此也可以寫:
Order order;
拆開來看:
Order
→ Type
order
→ Variable
可以這樣比較:
int
→ C# 已經提供的 Type
Order
→ 我們透過 Class 自己定義的 Type
所以 Class 不只是把程式碼包起來。
更重要的是:
Class 可以讓我們定義自己的 Type。
回到:
class Order
{
public string Product = "";
public decimal Price;
public int Quantity;
}
其中:
Product
Price
Quantity
稱為:
Field(欄位)
可以理解成:
Order
│
├── Product → string
├── Price → decimal
└── Quantity → int
Field 用來保存 Object 的資料。
所以:
Field 描述這類 Object 需要保存哪些資料。
public 是什麼?目前 Field 前面都有:
public
今天先簡單理解成:
Class 外部可以存取這個成員。
例如之後可以寫:
order.Price
今天暫時使用 public Field,目的是先專心理解 Class 與 Object。
實務上通常不會直接把所有 Field 公開。
下一篇介紹 Property 與 Encapsulation 時,再正式處理這個問題。
new 建立 Object目前:
class Order
{
public string Product = "";
public decimal Price;
public int Quantity;
}
只是定義:
Order 這種 Type
還沒有真正建立任何一筆 Order。
要建立實際的 Object,可以使用:
Order order = new Order();
這行可以拆成:
Order
→ Type
order
→ Variable
new Order()
→ 建立新的 Order Object
整個流程:
Order Class
↓
new Order()
↓
Order Object
↓
order
所以先記住:
Class 負責定義,
new負責建立 Object。
建立 Object:
Order order = new Order();
接著可以設定這筆 Order 的資料:
order.Product = "TXF";
order.Price = 250.5m;
order.Quantity = 5;
現在 order 可以理解成:
order
│
├── Product = "TXF"
├── Price = 250.5
└── Quantity = 5
以前我們使用:
string product = "TXF";
decimal price = 250.5m;
int quantity = 5;
現在則變成:
order.Product
order.Price
order.Quantity
從程式碼就可以直接看出:
這些資料都屬於同一筆 Order。
.:存取 Object 的成員例如:
order.Price
中間的:
.
稱為:
Member Access Operator(成員存取運算子)
目前可以簡單理解:
order.Price
order
→ 哪一個 Object
Price
→ 使用這個 Object 的哪個成員
例如:
order.Product
order.Price
order.Quantity
之後也會看到:
order.CalculateTotal();
都是透過 . 存取 Object 的成員。
到這裡需要先分清楚兩個最重要的概念:
Class
→ 定義
Object
→ 根據 Class 實際建立出來的物件
例如:
class Order
{
public string Product = "";
public decimal Price;
public int Quantity;
}
這是在定義:
Order 這種 Type
而:
Order order = new Order();
才真正建立出:
Order Object
根據某個 Class 建立出來的 Object,也常稱為這個 Class 的:
Instance(執行個體)
因此:
new Order()
可以理解成:
建立一個
OrderObject,也就是建立一個OrderInstance。
目前先記成:
Class
→ 定義
↓ new
Object / Instance
→ 實際建立出來的物件
初學階段不用特別糾結 Object 與 Instance 的差異,把它們先視為非常接近的概念即可。
目前建立一筆 Order,需要先:
Order order = new Order();
再逐一設定:
order.Product = "TXF";
order.Price = 250.5m;
order.Quantity = 5;
雖然可以正常使用,但有一個問題。
我們可能只寫:
Order order = new Order();
然後忘記設定資料。
如果希望:
建立 Order 的同時,就把需要的資料準備好。
可以使用:
Constructor(建構函式)
把 Order 改成:
class Order
{
public string Product;
public decimal Price;
public int Quantity;
public Order(
string product,
decimal price,
int quantity)
{
Product = product;
Price = price;
Quantity = quantity;
}
}
現在建立 Object 時,可以直接提供資料:
Order order =
new Order("TXF", 250.5m, 5);
建立完成後:
order
│
├── Product = "TXF"
├── Price = 250.5
└── Quantity = 5
因此 Constructor 可以先理解成:
Constructor 會在建立 Object 時執行,用來初始化 Object 的資料。
回頭看:
public Order(
string product,
decimal price,
int quantity)
{
Product = product;
Price = price;
Quantity = quantity;
}
可以拆成:
public
→ Access Modifier
Order
→ Constructor Name
string product
decimal price
int quantity
→ Parameters
{ ... }
→ Constructor Body
Constructor 有兩個重要特徵。
第一:
Constructor Name
→ 和 Class Name 相同
例如:
class Order
對應:
public Order(...)
第二:
Constructor
→ 沒有 Return Type
所以是:
public Order(...)
不是:
public void Order(...)
Day 9 學過:
Parameter
→ 定義需要什麼資料
Argument
→ 呼叫時實際傳入什麼資料
Constructor 也是一樣。
宣告:
public Order(
string product,
decimal price,
int quantity)
其中:
product
price
quantity
是 Parameters。
建立 Object:
new Order("TXF", 250.5m, 5);
其中:
"TXF"
250.5m
5
是 Arguments。
整個流程:
"TXF", 250.5m, 5
Arguments
↓
product, price, quantity
Parameters
↓
Constructor
↓
初始化 Object
↓
Product = "TXF"
Price = 250.5
Quantity = 5
可以看到 Day 9 的 Parameter / Argument 概念,在 Constructor 這裡仍然可以繼續使用。
Class 不只能保存資料。
還可以定義:
Behavior(行為)
例如 Day 9 的:
decimal CalculateTotal(
decimal price,
int quantity)
{
return price * quantity;
}
這個功能本來就在處理 Order。
而:
Price
Quantity
本來就是 Order 自己的資料。
因此可以把 CalculateTotal() 放進 Order:
class Order
{
public string Product;
public decimal Price;
public int Quantity;
public Order(
string product,
decimal price,
int quantity)
{
Product = product;
Price = price;
Quantity = quantity;
}
public decimal CalculateTotal()
{
return Price * Quantity;
}
}
現在 Order 同時具有:
Order
│
├── Data
│ ├── Product
│ ├── Price
│ └── Quantity
│
└── Behavior
└── CalculateTotal()
所以 Class 更完整的概念就是:
把屬於同一個概念的資料,以及操作這些資料的行為組織在一起。
現在建立:
Order order =
new Order("TXF", 250.5m, 5);
可以直接:
decimal total =
order.CalculateTotal();
結果:
1252.5
Day 9 原本需要:
CalculateTotal(price, quantity);
現在只需要:
order.CalculateTotal();
為什麼?
因為:
order自己已經擁有Price與Quantity。
可以理解成:
order
│
├── Price = 250.5
├── Quantity = 5
│
└── CalculateTotal()
↓
Price × Quantity
這種屬於 Object,並且可以使用這個 Object 自己資料的 Method,稱為:
Instance Method(執行個體方法)
這是從 Day 9 到 Day 10 很重要的一個變化:
以前
資料
↓
傳進 Method
現在
Object
├── 自己的資料
└── 操作自己資料的 Method
Day 9 原本寫:
bool IsValidOrder(
decimal price,
int quantity)
{
return price > 0 &&
quantity > 0;
}
但是:
Price
Quantity
同樣都是 Order 自己的資料。
因此也可以把驗證放進 Order:
public bool IsValid()
{
return Price > 0 &&
Quantity > 0;
}
現在可以:
if (order.IsValid())
{
Console.WriteLine(
order.CalculateTotal()
);
}
這時 Order 已經同時具有:
Order
│
├── Data
│ ├── Product
│ ├── Price
│ └── Quantity
│
└── Behavior
├── IsValid()
└── CalculateTotal()
也就是:
Object 不只保存自己的資料,也可以擁有操作這些資料的行為。
現在把今天學到的內容組合起來:
class Order
{
public string Product;
public decimal Price;
public int Quantity;
public Order(
string product,
decimal price,
int quantity)
{
Product = product;
Price = price;
Quantity = quantity;
}
public bool IsValid()
{
return Price > 0 &&
Quantity > 0;
}
public decimal CalculateTotal()
{
return Price * Quantity;
}
}
整個 Class 可以拆成:
Order Class
│
├── Fields
│ ├── Product
│ ├── Price
│ └── Quantity
│
├── Constructor
│ └── Order(...)
│
└── Instance Methods
├── IsValid()
└── CalculateTotal()
也就是:
Class
│
├── 定義資料
│
├── 定義初始化方式
│
└── 定義行為
這就是今天最重要的 Class 心智模型。
建立一筆 Order:
Order order =
new Order("TXF", 250.5m, 5);
接著使用它:
if (order.IsValid())
{
decimal total =
order.CalculateTotal();
Console.WriteLine(
$"Product: {order.Product}, Total: {total}"
);
}
else
{
Console.WriteLine("Invalid Order");
}
結果:
Product: TXF, Total: 1252.5
整個流程:
Order Class
↓
new Order(...)
↓
Constructor
↓
初始化 Object
↓
order
↓
IsValid()
↓
CalculateTotal()
↓
輸出結果
從這裡開始,程式不再只是:
零散的 Variable
+
零散的 Method
而是:
Order Object
│
├── 自己的資料
└── 自己的行為
Class 只需要定義一次:
class Order
{
// ...
}
但可以建立很多不同的 Object:
Order order1 =
new Order("TXF", 250.5m, 5);
Order order2 =
new Order("MXF", 120m, 10);
可以理解成:
Order Class
│
┌──────┴──────┐
↓ ↓
order1 order2
│ │
TXF MXF
所以:
Class 只需要定義一次,但可以建立很多 Object,每個 Object 都保存自己的資料。
目前我們使用的是 Top-level Statements。
如果暫時都寫在同一個 Program.cs,可以像這樣:
Order order =
new Order("TXF", 250.5m, 5);
Console.WriteLine(
order.CalculateTotal()
);
// Class 放在 Top-level Statements 後面
class Order
{
// ...
}
也可以另外建立:
Order.cs
專門放:
class Order
{
// ...
}
初學階段兩種方式都可以。
當 Class 開始變大時,拆成獨立的 .cs 檔案通常會更容易整理。
今天我們解決的問題是:
如何把屬於同一個概念的資料與功能組織在一起?
答案就是:Class
class Order
{
// ...
}
用來定義一種新的 Type,描述這類 Object 有哪些資料與行為。
例如:
public decimal Price;
用來保存 Object 的資料。
例如:
Order order =
new Order("TXF", 250.5m, 5);
其中:
Order
→ Type
order
→ Variable
new
→ 建立 Object
new Order(...)
→ 建立一個 Order Object / Instance
例如:
public Order(
string product,
decimal price,
int quantity)
會在建立 Object 時執行,用來初始化資料。
例如:
order.CalculateTotal();
Instance Method 可以直接使用這個 Object 自己的資料。
整篇可以整理成:
原本:
Product
Price
Quantity
都是零散 Variable
↓
Class
↓
定義新的 Type
↓
Field
描述有哪些資料
↓
new
↓
Object / Instance
↓
Constructor
初始化 Object
↓
Instance Method
使用 Object 自己的資料