1.3.1 用数据描述对象的状态
我们先从数据开始。数据代表一个特定对象的个体特征,也就是现在的状态。类可以定义一系列属于这个类的对象的共有特征。对于这些特征,任何特定对象都可以拥有不同的数据值。比如,在我们的桌子上放着的3个橘子重量可能各不相同。橘子类可以有一个weight属性表示这个特征,所有橘子类实例都有weight属性,但每个橘子都可以有不同的weight值。不过属性的值并不需要是唯一的,两个橘子也可能重量一样。
属性(attribute)也经常被称为成员(member)或特性(property)。有些作者认为这些术语有不同的含义,通常来说属性(attribute)是可以修改的,而特性(property)是只读的。在Python中,特性可以被定义为只读,但它在本质上还是可以修改的,所以只读的概念在Python中没有太大意义。在本书中,我们将这两个词视为同义词。另外,我们在第5章中会讨论property关键字的特殊作用。
在Python中,我们也可以把属性称为实例变量。这可以帮助理解属性的原理。属性是属于每个类实例的变量,这些变量可以有不同的值。Python也有其他类型的属性,但我们现在只讨论最常见的这种实例变量。
在我们的水果库存应用中,果农可能希望知道橘子来自哪个果园(orchard),是何时采摘(date_picked)的,以及重量(weight)是多少。他们也许希望跟踪每一个Basket中的橘子被存储在哪里(location)。苹果可能有颜色(color)属性,桶可能有不同的尺寸(size)。
有些属性可能是多个类共有的,比如我们可能也想知道是何时采摘的苹果。在这个示例中,我们就随意给类图设置了几个不同的属性,如图1.3所示。
图1.3 带有属性的类图
根据我们设计的具体程序,我们也可以指定每个属性值的类型。在UML中,属性类型通常使用编程语言中通用的名称,比如整数(integer)、浮点数(float)、字符串(string)、字节(byte)或布尔值(Boolean)。然后,它们也可以是列表、树、图等常见的集合类型,甚至是与具体应用相关的其他非通用类型。这是一个设计阶段可能与编程阶段有所重叠的地方。这些基本数据类型和自带的集合类型,在不同的编程语言中可能有所不同。
下面是一个Python版本的带属性类型的类图,如图1.4所示。
图1.4 带属性和属性类型的类图
通常,在设计阶段,我们不需要过度担心数据类型的问题,因为具体的实现细节是在编程阶段确定的。通用的类型名称在设计阶段就够用了,这就是为什么在图1.4中用date表示采摘日期,在实际的Python编程中使用datetime.datetime。如果在设计中需要用到列表类型,Java程序员可以选择LinkedList或ArrayList来实现,而Python程序员(也就是我们)可以选择list类型来实现,通过List[Apple]做类型提示。[1]
到目前为止,在水果库存的示例中,所有的属性都是基本类型的。然而,有一些隐含的属性,我们可以通过关联关系显式说明。对于一个给定的橘子,我们可以用一个basket属性来表示这个橘子所在的篮子,这个属性的类型提示是Basket。