[NAME]
ALL.dao.tutorial.class

[TITLE]
Dao Class and Object-Oriented Programming

[DESCRIPTION]

道语言对面向对象编程(object-oriented programming, OOP)有良好的支持。 道语言类使用关键字 
class定义。 一个类就是一组变量与函数的集合的抽象表示,这些变量和函数称为类的成员; 成员
变量的值将决定该类的特性,成员函数决定了该类的行为。 类本身是程序员定义的抽象类型,它的
值即实例就是一个具体的数据集合,此集合 包含了类定义的成员变量。 类实例可以以函数调用的方
式调用类的构造函数创建,也可通过枚举类成员变量来创建。 类成员的访问权限可由修饰词 
private(私有), protected(保护) 和 public(公开)来限定,缺省权限为公开。 这些修饰词
后可跟也可不跟冒号。

类与类之间可存在继承和包含关系。一个类(子类)可定义为另一个类(基类)的引申。 子类可从
基类继承某些特性,在基类的基础上扩充或专门化某些功能。类也可以包含其他类 的实例为成员。
 0.1  基类定义 


类的定义由关键字class开始申明类的名称, 然后在由花括号所包围的类体里,申明类的成员常量,
变量 和方法等。
     
   1  class MyNumber
   2  {
   3      private
   4  
   5     var value = 0;
   6     var name : string;
   7  
   8      public
   9  
  10     routine MyNumber( value = 0, s = "NoName" ){
  11       value = value;
  12       name = s;
  13     }
  14  
  15     routine setValue( v ){ value = v }
  16     routine getValue(){ return value }
  17  
  18     routine setValue( v : float );
  19  }
  20  
  21  routine MyNumber::setValue( v : float )
  22  {
  23     value = v;
  24  }
     

类定义体里使用关键字 var申明类的成员变量。 同普通变量类似,类成员变量也可按下面方式申明
为有固定类型,
     
   1  var variable : type;
     
这里 type必须是一类型名,这样 variable的类型将固定为 type的类型; 或者 variable必须是 
type的实例,如果 type也是类的话。

类的成员变量还可拥有缺省值。在类创建时,有缺省值的成员变量将被首先以缺省值填充。 类的成
员变量的缺省值可按以下方式指定,
     
   1  var variable = init_value;
   2  var variable : type = init_value;
     
这里 init_value也必须是一常量。

在道语言里,类的定义体也是类的构造函数。和其他某些脚本语言里的类构造函数类似, 道语言里
的类构造函数并不是真正用来构造类实例,而是用来初始化类的成员数据。 道语言里,类可拥有多
个重载的构造函数,用来根据不同的构造函数调用参数以不同的方式初始化类实例。
例子,
     
   1  class MyNumber( value = 0, name = "NoName" )
   2  {
   3      private
   4     my Value = 0; # value类型为整形,缺省值为零
   5     my Name : string; # name必须是字符串
   6  
   7     Value = value;
   8     self.Name = name;
   9  
  10     public
  11     routine setValue( value ){ Value = value }
  12     routine getValue(){ return Value }
  13  
  14     routine setValue( value : int );
  15  }
  16  
  17  routine MyNumber::setValue( value : int )
  18  {
  19     Value = value;
  20  }
     

在类的构造函数或成员函数里,可使用一特殊变量 self,它表示类的当前实例。

与C++里的类成员函数定义类似,类的成员函数可在类定义体内申明,然后在类体外定义。 不过在道
语言里,类体外定义的成员函数的参数表必须和申明时的完全一致。 Dao里,虚拟函数也可用关键词 
virtual来申明。

如果一个类成员函数里没使用类成员变量,且没调用其他使用了类成员变量的成员函数, 那么此成
员函数可按如下方式调用,
     
   1  classname.method(...)
     

 0.2  类实例 

新的类实例可以由调用该类的构造函数得到,构造函数的调用方式跟普通函数调用完全一样。
     
   1  obj1 = MyNumber(1);
     
类实例也可通过枚举类的成员变量创建,这种实例创建方式主要适合于比较简单的类,因为这种不需
要 复杂操作进行初始化。
     
   1  obj2 = MyNumber{ 2, "enumerated" };
     
在枚举成员变量时,可指定成员的名称,
     
   1  obj3 = MyNumber{
   2     Name  => "enumerated";
   3     Value => 3;
   4  };
     

当类实例由枚举创建时,类实例先被成员变量的缺省值填充,然后使用枚举中的数据初始化相应的成
员变量。通过枚举创建类实例比通过调用类构造函数创建类实例要快得多,调用类构造函数有一系列
额外开销,如参数传递,函数运行时数据的分配等。 枚举创建实例很适合于创建大量简单类的实例
。
 0.3  类成员数据 

