封裝 (物件導向程式設計)

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

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

適當的封裝,可以將物件使用介面的程式實作部份隱藏起來,不讓用戶看到,同時確保用戶無法任意更改物件內部的重要資料,若想接觸資料只能通過公開接入方法(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'

參考文獻

  1. ^ Scott, Michael Lee. Programming language pragmatics 2. Morgan Kaufmann. 2006: 481. ISBN 978-0-12-633951-2. Encapsulation mechanisms enable the programmer to group data and the subroutines that operate on them together in one place, and to hide irrelevant details from the users of an abstraction. 
  2. ^ Dale, Nell B.; Weems, Chip. Programming and problem solving with Java 2nd. Jones & Bartlett. 2007: 396. ISBN 978-0-7637-3402-2. 
  3. ^ Mitchell, John C. Concepts in programming languages. Cambridge University Press. 2003: 522. ISBN 978-0-521-78098-8. 
  4. ^ Pierce, Benjamin. Types and Programming Languages. MIT Press. 2002: 266. ISBN 978-0-262-16209-8. 
  5. ^ Rogers, Wm. Paul. Encapsulation is not information hiding. JavaWorld. 18 May 2001 [2019-11-22]. (原始內容存檔於2013-10-29). 
  6. ^ Connolly, Thomas M.; Begg, Carolyn E. Ch. 25: Introduction to Object DMBS § Object-oriented concepts. Database systems: a practical approach to design, implementation, and management 4th. Pearson Education. 2005: 814. ISBN 978-0-321-21025-8. 
  7. ^ Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. Design Patterns. Addison-Wesley. 1994. ISBN 978-0-201-63361-0. 
  8. ^ 蕭蕭. 怎样理解“组合优于继承”以及“OO的反模块化”,在这些方面FP具体来说有什么优势?. 2017-06-09 [2019-11-22]. (原始內容存檔於2020-02-04) (中文(簡體)).