統一訪問原則

計算機編程統一訪問原則(英語:Uniform access principle)是由伯特蘭·邁耶在《Object-Oriented Software Construction英語Object-Oriented Software Construction》中提出。它指出「一個模塊提供的所有服務都應該通過統一的符號提供,與它們是通過存儲還是通過計算實現無關」。[1]這個原則普遍適用於面向對象編程語言的語法。使用屬性、預先計算的屬性或對象的方法/查詢實現,調用時沒有語法差異。

雖然大多數示例都聚焦在該原則的「讀取」方面(即檢索值),但伯特蘭·邁耶表示,該原則「寫入」(即修改值)方面更難實現。[2]

解釋

伯特蘭·邁耶解決的問題涉及大型軟件項目或軟件庫的維護。有時在開發或維護軟件時,有必要在大量代碼到位後,把簡單的屬性訪問轉換為方法調用的方式。編程語言通常使用不同的語法來訪問屬性和調用方法。例如,object.somethingobject.something()。在當今流行的編程語言中,語法變化需要修改所有使用該屬性的地方的源代碼。這可能需要在大量源代碼中的許多不同位置修改源代碼。或者更糟的是,如果修改是在數百個客戶使用的對象庫中進行的,那麼這些客戶中的每一個都必須找到並修改在他們自己的代碼中使用該屬性的所有位置,然後重新編譯他們的程序。

以相反的方向(從方法調用到簡單屬性訪問)確實不是問題,因為人們總是可以只保留函數並讓它簡單地返回屬性值。

伯特蘭·邁耶認識到軟件開發人員需要以這樣一種方式編寫代碼,以最大限度地減少或消除由於將對象屬性轉換為方法調用或反之亦然的更改而導致的代碼級聯修改。為此,他提出了統一訪問原則。

許多編程語言並不嚴格支持統一訪問原則,但支持它自己的某種形式。多種編程語言提供的屬性以不同的方式解決了伯特蘭·邁耶用統一訪問原則解決的問題。屬性提供了一種調用對象方法的方法,而不是只提供一種統一表示法。屬性對實例的方法調用提供了與特性(attribute)訪問相同的表示法。當然,傳統的方法調用語法仍然可用。

統一訪問原則的例子

如果編程語言使用了方法調用的語法形式,看起來類似於:

// Assume print displays the variable passed to it, with or without parens
// Set Foo's attribute 'bar' to  value 5.
Foo.bar(5)
print Foo.bar()

執行時顯示:

5

如果語言使用了attribute語法,看起來如下:

Foo.bar = 5
print Foo.bar

這裡無論是調用了方法還是簡單使用了attribute,主調者被隱藏了這些差異。

編程語言示例

Python

Python的屬性允許以訪問特性(attribute)那樣調用方法。因此統一訪問原則要求的特性訪問與方法調用採用相同表示法,可以被滿足。[3]

'''
>>> egg = Egg(4.0, "white")
>>> egg.color = "green"
>>> print(egg)
Egg(4.0, green)
'''

class Egg:
    def __init__(self, weight, color) -> None:
        self.weight = weight
        self.color = color
    def __str__(self) -> str:
        return f'{__class__.__name__}({self.weight}, {self.color})'


# ...(snip)...
class Egg:
    def __init__(self, weight_oz: float, color_name: float) -> None:
        self.weight = weight_oz
        self.color = color_name
        
    @property
    def color(self) -> str:
        '''Color of the Egg'''
        return to_color_str(self._color_rgb)

    @color.setter
    def color(self, color_name: str) -> None:
        self._color_rgb = to_rgb(color_name)   

    @property
    def weight(self) -> float:
        '''Weight in Ounces'''
        return self._weight_gram / 29.3

    @weight.setter
    def weight(self, weight_oz: float) -> None:
        self._weight_gram = 29.3 * weight_oz

    # ...(snip)...


C#

C#語言支持類的properties, 這提供了定義成員變量的getset操作(getterssetters)的方法。訪問與修改屬性的語法與訪問普通的類成員變量是相同的。但實際實現可以是簡單讀寫或者定義為一個函數:

public class Foo
{
    private string _name;

    // Property
    public int Size
    {
        get;    // Getter
        set;    // Setter
    }

    // Property
    public string Name
    {
        get { return _name; }     // Getter
        set { _name = value; }    // Setter
    }
}

忽略set操作,則屬性只讀。忽略get操作,則屬性只寫。

只用這種滿足統一訪問原則的屬性的代碼:

    public Foo CreateFoo(int size, string name)
    {
        var foo = new Foo();
        foo.Size = size; // Property setter
        foo.Name = name; // Property setter
        return foo;
    }

C++

C++既不支持統一訪問原則也不支持屬性。當從對字段的直接訪問變為一對函數(getA, setA),任何對該對象屬性的訪問 (x = obj.colorobj.color = x) 必須改為函數調用: (x = obj.getColor()obj.setColor(x)). 使用模板或者運算符重載可能作出屬性的效果,但非常複雜,不是語言的直接支持。

JavaScript

JavaScript從2009年開始支持計算屬性.[4]

參考文獻

  1. ^ The UniformAccessPrinciple. c2 wiki. [6 August 2013]. (原始內容存檔於2013-07-19). 
  2. ^ Meyer, Bertrand. EiffelWorld Column: Business plus pleasure. [6 August 2013]. (原始內容存檔於2008-11-20). 
  3. ^ Official Python Docs, built-in functions. [2021-07-09]. (原始內容存檔於2021-07-12). 
  4. ^ w3schools.com, Javascript Accessors. [2021-07-09]. (原始內容存檔於2020-01-04).