統一訪問原則

電腦編程統一訪問原則(英語: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#語言支援類的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++既不支援統一訪問原則也不支援屬性。當從對欄位的直接訪問變為一對函數(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).