统一访问原则
2011年4月20日
模块提供的所有服务都应该通过统一的符号访问,而不暴露它们是通过存储还是通过计算实现的。
-- Bertrand Meyer
Bertrand Meyer 在他极具影响力的著作《面向对象软件构建》中提出了这一原则。 Object-Oriented Software Construction.[1]
该原则的核心在于,如果你有一个 person 对象,并且你询问它的年龄,无论年龄是对象的存储字段还是计算值,你都应该使用相同的符号。它实际上意味着 person 的客户端不应该知道或关心年龄是存储的还是计算的。
这使得 person 对象能够轻松地在两者之间切换,同时消除了客户端通常不必要的关注。这是封装的重要组成部分——或者至少是封装的数据隐藏方面。
虽然它是面向对象编程的基本特性,但令人惊讶的是,很少有面向对象语言真正遵循它。(Eiffel 自然是一个例外。)大多数语言通过约定模拟它,因此在 Java 和 C++ 程序中使用诸如 getAge
之类的访问器成为习惯。
无论语言如何,重要的是程序员不要违反该原则。你经常会看到破坏统一访问的约定。一个例子可能是说数据访问器命名为 getAge
,而计算访问器命名为 calcAge
。我总是尽力在两种情况下都以相同的方式命名访问器。
反对统一访问的一个论点是,数据访问很快,而计算可能很慢——并且让客户端程序员了解方法响应速度是否缓慢是有用的。我更倾向于将其视为一个特例——遵循统一访问原则,除非存在特别缓慢的计算(并且这应该是一个经过测量发现缓慢的计算,而不仅仅是预期缓慢的计算)。在这种情况下,还有其他选择,例如缓存访问器,你可以探索。
统一访问适用于 getter 和 setter,尽管计算访问器通常是只读的。属性是只读的还是可写的,与它是存储的还是计算的无关。
备注
1: 这种对原则的表述来自第二版。在第一版中,他称之为统一引用原则(§2.1.4)。他在第二版中将其改为统一访问原则,并提供了引用的定义。