统一访问原则
计算机编程的统一访问原则是由伯特蘭·邁耶在《Object-Oriented Software Construction》中提出。它指出“一个模块提供的所有服务都应该通过统一的符号提供,与它们是通过存储还是通过计算实现无关”。[1]这个原则普遍适用于面向对象编程语言的语法。使用属性、预先计算的属性或对象的方法/查询实现,调用时没有语法差异。
虽然大多数示例都聚焦在该原则的“读取”方面(即检索值),但伯特蘭·邁耶表示,该原则“写入”(即修改值)方面更难实现。[2]
解释
伯特蘭·邁耶解决的问题涉及大型软件项目或软件库的维护。有时在开发或维护软件时,有必要在大量代码到位后,把简单的属性访问转换为方法调用的方式。编程语言通常使用不同的语法来访问属性和调用方法。例如,object.something
与object.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, 这提供了定义成员变量的get
与set
操作(getters 与 setters)的方法。访问与修改属性的语法与访问普通的类成员变量是相同的。但实际实现可以是简单读写或者定义为一个函数:
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
}
}
只用这种满足统一访问原则的属性的代码:
public Foo CreateFoo(int size, string name)
{
var foo = new Foo();
foo.Size = size; // Property setter
foo.Name = name; // Property setter
return foo;
}