正如上面提到,类的成员变量由关键字 var在类体内申明。 类的常量和静态成员可分别由 const
static来申明。 
     
   1  class Klass
   2  {
   3     const aClassConst = "KlassConst";
   4     static aClassStatic;
   5  }
     

 0.4  Setters, Getters 和运算符重载 

相对于给私有或保护的成员变量定义 setXyz()getXyz()方法, 更好的方式是定义所谓的Setters
和Getters,定义这样的方法使得从外部环境可以直接 对私有或保护的成员变量进行访问,就像访问
公开成员一样。 对于成员变量 Xyz其Setters应被定义为 .Xyz=(), 而其Getters应被定义为 
.Xyz()。 当Setters被定义后 Xyz可由 obj.Xyz=abc设定; 而当Getters被定义后 Xyz可由 obj.Xyz
获取。
     
   1  class MyNumber0
   2  {
   3      private
   4     
   5      var value = 0;
   6  
   7      public
   8  
   9    routine MyNumber0( v = 0 ){
  10      value = v;
  11    }
  12  
  13    operator .value=( v ){ value = v; io.writeln( "value is set" ) }
  14    operator .value(){ return value }
  15  }
  16  
  17  num = MyNumber0( 123 )
  18  num.value = 456
  19  io.writeln( num.value )
     

你也许已经猜到,通过Setters和Getters对类成员变量的访问会有比较大的运算开销。 因此为了效
率起见,需要从外部访问的成员变量应被设为公开。 只有在必要时才用Setters和Getters,比如当
你需要在那些成员变量被访问时作些额外工作时。

被支持重载的运算符还包括:
  1. [operator =(...)] 用作赋值运算;
  2. [operator ()(...)] 用作函数调用;
  3. [operator [](...)] 用作取元素;
  4. [operator []=(...)] 用作设定元素;
其运算符的重载将载后续版本中支持。
 0.5  重载成员方法 

类的成员方法可以象普通函数那样被重载。类的构造函数也可被重载,给类定义于类同名的 成员方
法即可,不过在重载的类构造函数中不可使用关键词 my来定义成员变量。 如类 MyNumber可作如下
修改使得它只可从数字或该类的实例构造和赋值,
     
   1  class MyNumber
   2  {
   3      private
   4     var value : int;
   5  
   6     public
   7     routine MyNumber( value = 0 ){ # accept builtin number as parameter
   8        self.value = value;
   9     }
  10     # 接受MyNumber实例为参数的构造函数,类似于C++里的复制构造函数:
  11     routine MyNumber( value : MyNumber ){ self.value = value.value }
  12  
  13     operator .value=( v : int ){ value = v }
  14     operator .value=( v : MyNumber ){ value = v.value }
  15     operator .value(){ return value }
  16  }
  17  
  18  num1 = MyNumber( 123 )
  19  num1.value = 456
  20  io.writeln( num1.value )
  21  
  22  num2 = MyNumber( num1 )
  23  io.writeln( num2.value )
  24  
  25  num2.value = 789
  26  io.writeln( num2.value )
  27  
  28  num2.value = num1
  29  io.writeln( num2.value )
     

 0.6  类继承 
     
   1  class ColorRBG
   2  {
   3     var Red = 0;
   4     var Green = 0;
   5     var Blue = 0;
   6  
   7     routine ColorRBG( r, g, b ){
   8       Red = r;
   9       Green = g;
  10       Blue = b;
  11     }
  12     
  13     routine setRed( r ){ Red = r; }
  14     routine setGreen( g ){ Green = g; }
  15     routine setBlue( b ){ Blue = b; }
  16     
  17     routine getRed(){ return Red; }
  18     routine getGreen(){ return Green; }
  19     routine getBlue(){ return Blue; }
  20  }
  21  
  22  yellow = ColorRBG( 255, 255, 0 ); # create an instance.
     
下面将定义 ColorRBG的一个派生类, 
     
   1  class ColorQuad : ColorRBG
   2  {
   3     var alpha = 0; # alpha component for tranparency.
   4  
   5     routine ColorQuad( r, g, b, a ) : ColorRBG( r, g, b ){
   6       alpha = a;
   7     }
   8  }
   9  
  10  yellow2 = ColorQuad( 255, 255, 0, 0 ); # not tranparent.
  11  yellow2.alpha = 127; # change to half tranparency.
     

当从已有类派生新类时,基类必须放在派生类的参数列表后,并由 :隔开。 如果有多个基类,这基
类都应被放在 :后并被 ,隔开。 派生类构造函数的参数可按例子所显示的方式传递给基类的构造函
数。

定义派生类时,如果只有一个父类,派生类将同时继承父类的构造函数。