摘要:物件導向系列菜單9-『物件導向特性-封裝(Encapsulation)』
包大人:這是秘密~~
包大人:要做的乾淨~懂嗎!?
正文開始
本文主題
「存取修飾詞』
『封裝的使用』
『存取修飾詞』
存取修飾詞 | 說明 |
public | 無任何存取限制。 |
internal | 只能在自已類別與專案(組件)中其它類別進行存取。(不包含其它專案)。 |
protected | 只能在自已類別和子類別中存取。 |
protected internal | 只能在目前專案(組件)與子類別存取。 |
private | 在自已類別存取。 |
『封裝的使用』
1.封裝表示讓一個類別對其他類別隱藏特定資訊,這樣有助於防止程式發生臭蟲。
2.當你回頭編程已經有好一陣子沒有沒看程式碼時,很容易就會忘記當初你要它做什麼
,那正是封裝能夠大展身手的地方。
封裝的精神
1.只要在必要時才讓欄位與方法為public
2.將物件想成黑箱
你並不在意該方法到底是怎麼運作的。你只在意它接受你提供的輸入,並回傳正確的結果。
3.減少程式BUG,因為相依性減少了
我們來建立兩程個類別範例來說明何謂『不良好封裝性』與『良好封裝性』。
程式說明
『封裝性不良好類別』
首先我們先建立個,幾乎沒有使用到封裝的類別BagFamer,
這個類別主要是計算該農場需要多少袋飼料來養牛。
農場所需飼料(袋) = 牛隻數量 * 每隻牛需要的飼料
BagOfFeed = NumberOfCows * FeedMulitplier
三個欄位
FeedMulitplier : 存放一隻牛需要多少袋飼料。
NumberOfCows : 存放這個農場有幾隻牛。
BagOfFeed : 存放這個農場需要多少飼料。
一個方法
CalculateBagOfFeed() : 計算這個農場需要多少飼料。
Tip.
共同遵循的約定與慣例。
假設有一欄位PeopleNumber
私有(private)欄位會表示 peropleNumber
公用(public)欄位會表示 PeropleNumber
//建立類別使用欄位儲存牛隻數量(NumberOfCows),並且乘上一個數字(FeedMultiplier)
//,計算出需要多少袋飼料餵養牛。
public class BadFamer
{
//欄位存放一隻牛需要多少袋飼料,預設30
public const int FeedMultiplier = 30;
//欄位存放有幾隻牛
public int NumberOfCows;
//欄位存放需要有多少飼料
public int BagOfFeed;
//計算農場需要多少袋飼料的方法。
public void CalculateBagOfFeed()
{
BagOfFeed = NumberOfCows * FeedMultiplier;
}
}
BadFamer myFarmer;
public Form1()
{
InitializeComponent();
//實體化類別。
myFarmer = new BadFamer ();
}
private void button1_Click(object sender, EventArgs e)
{
//設定NumberOfCows欄位有幾隻牛。
myFarmer.NumberOfCows = (int)numericUpDown1.Value;
myFarmer.CalculateBagOfFeed();
textBox1.Text = "I need " + myFarmer.BagOfFeed + "bags of feed for " + myFarmer.Numb erOfCows + " cows";
}
執行結果
雖然不使用封裝重構,可以得到正確的執行結果。但如果有一天,
一位來維護你的類別的新手Coder(就是小弟我),來不小心加了一行。
myFarmer.BagOfFeed = 30;
變成下列程式碼。
private void button1_Click(object sender, EventArgs e)
{
//設定NumberOfCows欄位有幾隻牛。
myFarmer.NumberOfCows = ( int)numericUpDown1.Value;
myFarmer.CalculateBagOfFeed();
myFarmer.BagOfFeed = 30;
textBox1.Text = "I need " + myFarmer.BagOfFeed + "bags of feed for " + myFarmer.NumberOfCows + " cows";
}
執行結果
這時你就GG了,這時是可以罵他,但你也該想想為什麼你寫的類別,
BagOfFeed這東西你明明只能讀,但為什麼他可以寫,
不該出現的東西出現了,這就代表封裝性沒有做好。
『封裝性良好類別』
接下來我們在建立一個封裝性良好的類別GoodFamer。
三個欄位
numberOfCows : NumberOfCows屬性的支援欄位。存放有農場有幾隻牛。
feedMultiplier : 存放每隻牛需要多少飼料。
bagOfFeed: 存放農場需要多少飼料。
三個屬性
BagOfFeed : 設立『唯讀』。讀時回傳bagOfFeed欄位。主要用來給別人看農場需要多少飼料。
NumberOfCows : 設立『讀寫』。
讀時回傳numberOfCows欄位。主要用來給人知道農場有多少牛。
寫時將值寫入numberOfCows欄位,並直接計算農場所需飼料存入bagOfFeed屬性。
FeedMultipler:設立『唯讀』。讀時回傳feedMultipler欄位。主要用來給別人看每頭牛需要多少飼料。
public class GoodFamer
{
//會變成『NumberOfCows屬性(properties)』的支援欄位(backing field)
//不需要public的就設private
private int numberOfCows;
private const int feedMultiplier =30;
private int bagOfFeed;
//這個就是屬性
public int FeedMultiplier { get { return feedMultiplier; } }
//BagsOfFeed屬性,只可以讀取。
public int BagsOfFeed
{
get
{
//讀取時回傳bagOfFeed欄位。
return bagOfFeed;
}
}
//這是NumberOfCows屬性(properties)的宣告
public int NumberOfCows
{
get
{
//讀取NumberOfCows屬性,就會回傳numberOfCows欄位
return numberOfCows;
}
set
{
//會將值寫入numberOfCows欄位。
//並直接進行農場所需的飼料計算。並存入BagsOfFeed屬性.
numberOfCows = value;
bagOfFeed = numberOfCows * FeedMultiplier;
}
}
}
呼叫~
GoodFamer myFarmer;
public Form1()
{
InitializeComponent();
//實體化類別。
myFarmer = new GoodFamer ();
}
private void button1_Click(object sender, EventArgs e)
{
//設定NumberOfCows欄位有幾隻牛。
myFarmer.NumberOfCows = ( int)numericUpDown1.Value;
textBox1.Text = "I need " + myFarmer.BagsOfFeed + "bags of feed for " + myFarmer.NumberOfCows + " cows";
}
執行結果
從上面執行結果,還是和上支程式一樣,那我為何還需要改程式呢??
假設我和上支程式碼做一樣的修改,增加一行
myFarmer.BagsOfFeed = 30
程式碼改為。
private void button1_Click(object sender, EventArgs e)
{
//設定NumberOfCows欄位有幾隻牛。
myFarmer.NumberOfCows = ( int)numericUpDown1.Value;
//加入這行,試試看。
myFarmer.BagsOfFeed = 30
textBox1.Text = "I need " + myFarmer.BagsOfFeed + "bags of feed for " + myFarmer.NumberOfCows + " cows";
}
執行結果
總結一下『封裝性』的概念就是
『不需要出現的東西就不要出現』
參考書籍