BOO入门 > 类别 (上一章:函数 下一章:方法)


定义 定义 类别:一个聚合的包装,包含了特定种类的编译时期元数据。类别描述了物件如何呈现的规则,里面除了定义资料结构之外,也定义了存取/操作资料的方法(函式)。
定义 定义 物件: 类别的实体。

定义类别

编辑

类别很重要,因为这可以让你将程式码切割为一个或数个比较简单、比较具逻辑的区块,以便于作适当的组织和资料操作。

// 宣告類別
class Cat:
    pass
fluffy = Cat()

上面的程式宣告了一个空的类别,名称是 "Cat",里面没有任何的成员与方法。而 fluffy 则是 Cat 类别的实体。

  建议 所有类别名称都应该使用 PascalCase,PascalCase 的意思是,将每个单字的第一个字母大写并且不要使用空白。如果是缩写的话,像 "URL",则用 "Url"。

栏位与属性

编辑
  定义 栏位:类别里包含特定资讯术语的元素。
  定义 属性:用来取代 getter/setter 函数的语法。

简单的说,栏位持有资讯,而属性则是存取资讯。

// 屬性範例
class Cat:
    [Property(Name)]
    _name as string

fluffy = Cat()
fluffy.Name = 'Fluffy'
  1. class Cat: 开始宣告类别
    1. [Property(Name)] 表示宣告一个属性:Name,来源是 _name。
    2. _name as string 表示宣告 Cat 的栏位: _name,型态为字串。
  2. fluffy = Cat() 宣告一个 Cat 的实体。
  3. fluffy.Name = 'Fluffy' 存取 Cat 的属性:Name,并设置为 'Fluffy'。实际上会使得 _name 被设为 'Fluffy'。

因为安全(封装)的缘故,栏位通常不直接存取。

  建议 使用 PascalCase 来命名属性,就像命名类别一样。使用底线为首的 CamelCase 来命名属性。(CamelCase 类似 PascalCase,差别在于第一个字母为小写。)

有另外两种宣告 Property 的 Attribute:getter 与 setter。技术上来说,[Property] 就等同于 getter 加上 setter。

class Cat:
    [Getter(Name)]
    _name = 'Meowster'

    [Setter(FavoriteFood)]
    _favoriteFood as string

fluffy = Cat()
print fluffy.Name
fluffy.FavoriteFood = 'Broccoli'

输出结果

Meowster

如果你试著指派值给 fluffy.Name 或是读取 fluffy.FavoriteFood,将会发生错误,因为并没有 Name 的 setter 与 FavoriteFood 的 getter。

使用 Property、Getter、Setter 这三个 attribute 非常方便,但只有 Boo 才能使用。当然,如果你比较熟悉 C#/VB.Net 的话,你也可以采取这样的写法:

// 明確的屬性範例
class Cat:
    Name as string:
        get:
            return _name
        set:
            _name = value

    _name as string

fluffy = Cat()
fluffy.Name = 'Fluffy'

因为栏位只有类别里的方法、属性能使用,你可以看到 Name 只是将 _name 包裹起来而已。将来如果你要加上额外的检查或处理,只需要在 getter/setter 上加代码就行了。

value 是 setter 述句里的特殊关键字,它包含了指派的值。

属性的预先条件判别

编辑

你可以在 Property attribute 里面指定检查条件式,而这条件式会发生在指派值之前,看看下面的例子:

// 屬性範例
class Cat:
    [Property(Name, Name is not null)]
    _name as string

fluffy = Cat()
fluffy.Name = null // 這行將會導致 boo 擲出引數錯誤的異常

类别修饰词

编辑
修饰词 描述
public 建立一个正常、公开的类别,可以被其他型别完整存取。
protected 表示类别只能被它所包含的类别(宣告在类别里的类别)与继承此类别的类别存取。
internal 只能在宣告的组件内使用。
protected internal 结合 protected 与 internal
private 表示类别只能被它所包含的类别(宣告在类别里的类别)存取。
abstract 表示无法被实体化,通常作为其他类别的基底类别。
final 表示无法被继承。
  建议 永远都别用 public 修饰词,因为当你没有指定时,预设就是 public。
// 類別修飾詞範例
abstract class Cat:
    [Property(Name)]
    _name as string

abstract 关键字就是类别修饰词之一,用法就是放在 class 的前面。

继承

编辑
  定义 继承:可以让新类别享有既有类别实作与成员的方法,这可以让你能尽可能地重复使用既有的代码(当然,也许可能得视情况作一些修改。

在 Boo 里,继承非常简单。

// 繼承範例
class Cat(Feline):
    [Property(Name)]
    _name as string

class Feline:
    [Property(Weight)]
    _weight as single //In Kilograms

上面的代码让 Cat 类别继承 Feline,所以虽然 Cat 并没有宣告 Weight 与 _weight,但 Cat 类别同样有了 Weight 与 _weight 这两个成员。 你可以让多个类别继承自同个类别,这可以达到代码的可重用性。

更多关于继承的东西,含括在[[第十章:多型与继承里面。

类别可以继承自零或一个其他的类别,或是继承自任意数目的介面。

如果要继承多个介面,你应该使用这样的语法:Child(IBaseOne, IBaseTwo, IBaseThree),至于介面,下一节会介绍。

介面

编辑
  定义 介面:介面定义了一组方法,让类别能实作这些方法。对外界来说,只需要知道此介面,就可以使用介面里定义的方法进行操作。

介面让你可以设置给类别使用的 API,在定义的时候,并不会有程式码的实作在里面,真正的实作都会在继承此介面的类别里面。介面可以继承多个介面,但不能继承类别。

// 介面範例
interface IFeline:
    def Roar()

    Name:
        get:
        set:

范例里面定义了介面 IFeline,里面有一个方法:Roar 和一个属性:Name。属性必须明白地宣告在介面里面。方法将会在下一章:方法解释。

  建议 使用 PascalCase,并加上大写 I 来命名你的介面,例如:IFeline。

值与参考型别的相异处

编辑

在 Boo/.NET 的世界里,型别有两种类型:值与参考。所有的类别都是参考型别。在值型别列表提到的数值、boolean、字元都是值型别。

  定义 null:用来表示参考型别变数未定义值的情形。

值型别永远不能被设为 null,永远都有预设值。数值性别的预设值永远是 0。

练习

编辑
  1. 建立一个继承多个介面的类别。
  2. 试试看继承多个类别,看会发生什么事情?

// 继承范例

class Cat(Feline,Love):

   [Property(Name)]
   _name as string

class Feline:

   [Property(Weight)]
   _weight as single //In Kilograms

class love:

   [Property(Love)]
   _love as string

参考

编辑