初版是為 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您還可以幫助支援 Lua 專案


16.4 – 私密性

許多人認為私密性是物件導向語言中不可或缺的一部分;每個物件的狀態應為其自己的內部事務。在某些 OO 語言中,例如 C++ 和 Java,您可以控制物件欄位(也稱為實例變數)或方法是否在物件外部可見。其他語言,例如 Smalltalk,會將所有變數設為私有,而將所有方法設為公開。第一個 OO 語言 Simula 沒有提供任何類型的保護。

我們先前展示過的 Lua 中物件的主要設計並未提供私密性機制。這部分是因為我們使用一般結構(表格)來表示物件。但這也反映了 Lua 背後的一些基本設計決策。Lua 並非用於建置大型程式,讓許多程式設計師長時間參與其中。恰恰相反,Lua 的目標是中小型程式,通常是較大系統的一部分,通常由一位或幾位程式設計師,甚至是非程式設計師開發。因此,Lua 避免過多的冗餘和人為限制。如果您不想存取物件中的某些內容,請不要這麼做

儘管如此,Lua 的另一個目標是靈活,透過提供元機制給程式設計師,讓她能夠模擬許多不同的機制。儘管 Lua 中物件的基本設計並未提供私密性機制,但我們可以用不同的方式實作物件,以便進行存取控制。儘管這種實作並不常使用,但了解它是有益的,因為它探索了 Lua 的一些有趣角落,而且它可能是其他問題的良好解決方案。

這種替代設計的基本概念是透過兩個表格來表示每個物件:一個表示其狀態;另一個表示其運算,或其介面。物件本身是透過第二個表格存取的,也就是透過組成其介面的運算存取的。為了避免未經授權的存取,表示物件狀態的表格不會保留在另一個表格的欄位中;而是僅保留在方法的封閉中。例如,要使用此設計來表示我們的銀行帳戶,我們可以建立執行下列工廠函式的物件

    function newAccount (initialBalance)
      local self = {balance = initialBalance}
    
      local withdraw = function (v)
                         self.balance = self.balance - v
                       end
    
      local deposit = function (v)
                        self.balance = self.balance + v
                      end
    
      local getBalance = function () return self.balance end
    
      return {
        withdraw = withdraw,
        deposit = deposit,
        getBalance = getBalance
      }
    end
首先,函數會建立一個表格來保留內部物件狀態,並將其儲存在區域變數 self 中。接著,函數會為物件的每個方法建立封閉函數(也就是巢狀函數的實例)。最後,函數會建立並傳回外部物件,將方法名稱對應到實際的方法實作。這裡的重點是,這些方法不會將 self 當作額外的參數;相反地,它們會直接存取 self。由於沒有額外的引數,我們不會使用冒號語法來處理此類物件。方法的呼叫方式就像任何其他函數一樣
    acc1 = newAccount(100.00)
    acc1.withdraw(40.00)
    print(acc1.getBalance())     --> 60

此設計讓儲存在 self 表格中的任何內容都能完全保密。在 newAccount 傳回後,就沒有辦法直接存取該表格。我們只能透過在 newAccount 內部建立的函數來存取它。雖然我們的範例只將一個實例變數放入私人表格中,但我們可以將物件的所有私人部分儲存在該表格中。我們也可以定義私人方法:它們就像公開方法,但我們不會將它們放入介面中。例如,我們的帳戶可能會對餘額超過特定限制的使用者提供額外 10% 的信用,但我們不希望使用者能夠存取此計算的詳細資料。我們可以按以下方式實作這項功能

    function newAccount (initialBalance)
      local self = {
        balance = initialBalance,
        LIM = 10000.00,
      }
    
      local extra = function ()
        if self.balance > self.LIM then
          return self.balance*0.10
        else
          return 0
        end
      end
    
      local getBalance = function ()
        return self.balance + self.extra()
      end
    
      ...
同樣地,沒有任何使用者可以直接存取 extra 函數。