Think in ActionScript 3.0Ⅲ -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【meiwen.anslib.com - 电脑资料】

8.面向对象程序设计

8.1类和对象的概念

类:类是用来创建同 一类型的对象的“模板”,在一个类中定义了该类对象所应具有的成 员变量以及方法,

Think in ActionScript 3.0Ⅲ

对象:对象是类的实例。

8.2类之间的关系

系统中的类有那些关系:依赖、关联(聚合、合成)、泛化、实现。

1.依赖:对于外部类或对象的引用;

5.关联:关联暗示两个类在 概念上位于相同的级别;

6.聚合:表示一种“拥有”关系,是 两个类之间一种整体 / 局部的关系;

7.合成:表示一种更强“拥有 ”关系,就像人和腿的关系一样。组合而成的新对象对组成部分的内容分配 和释放有绝对责任;

8.泛化:表现为继承 extends;

9.实现:表 现为实现 implements。

8.3 面向对象程序设计(OOP)

在面向对 象出现以前,结构化程序设计是程序设计的主流,结构化程序设计又称为面向过 程的程序设计。这种设计方法开发的软件稳定性、可修改性和可重用性都比较差 。

与过程相比对象是稳定的。面向对象的软件系统是由对象组成的,复杂 的对象是由比较简单的对象组合而成的。也就是说,面向对象方法学使用对象分 解取代了传统的功能分解。

面向对象的精髓在于考虑问题的思路是从现实 世界人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。 万事万物皆为对象,大至日月星辰,小至沙粒微尘,都是对象。对象包容了一切 事物,不仅仅是那些看得见摸得着的是实体,如:地球、汽车、树叶,还包括那 些客观存在的事物,如:社会、互联网、朋友圈子等等,包罗万象。

以开 车为例,用面向过程的思想去考虑,那么你先得知道怎么启动,怎么踩油门,怎 么挂档。这些应该是司机的活,你要把这些步骤都实现出来。如果用面向对象的 思想,把自己看成领导,只需要下达命令,告诉它你要去哪里就行了(例如,调 用drive() 方法),具体怎么开,怎么踩油门,怎么挂档,不需要我们去管。

那么 dirve() 这个方法放到车里是否合适呢,是不是应该放到“司 机”类更合理呢?封装是很灵活的,没有对与错之分,只有好与更好,需要 具体问题具体分析。因为 dirve() 方法要用到油门和车档,而这些东西都在车里 面,因此如果将它封装到车这个类里面可能更好些。

下面我们通过对比面 向过程和面向对象的设计方式体会什么才是面向对象的思维。

8.4 出圈游 戏 —— 面向过程 VS 面向对象

8.4.1 游戏规则

假设 有5 个小孩儿手拉手围成一圈。从第一个小孩儿开始以顺时针方向依次报数 —— “1,2,3”,报 3的人出列,第四个人从1 开始重 新报数,报到 3 时再出列。如此下去,直到所有人全部出列为止,要求按照出列 的顺序输出他们的序号。

下面来看图理解,首先有5 个小孩围成一个圈:

图一、 5 个小孩儿围成一圈

图二、数到 3的小孩儿退出去

图三、再从2 号开始数三个人, 5 号退出,然后是2 号,最后是4 号。

最终输出的顺序应该是3、1、5、2、4。

8.4.2 出圈游戏 —— 面向过程(cirgame/ CircleGame1.as)

下面用面向过程的思想写这个程 序,通过读注释先来看一下这个程序:

//有5 个小孩儿围成的圈
var array:Array = new Array(5);
for (var i = 0; i < array.length; i++) {
    // 如果元素值为 true 表示他在圈内,如果是false 表示不在圈内
    array[i] = true;
}

// 圈内还剩多少人,最开始人都在,等于 array.length
var leftCount:int = array.length;
// 当前所报的数,初始为 0
var countNum:int = 0;
// 圈子的数组下标,表示当前指向的是谁
var index:int = 0;

while(leftCount > 0) {
    if (array[index] == true) {
        // 如果当前这个人在圈内则报数
        countNum++;
        if (countNum == 3) {
            // 如果所报的数是3 则出列,剩余人数减1,并且下一 次从新开始报数
            trace("out " + (index + 1));
            array[index] = false;
            leftCount--;
            countNum = 0;
        }
    }

    //数组下标增加
    index++;
    if (index == array.length) {
        // 如果下标是最后一个位则归 0,因为这个圈是圆的
        index = 0;
    }
}

