物件導向系列菜單 9-『 物件導向特性-封裝 ( Encapsulation ) 』
c井字號 物件導向
Lastmod: 2019-12-11

存取修飾詞

存取修飾詞 說明
public 無任何存取限制。
internal 只能在自已類別與專案(組件)中其它類別進行存取。(不包含其它專案)。
protected 只能在自已類別和子類別中存取。
protected internal 只能在目前專案(組件)與子類別存取。
private 在自已類別存取。

封裝的使用

封裝的使用時機 :

  • 封裝表示讓一個類別對其他類別隱藏特定資訊,這樣有助於防止程式發生臭蟲。
  • 當你回頭編程已經有好一陣子沒有沒看程式碼時,很容易就會忘記當初你要它做什麼,那正是封裝能夠大展身手的地方。

封裝的精神

  • 只要在必要時才讓欄位與方法為public
  • 將物件想成黑箱。你並不在意該方法到底是怎麼運作的。你只在意它接受你提供的輸入,並回傳正確的結果。
  • 減少程式BUG,因為相依性減少了。

封裝性不良好類別

首先我們先建立個,幾乎沒有使用到封裝的類別BagFamer,這個類別主要是計算該農場需要多少袋飼料來養牛。

農場所需飼料(袋) = 牛隻數量 * 每隻牛需要的飼料 BagOfFeed = NumberOfCows * FeedMulitplier

三個欄位

  • FeedMulitplier : 存放一隻牛需要多少袋飼料。
  • NumberOfCows : 存放這個農場有幾隻牛。
  • BagOfFeed : 存放這個農場需要多少飼料。

Tip.共同遵循的約定與慣例。

假設有一欄位PeopleNumber
私有(private)欄位會表示  peropleNumber
公用(public)欄位會表示  PeropleNumber

CalculateBagOfFeed() : 

	//建立類別使用欄位儲存牛隻數量(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.NumberOfCows + " cows";
       
        }

執行結果

ScreenClip.png

雖然不使用封裝重構,可以得到正確的執行結果。但如果有一天,一位來維護你的類別的新手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";
       
        }

執行結果

ScreenClip.png

這時你就GG了,這時是可以罵他,但你也該想想為什麼你寫的類別,為什麼這 BagOfFeed 這東西你明明只能讀,但為什麼你也開給他可以寫,不該出現的東西出現了,這就代表封裝性沒有做好。

封裝性良好類別

接下來我們在建立一個封裝性良好的類別GoodFamer。

三個欄位

  • numberOfCows : NumberOfCows屬性的支援欄位。存放有農場有幾隻牛。
  • feedMultiplier : 存放每隻牛需要多少飼料。
  • bagOfFeed: 存放農場需要多少飼料。

二個屬性

  • BagOfFeed : 設立『唯讀』。讀時回傳bagOfFeed欄位。主要用來給別人看農場需要多少飼料。
  • NumberOfCows : 設立『讀寫』。

讀時回傳numberOfCows欄位。主要用來給人知道農場有多少牛。寫時將值寫入numberOfCows欄位,並直接計算農場所需飼料存入bagOfFeed屬性。

 	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";
       
        }

執行結果

ScreenClip.png

從上面執行結果,還是和上支程式一樣,那我為何還需要改程式呢??假設我和上支程式碼做一樣的修改,增加一行

	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";
       
        }

結果

ScreenClip.png

參考資料

comments powered by Disqus