物件導向程式設計方法中,封裝(英語:Encapsulation)是指,一種將抽象性函數介面的實作細節部份包裝、隱藏起來的方法。同時,它也是一種防止外界呼叫端,去存取物件內部實作細節的手段,這個手段是由程式語言本身來提供的。封裝被視為是物件導向的四項原則之一。

Thumb
封裝,一種將抽象性函數介面的實作細節部份包裝、隱藏起來的方法

適當的封裝,可以將物件使用介面的程式實作部份隱藏起來,不讓用戶看到,同時確保用戶無法任意更改物件內部的重要資料,若想接觸資料只能通過公開接入方法(Publicly accessible methods)的方式( 如:"getters" 和"setters")。它可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。[來源請求]


解釋

在物件導向的語言裏,封裝往往指以下兩個相關聯但是獨立的概念,有時候這兩者是存在因果關係。[1][2]

  1. 一種程式語言的機制,限制直接訪問某些對象的部件。[3][4]
  2. 一種程式語言的結構體,其將數據和操作該數據的方法綁在一起,提供了便利性。[5][6]

一些程式語言的研究者和學者將定義①或者定義①+②作為辨認一門語言是否為物件導向語言的標準之一。一些程式語言提供了閉包作為封裝,但是這種功能不屬於物件導向的範疇。

在許多程式語言里,組件並不會自動隱藏並且能夠被重寫,因此,一些傾向於定義②的人會將資訊隱藏(information hiding)作為一個單獨的定義③列舉出來。

在使用類的大多物件導向的程式語言中,雖然封裝是被支援的,但是仍有其他替代品可以選擇。

封裝和繼承

《Design Patterns》的作者們曾經大篇幅地討論封裝和繼承的矛盾。根據他們自身的經驗,設計師們濫用繼承。他們認為繼承將破壞封裝,考慮父類別的實現細節將暴露給子類。[7]

父類別的內部實現對於子類來說是不透明的(實現一個子類時, 你需要了解父類別的實現細節, 以此決定是否需要重寫某個方法)。[8]同時,一旦父類別被修改,因為子類依賴着父類別,所以子類的實現也需要被重新審視。

資訊隱藏

封裝可以隱藏成員變數以及成員函數,對象的內部實現通常被隱藏,並用定義代替。舉個例子,僅僅對象自身的方法能夠直接接觸或者操作這些成員變數。隱藏對象內部資訊能供保證一致性:當用戶擅自修改內部部件的數據,這可能造成內部狀態不一致或者不可用;隱藏對象內部資訊能阻止這種後果。一個眾所周知的好處是,降低系統的複雜度和提高健壯性

大多數語言(如:C++、C#、 Delphi、Java)通過設定等級去控制內部資訊隱藏,經典的是通過保留字 public 暴露資訊和 private隱藏資訊。一些語言(如: SmalltalkRuby )只允許對象去訪問隱藏資訊。

通常,也是存在方法去暴露隱藏資訊,如通過反射(Ruby、Java、C#、etc.)或名字修飾(Python)。

程式範例

保留字

C#範例

這是一段C#代碼,演示了如何使用private關鍵字限制變數的訪問:

namespace Encapsulation 
{
	class Program 
	{
		public class Account 
		{
			private decimal accountBalance = 500.00m;

			public decimal CheckBalance() 
			{
				return accountBalance;
			}
		}

		static void Main() 
		{
			Account myAccount = new Account();
			decimal myBalance = myAccount.CheckBalance();

			/* Main方法能够通过public的“CheckBalance”方法确认账户余额,但是不能更改它 */
		}
	}
}

JAVA範例

下面是Java的演示程式:

public class Employee {
    private BigDecimal salary = new BigDecimal(50000.00);
    
    public BigDecimal getSalary() {
        return salary;
    }

    public static void main() {
        Employee e = new Employee();
        BigDecimal sal = e.getSalary();
    }
}

名字修飾(Name mangling)

下面是Python的要給實例,Python並不支援隱藏變數。然而約定俗成,_var 形式的變數被認為是私有變數。

class Car: 
    def __init__(self):
        self._maxspeed = 200
 
    def drive(self):
        print(f'maximum speed is {self._maxspeed}')
 
redcar = Car()
redcar.drive()  # 打印 'maximum speed is 200'

redcar._maxspeed = 10
redcar.drive()  # 打印 'maximum speed is 10'

參考文獻

Wikiwand in your browser!

Seamless Wikipedia browsing. On steroids.

Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.

Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.