    [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.

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

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