用array数组代表这个围成的圈,开始让圈数组中的每个元素都 为 true,表示它们都在圈内,如果设为 false 则表示不在圈内,后面报数的时 候就不予考虑了。

接下来定义三个变量分别表示圈内还剩多少人,所报的 数是多少和数组下标。

下面 while 循环开始,只有圈内还有人 (leftCount > 0)就进行循环,首先判断当前 index 所指的元素是否为真, 如果是则报数加 1,再判断是不是加到 3 了,如果是则打印出当前的数组下标, 再将该元素设为 false,剩余人数减1,下一次从新开始报数,

电脑资料

Think in ActionScript 3.0Ⅲ》(http://meiwen.anslib.com)。

最后让数组下标加 1,当指到最后时,将数组下标置为 0,因为这是一个圈, 要用循环的数组来表示。

8.4.3 出圈游戏 —— 面向对象 (cirgame/ CircleGame2.as)

回顾上一个例子,在面向过程的程序中, 明明是围成的一个圈儿,却要看成是一个数组;明明是一个个小孩儿却要看成是 数组的一个个元素。这不就是为了让计算机看懂吗?但是,面向对象是更加接近 人类的思维模式,我们在现实中看到的就是一个个小孩儿,怎么能说是数组?那 么这一个个小孩儿就是一个个对象,他们都是Kid。围成的这个圈,就是一个 KidCircle。很自然吧,比大自然还自然!下面来体会面向对象的设计思想:

package cirgame {
    public class CircleGame2 {
        public function CircleGame2() {
            var kc:KidCircle = new KidCircle(5);
            var countNum:int = 0;
            var k:Kid = kc.head;

            while (kc.count > 0) {
                countNum++;
                if (countNum == 3) {
                    trace(k.id + 1);
                    kc.remove(k);
                    countNum = 0;
                }
                k = k.right;
            }

        }
    }
}

// 每个Kid都有自己的id,并且左右手还拉着其它的Kid
class Kid {
    var id:uint;
    var left:Kid;
    var right:Kid;
}

// 这个圈子里可以加入或移除一些Kid
class KidCircle {
    var count:uint = 0;
    var head:Kid;
    var rear:Kid;

    function KidCircle(n:uint) {
        for (var i = 0; i < n; i++) {
            var kid:Kid = new Kid();
            add(kid);
        }
    }

    function add(kid:Kid):void {
        kid.id = count;
        if (count == 0) {
            head = kid;
            rear = kid;
            kid.left = kid;
            kid.right = kid;
        } else {
            rear.right = kid;
            kid.left = rear;
            kid.right = head;
            head.left = kid;
            rear = kid;
        }
        count++;
    }

    function remove(kid:Kid):void {
        if (count <= 0) {
            return;
        } else if (count == 1) {
            head = rear = null;
        } else {
            kid.left.right = kid.right;
            kid.right.left = kid.left;
            if (kid == head) {
                head = kid.right;
            } else if (kid == rear) {
                rear = kid.left;
            }
        }
        count--;
    }
}

这段程序中设计了两个类,代表两类客观事物 —— 小孩(Kid)和圈子(KidCircle)。从8.4.1的图中可以确切地看到。Kid有三个 属性:id 号、左手和右手,左手拉着一个Kid,右手拉着一个Kid,因此left和 right存放两个Kid的引用。

下面是KidCircle类,代表围成的圈子,这个圈子可以加入或移除一些Kid,因 此有add和remove两个方法。head和rear两个成员变量用于指向队首和队尾的两个 Kid,因为些添加的Kid 要放在队尾(rear)的后面,因为这是一个圈子,所以还 需要让队尾的小孩儿拉住队首(head)的小孩儿,因此需要保存这两个成员变量 。

最后是测试类,主要的逻辑和前一个例子比较像,这里就不多解释了。

通过这个例子大家可以看出,我们无形之间就完成了一个数据结构 —— 双向循环链表。而前面面向过程的例子,实际上就是一个顺序的 存储结构 —— 线性表。

后面,我会带大家写一个贪吃蛇的游 戏,目的是学习面向对象编程的思想(并非该游戏本身),在贪吃蛇中我们就会 运用到类似于单向链表的结构,如果双向链表掌握了,那么单向一定没问题。

通过本节请大家认真体会面向对象的设计思想。一次学会,终身受用。

最新文章