PKU Novo Team's profilePKU Novo Team SpaceBlogListsGuestbookMore Tools Help

Blog


    August 04

    Final Summary

    软件实现技术课结课了,最终小组取得了第二名的成绩。

    这门课,是由软微学院和微软联合创办的课程,虽然开始选这门课的时候,更多的是看重的微软的这个名字。不过学完之后看过来,的确学到了一些很有用的东西,受益匪浅。

    总结一下:

    • Learning by Doing
      通过实践来学习,掌握技能和技巧。要努力提高自己遇到问题解决问题的能力。
    • 算法设计要提到一个非常高的重视程度
      尽管在开发时,架构、框架、管理……一切都很重要,但是算法绝对不可以忽视,有了足够好的算法,系统才能更加稳定更加健壮,也更加完善地运行
      加强自己的算法设计能力,通过研究算法,加强自己对操作系统的认识,提高逻辑思维能力
    • 做就要做最好的
      在任何时候,能够被大家记住的,只有第一名。
      在参与竞争时,要给自己暗示:只有两个名次,要么做第一名,要么做除了第一名以外的并列最后一名。
      我们都记得神五飞船杨利伟的名字,可是又有多少人还能马上叫出的神六的两位航天员的名字呢?
    • 向比自己做的好的人和团队借鉴经验
      正所谓踏在巨人的肩膀上前进,这样才能事倍功半
    • 各自在团队中扮演好自己的角色,做出自己应有的贡献
      没有PM的组织,团队是一团散沙;没有Dev的辛勤开发,系统无法运行;没有Test的认真测试,系统正确性无法保证。三足鼎立,就像三角形,缺一角,就变得不够稳定了。
    • Nothing is impossible
      没有什么是做不到的,只要你想,并且为之付出足够的努力

    我组的PM杰哥,获得了整个课程的最佳PM奖,恭喜杰哥!没有他,小组的沟通与进度便无法保证,大家也不会如此重视。

    最后,欢迎各位去我的Blog:http://www.techwork.cn/

    --- Paul

    July 27

    Code Review

    软件实现技术(补)第十四次课

          7月25日本周五是补上周日这门课的第十四次课,微软的工程师对我们这门课的大作业“嵩山项目”进行讲评。我们前6各小组做的都是电梯的小项目由一位老师进行讲评;后面6个小组做了3个小项目,包括电梯、自动售货机和汽车,由一位老师进行讲评。老师在讲个过程中主要是讲了每个小组代码的优点以及需要改进的地方。

    我们小组的情况是:

    优点:1、面向对象的意图很明显,使用多个类协同完成;

             2、大量大小函数的内聚比较好;

             3、采用模块化,各模块具有很强的独立性,容易维护和升级。

    需要改进的地方:

            1、用死循环查询(设置)电梯状态。在电梯等待或者空闲状态也会一直在扫描电梯的状态,有一定的浪费;

            2、命名规则前后不一致;

            3、有些函数和变量名字起的不恰当;

            4、缺少必要的错误检查处理;

            5、有些方法可以改为C#的属性。

          以上这些就是我们小组的情况。需要改进的地方,第一,我们将电梯状态查询/设置的算法可以细化到根据某些状态需要每次都扫描,有些状态不需要扫描例如空闲和停止状态,但是可以有一个状态激活,如果这两个状态改变了就要每次都进行扫描。第二,在开发过程中两名开发人员的编码习惯有一定的不同,在编码完成后应该让开发小组负责人进行代码审核。

         感谢老师在繁忙的工作中抽时间对我们代码的评审!

    July 20

    在 netBeans / Eclipse / Myeclipse等Java IDE下连接 SQL Server 2005的问题及解决方法

    最近将一个项目的数据库由SQL Server 2000迁移到了2005,而在这过程中出现了一点点问题,特总结如下:

    1、如何找到SQL Server 2005 的 JDBC 驱动?
    http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=6d483869-816a-44cb-9787-a866235efc7c

    2、如果管理SQL Server 2005?
    使用SQL Server Management Studio:http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=c243a5ae-4bd1-4e3d-94b8-5a0f62bf7796#filelist

    3、如何设置SQL Server 服务器?
    (1). 打开SQL Server Configuration Manager -> MSSQLSERVER的协议 -> TCP/IP
    (2). 右键单击启动TCP/IP
    (3). 双击进入属性,把IP地址中的IP all中的TCP端口设置为1433
    (4). 重新启动SQL Server 2005服务中的MSSQLSERVER服务器
    (5). 关闭SQL Server Configuration Manager
    (6). 现在就可以使用1433端口通过TCP/IP协议访问SQL Server 2005了

    4、原来是SQL Server 2000的程序,为什么转移到2005中连接不了了?
    (1). 加载的驱动不同了,SQL Server 2005的JDBC驱动JAR包为:sqljdbc.jar,原来的则有三个。
    (2). JAR包的类名和连接字符串不同了。在SQL Server 2000 中加载驱动和URL路径的语句是
          String driverName = "com.microsoft.jdbc.sqlserver.SQLServerDriver";
          String dbURL = "jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sample";
          而SQL Server 2005 中加载驱动和url的语句则为
          String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
          String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=sample";
          这里需要十分注意。
    (3). 经过上述修改,便可以正确连接SQL Server 2005

    电梯程序设计 代码级程序分析(四)

    今天是代码分析的最后一部分:最关键的四个函数。

    从最简单的说起吧。

    /// <summary>
    /// 运行到某层时检查是否需要停下
    /// </summary>
    /// <param name="floor"></param>
    /// <param name="direction"></param>
    /// <returns></returns>
    protected bool NeedStop(int floor, Direction direction)
    {
        if (_stopfloors.ContainsKey(floor))
        {
            _stopfloors.Remove(floor);
            _task.NeedStop(floor, direction);
            return true;
        }
        else if (_task.NeedStop(floor, direction))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    NeedStop()函数判断了在某层、某方向上是否需要停止。在判断的时候,我们需要考虑外部呼梯和内部呼梯两种情况;同时,如果可以停,那么还不能忘记了把相关的层数从相关的列表中去除,也就是马上要停到该层,该层的呼梯任务完成。而在检查外部呼梯的过程中,直接调用了Task类已经写好的同名函数NeedStop(),大家也可以看我们曾经的日志来检查那个函数的写法。

    /// <summary>
    /// 检查是否还有同方向上更多的层需要经停
    /// </summary>
    /// <param name="floor"></param>
    /// <param name="direction"></param>
    /// <returns></returns>
    protected bool ContainsMore(int floor, Direction direction)
    {
        switch (direction)
        {
            case Direction.up:
                //首先检查内部呼梯
                for (int i = 0; i < _stopfloors.Count; i++)
                {
                    if (floor < _stopfloors.Keys[i])
                    {
                        return true;
                    }
                }
                //再检查外部呼梯
                if (_task.ContainsMore(floor, direction))
                {
                    return true;
                }
                break;
            case Direction.down:
                //首先检查内部呼梯
                for (int i = 0; i < _stopfloors.Count; i++)
                {
                    if (floor > _stopfloors.Keys[i])
                    {
                        return true;
                    }
                }
                //再检查外部呼梯
                if (_task.ContainsMore(floor, direction))
                {
                    return true;
                }
                break;
            case Direction.none:
                break;
        }

        return false;
    }

    ContainsMore()这个函数主要是判断电梯是否需要继续上行。例如,电梯在3层,5层有个向下呼梯。那么到了5层,电梯便可以转变方向而不用再上到六层。同样,这个函数也需要分别检查内部呼梯和外部呼梯两个列表,而在调用外部呼梯的检查过程时使用了Task类的同名函数。

    下一个函数是FindDirection(),判断电梯的运行方向。这个过长会发生在stopping阶段。判断电梯下一步的运行方向如何。大家看了程序就很容易明白,因为代码注释写的很清楚了:

    /// <summary>
    /// 判断电梯下一步的运行方向
    /// </summary>
    /// <returns>电梯下一步要运动的方向</returns>
    public Direction FindNextDirection()
    {

        //如果外部呼梯和内部呼梯均没有内容,则原地待命
        if (_stopfloors.Count==0 && _task.ifEmpty())
        {
            return Direction.none;
        }

        //如果现在电梯处于上升或停止状态,那么判断本楼层以上是否还有呼梯
        if (_direction == Direction.none || _direction == Direction.up)
        {
            //如果还有未完成上行的任务
            if (ContainsMore(_floor.Current, Direction.up))
            {
                return Direction.up;
            }
            //如果还有未完成下行的任务
            else if (ContainsMore(_floor.Current, Direction.down))
            {
                return Direction.down;
            }
        }
        else if(_direction == Direction.down)
        {
            //如果还有未完成下行的任务
            if (ContainsMore(_floor.Current, Direction.down))
            {
                return Direction.down;
            }
            //如果还有未完成上行的任务
            else if (ContainsMore(_floor.Current, Direction.up))
            {
                return Direction.up;
            }
        }

        return Direction.none;
    }

    最后就是最重要的RefreshStatus()函数了。它为电梯状态改变,提供了转换及其发生的相关条件判断。

    /// <summary>
    /// 刷新运行状态
    /// </summary>
    protected void RefreshStatus()
    {
        while (_runinngPermit)
        {
            int ticks = Environment.TickCount;
            int activetime = ticks - _lastactive;

            switch (_status)
            {
                case Status.Out_of_Services:
                    //如果电梯处在不可用状态,那么不予请求,直到电梯开启使用
                    break;
                case Status.Maintenance:
                    //如果电梯处在维护状态,那么不予请求
                    break;
                case Status.Idle:
                    //电梯处在空闲状态,此时如果有外部呼梯存在,或是内部请求存在,则决定前往的方向,将状态改变为上或下
                    _direction = FindNextDirection();
                    switch (_direction)
                    {
                        case Direction.up:
                            _status = Status.Up;
                            break;
                        case Direction.down:
                            _status = Status.Down;
                            break;
                    }
                    break;
                case Status.Up:
                    //电梯在上升的过程中,每5秒上升一层。在本层判断下一层是否是经停层。如果是,则将状态转化为停止。
                    if (activetime < 2000)
                    {
                        break;
                    }
                    else
                    {
                        if (NeedStop(_floor.Current, _direction))
                        {
                            _status = Status.Stopping;
                        }
                        else
                        {
                            //如果到达顶层则向下
                            if (_floor.Current == _floor.getTopFloor())
                            {
                                _status = Status.Down;
                                _direction = Direction.down;
                            }
                            else
                            {
                                _floor.Up();
                            }
                        }
                        _lastactive = ticks;
                    }
                    break;
                case Status.Down:
                    //电梯在下降的过程中,每2秒下降一层。在本层判断下一层是否是经停层。如果是,则将状态转化为开门。
                    if (activetime < 2000)
                    {
                        break;
                    }
                    else
                    {
                        if (NeedStop(_floor.Current, _direction))
                        {
                            _status = Status.Stopping;
                        }
                        else
                        {
                            //如果到达底层则向上
                            if (_floor.Current == _floor.getBottomFloor())
                            {
                                _status = Status.Up;
                                _direction = Direction.up;
                            }
                            else
                            {
                                _floor.Down();
                            }
                        }
                        _lastactive = ticks;
                    }
                    break;
                case Status.Stopping:
                    //电梯停止的过程中。这里判断下一步是继续上升还是下降
                    if (activetime >= 2000)
                    {
                        _status = Status.Opening;
                        _lastactive = ticks;
                    }
                    _direction = FindNextDirection();
                    break;
                case Status.Opening:
                    //电梯门打开,打开时间长3秒钟
                    if (activetime >= 3000)
                    {
                        _status = Status.Waiting;
                        _lastactive = ticks;
                    }
                    break;
                case Status.Waiting:
                    //开门后,电梯停顿2秒,关门
                    if (activetime >= 2000)
                    {
                        _status = Status.Closing;
                        _lastactive = ticks;
                    }
                    break;
                case Status.Closing:
                    //电梯门关闭,关闭时间长3秒钟
                    if (activetime >= 3000)
                    {
                        _status = Status.Idle;
                        _lastactive = ticks;
                    }
                    break;
            }

            StringBuilder status = new StringBuilder();
            status.AppendLine("Current Status:");
            status.AppendLine("Floor:" + _floor.Current + "  Direction:" + _direction + "  Status:" + _status);
            status.AppendLine();
            status.AppendLine("Outside Calls:");
            status.AppendLine(_task.ListAll());
            status.AppendLine("Inside Calls:");
            status.AppendLine(ListAllStopFloors());
            GetAllStatus(status.ToString());
            Console.WriteLine(status.ToString());
            Thread.Sleep(500);
        }
    }

    这段代码主要就是根据目前status状态,以及上一次系统活动的时间,来判断下一步电梯做什么,状态如何。有了上面很多关键函数的支撑,这里其实已经简化了很多,更多的都是周边的设置和输入输出工作了了。在正式运行时,我们激活了一个线程,每隔500毫秒执行一次本程序。最后,通过委托的挂接,将结果传送到高层模块中,以最终完成输出工作。

     

    整个电梯的设计工作及代码分析到此结束了。在这一段日子里,我们通过这个程序,根据自己的需求和设计进行编码和实现,完成了电梯运行的再现。
    最后一次的entry,将会展示我们电梯的测试类,并展示电梯的运行效果。

    July 19

    电梯程序设计 代码级程序分析(三)

    今天来主要分析电梯程序设计中最关键的类:ElevatorMain

    首先贴出类的声明和它的字段列表:

    /// <summary>
    /// 电梯主类
    /// </summary>
    public class ElevatorMain
    {
        /// <summary>
        /// 电梯外部呼梯任务
        /// </summary>
        private Task _task;
        /// <summary>
        /// 电梯内部经停任务
        /// </summary>
        private SortedList<int, int> _stopfloors;
        /// <summary>
        /// 电梯运行状态
        /// </summary>
        private Status _status;
        /// <summary>
        /// 电梯所有可以经停的楼层
        /// 以及现在所处的楼层
        /// </summary>
        private Floor _floor;
        /// <summary>
        /// 电梯运行的方向
        /// </summary>
        private Direction _direction;
        /// <summary>
        /// 最后一次系统活动时间
        /// </summary>
        private int _lastactive;

        //控制线程
        private Thread _running;
        //线程运行许可
        private bool _runinngPermit = true;

    可以看出,我们的电梯程序中,主要用了如下字段:

    • _task: 用来存储外部呼梯任务的Task对象
    • _stopfloors: 保存内部电梯任务的对象,用SortedList<int,int>作为其载体
    • _status: 存储电梯的各个运行状态
    • _floor: 保存电梯想对应的层数信息,如当前层、层数列表等等
    • _direction: 保存电梯运行的方向
    • _lastactive: 存储上次电梯活动的时刻,用来计算电梯的运行时长来判断下一个任务的执行

    有了这些相关类型数据的存储,就可以保证程序的顺利运行了。

    /// <summary>
    /// 电梯类构造函数
    /// </summary>
    /// <param name="floors">可以经停的楼层信息</param>
    public ElevatorMain(Floor floor)
    {
        _task = new Task();
        _stopfloors = new SortedList<int, int>();
        _floor = floor;
        _status = Status.Out_of_Services;
        _direction = Direction.none;
        _lastactive = Environment.TickCount;

        _running = new Thread(new ThreadStart(this.RefreshStatus));
        _running.Name = "Running";
        _running.Start();
    }

    上述函数是电梯运行的构造函数。将一些基本的数据初始化,还有就是新建一个线程,来运行RefreshStatus检测系统状态。这里使用了Polling轮询的方法,电梯实际运行中,应该会有一些触发的过程来处理。

    /// <summary>
    /// 开启电梯到运行状态
    /// </summary>
    public void Startup()
    {
        if (_status == Status.Out_of_Services || _status == Status.Maintenance)
        {
            _status = Status.Idle;
        }
    }

    /// <summary>
    /// 关闭电梯
    /// </summary>
    public void Closedown()
    {
        if (_status == Status.Idle || _status == Status.Maintenance)
        {
            _status = Status.Out_of_Services;
        }
    }

    /// <summary>
    /// 维修电梯
    /// </summary>
    public void Repair()
    {
        if (_status == Status.Out_of_Services || _status == Status.Idle)
        {
            _status = Status.Maintenance;
        }
    }

    /// <summary>
    /// 外部呼梯
    /// </summary>
    /// <param name="floor">楼层</param>
    /// <param name="direction">方向</param>
    public void Call(int floor, Direction direction)
    {
        //如果电梯尚未运行则退出
        if (_status == Status.Out_of_Services || _status == Status.Maintenance)
        {
            return;
        }
        //如果没有这一层则退出
        if (!_floor.AllFloors.Contains(floor))
        {
            return;
        }
        //如果顶层按上行则退出
        if (floor == _floor.AllFloors[_floor.AllFloors.Count - 1] && direction == Direction.up)
        {
            return;
        }
        //如果底层按下行则退出
        if (floor == _floor.AllFloors[0] && direction == Direction.down)
        {
            return;
        }
        //增加任务
        _task.AddTask(floor, direction);
    }

    上述几个函数主要完成了电梯的开启、关闭、维修、外部呼梯过程。前几个较为简单,主要说下外部呼梯。其思想为:只有在电梯处于正常状态(非关闭、维护状态)才可以呼梯;不能在没有注册到电梯层数中的某层呼梯(比如电梯没有第4层,那么在第4层就不能呼梯);最高层不能向上呼梯;最底层不能向下呼梯。

    /// <summary>
    /// 内部按键
    /// </summary>
    /// <param name="key">楼层号</param>
    public void Click(int key)
    {
        //如果电梯未运行则退出
        if (_status == Status.Out_of_Services || _status == Status.Maintenance)
        {
            return;
        }
        if (!_stopfloors.ContainsKey(key))
        {
            _stopfloors.Add(key, key);
        }
    }

    /// <summary>
    /// 内部按键
    /// </summary>
    /// <param name="special_key">特殊按键</param>
    public void Click(SpecialKeys special_key)
    {
        if (_status == Status.Out_of_Services || _status == Status.Maintenance)
        {
            return;
        }
        switch (special_key)
        {
            case SpecialKeys.Alarm:
                _status = Status.Out_of_Services;
                break;
            case SpecialKeys.Call_Outside:
                // no use
                break;
            case SpecialKeys.Open_Door:
                if (_status == Status.Waiting)
                {
                    //等待状态下按下开门
                    _lastactive = Environment.TickCount;
                }
                else if (_status == Status.Closing)
                {
                    //关门状态按下开门
                    _lastactive = Environment.TickCount;
                    _status = Status.Opening;
                }
                break;
            case SpecialKeys.Close_Door:
                if (_status == Status.Waiting)
                {
                    //等待状态按下关门
                    _lastactive = Environment.TickCount;
                }
                /*
                if (_status == Status.Opening)
                {
                    //开门状态按下关门
                    _lastactive = DateTime.Now.Ticks;
                    _status = Status.Closing;
                }*/
                break;
        }
    }

    这两个函数是内部呼梯出发的函数。分为两个:一个是按普通的楼层按键,另一个则是按特殊按键。分别处理,重载函数,则可以显示程序的多态性。

    /// <summary>
    /// 取得到现在的运行状态
    /// </summary>
    /// <returns>当前运行状态</returns>
    public Status GetCuurentStatus()
    {
        return _status;
    }

    /// <summary>
    /// 取得到当前的楼层号
    /// </summary>
    /// <returns>当前楼层号</returns>
    public int GetCurrentFloor()
    {
        return _floor.Current;
    }

    /// <summary>
    /// 取得到电梯运行方向
    /// </summary>
    /// <returns>电梯当前运行方向</returns>
    public Direction GetDirection()
    {
        return _direction;
    }

    这三个函数即为取得到电梯当前运行状态的函数。通过它们,就可以准确判断电梯现在的状态了。

    为了外部测试程序的调用,我又写了如下几个相关函数:

    /// <summary>
    /// 列出所有内部经停的层
    /// </summary>
    /// <returns></returns>
    protected string ListAllStopFloors()
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < _stopfloors.Count; i++)
        {
            sb.AppendLine(_stopfloors.Keys[i].ToString() + "th floor");
        }
        return sb.ToString();
    }

    /// <summary>
    /// 设置委托以便参数检查
    /// </summary>
    /// <param name="status"></param>
    public delegate void GetAllStatusDelegate(string status);
    /// <summary>
    /// 设置事件
    /// </summary>
    public event GetAllStatusDelegate GetAllStatus;

    public void StopRunning()
    {
        _runinngPermit = false;
    }

    通过委托和事件的使用,就可以使外部的测试程序挂接到本类上,以取得到相关的输出更新。

    到目前为止,程序已经介绍了大半部分,还剩下四个最为关键的函数。敬请期待。

    July 18

    《软件实现技术》课程感想

    软件实现技术课程感想

     

        这学期开学的时候选课,看到《软件实现技术》是学院跟微软合作来开设的,当时的想法很单纯就是冲着微软开课去的。后来这门课一波三折,开始说是推迟开,又说下学期开,最终还是确定了推迟到6月份开设。当时差点改选了别的课,但是后来还是坚持了一下,因为微软的魅力,想通过微软工程师的讲解,了解前沿技术,提高自己的能力。通过这一个多月,共十几次课的学习,感受确实颇深。

    前两次课是蒋严冰老师大概讲了下这门课的要求和开课计划,然后讲了些微软的一些软件的开发方法。从第三次课开始就是由微软的工程师来给大家授课和交流。先后有6位微软的工程师来到我们偏远的大兴给我们授课。


        在第一位许景阳老师来讲课的时候,对他的经历很吃惊,9岁出去美国,20岁大学毕业进入微软。真的很佩服许老师的,许老师说自己的国语就小学水平,但是他的讲课给我的感觉就是言简意赅,话不多,但是说的很明白。在开始的时候许老师就布置了课程的大作业《嵩山项目》,我们小组选择的是“电梯的模拟”。

        第二位来自微软的工程师给我们讲授了项目管理的知识,关于Project managerProgress Manager的区别,讲了在项目的过程中真正的PM应该从什么角度去考虑需求,如何去管理好一个项目。

        后面的课都是邹欣老师和微软的其他工程师讲授的。每次课邹欣老师都会进行一个小的测验,半个小时的时间,根据一个实际的问题,写一个简单的算法来实现。在第一次测验的时候,在我们小组成员的共同努力下取得了最高的成绩,在第二次课上邹欣老师奖励我们小组一本他主编的书《编程之美——微软技术面试心得》。但是第二次的测验我们小组的成绩很不理想。

    课程中还安排了几次小组大作业“嵩山项目”的展示,主要由蒋严冰老师和郁莲老师指导完成。通过我们的展示,两位老师点评我们小组的整个项目是设计部分比较完善和详细,测试部分完成不是很好。在郁莲老师的指导之下,后来的项目完成过程中,在设计部分按时间进度完成的情况下,侧重了对测试部分的完成,到目前为止,整个项目完成还算比较理想,期待最后一次展示。。。

       

        通过这门课的学习和作业的完成,第一,作为我们小组的PM,我觉得自己确实学到了如何去管理一个小组,如何很好得去权衡各个部分之间的关系,从最开始的时候我觉得是这门课“最烂PM”到现在,我觉得我肯定已经不是刚开始的那样了,虽然还是有相当多的需要学习的地方,但是我自己在成长。第二,通过和小组成员内部以及小组之间在完成作业过程中的交流,是我这个技术本来就不是很强的人在coding方面有了提高。第二,通过微软各位工程师的授课和对微软亚洲研究院的参观,是我对神秘的微软有了更深入的了解,知道了自己要想做IT这一行,需要朝着神秘方向去努力。

     

        这学期的《软件实现技术》让我获益匪浅,感谢这门课的所有老师!

     

    大家可以在这里下载我们小组项目展示过程中的PPT和小组的一些文档。欢迎下载!

    课程PPT:http://cid-d24d563ec42d111c.skydrive.live.com/browse.aspx/Documents

    其他资料:http://cid-d24d563ec42d111c.skydrive.live.com/browse.aspx/Public

     

    电梯程序设计 代码级程序分析(二)

    今天这篇entry,主要来分析一下我们的Task类。这个类是一个非常关键的类,是存储外部呼梯的任务集合。正是这个类的存在,才使得我们的电梯程序能够支持多太电梯挂接。废话少说,请先看源代码:

    /// <summary>
    /// 外部呼梯任务集
    /// </summary>
    public class Task
    {
        /// <summary>
        /// 上行列表
        /// </summary>
        private SortedList<int, int> _upList = new SortedList<int, int>();
        /// <summary>
        /// 下行列表
        /// </summary>
        private SortedList<int, int> _downList = new SortedList<int, int>();

        /// <summary>
        /// 添加一个新的任务
        /// </summary>
        /// <param name="floor">呼梯层号</param>
        /// <param name="direction">呼梯方向</param>
        public void AddTask(int floor, Direction direction)
        {
            switch (direction)
            {
                case Direction.up:
                    if (!_upList.ContainsKey(floor)) //判断上升楼层是否已在队列
                        _upList.Add(floor, floor);
                    break;

                case Direction.down:
                    if (!_downList.ContainsKey(floor))//判断下降楼层是否已在队列
                        _downList.Add(floor, floor);
                    break;
                case Direction.none:
                    break;
            }
        }

        /// <summary>
        /// 判断是否没有上行或下行任务
        /// </summary>
        /// <returns></returns>
        public bool ifEmpty()
        {
            return (_upList.Count == 0 && _downList.Count == 0);
        }

        /// <summary>
        /// 判断本层后是否还有需要处理的楼层
        /// </summary>
        /// <param name="floor"></param>
        /// <param name="currentdirection"></param>
        /// <returns></returns>
        public bool ContainsMore(int currentfloor, Direction currentdirection)
        {
            switch (currentdirection)
            {
                case Direction.up:
                    for (int i = 0; i < _upList.Keys.Count; i++)
                    {
                        if (currentfloor < _upList.Keys[i])
                        {
                            return true;
                        }
                    }
                    for (int i = 0; i < _downList.Keys.Count; i++)
                    {
                        if (currentfloor < _downList.Keys[i])
                        {
                            return true;
                        }
                    }
                    break;
                case Direction.down:
                    for (int i = 0; i < _downList.Keys.Count; i++)
                    {
                        if (currentfloor > _downList.Keys[i])
                        {
                            return true;
                        }
                    }
                    for (int i = 0; i < _upList.Keys.Count; i++)
                    {
                        if (currentfloor > _upList.Keys[i])
                        {
                            return true;
                        }
                    }
                    break;
                case Direction.none:
                    break;
            }
            return false;
        }

        /// <summary>
        /// 判断在某一层是否需要停止
        /// </summary>
        /// <param name="floor">楼层</param>
        /// <param name="direction">现方向</param>
        /// <returns></returns>
        public bool NeedStop(int floor, Direction direction)
        {
            switch (direction)
            {
                case Direction.up:
                    if (_upList.ContainsKey(floor))
                    {
                        _upList.Remove(floor);
                        return true;
                    }
                    break;
                case Direction.down:
                    if (_downList.ContainsKey(floor))
                    {
                        _downList.Remove(floor);
                        return true;
                    }
                    break;
                case Direction.none:
                    break;
            }
            return false;
        }

        public string ListAll()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < _upList.Count; i++)
            {
                sb.AppendLine(_upList.Keys[i].ToString() + " up");
            }
            for (int i = 0; i < _downList.Count; i++)
            {
                sb.AppendLine(_downList.Keys[i].ToString() + " down");
            }
            return sb.ToString();
        }

    }

    整个的类使用了两个SortedList来存储电梯呼梯任务:_upList和_downList,分别用来存储上行的任务和下行的任务。在设计的时候,我们曾经考虑过使用一个列表来保存。但是由于相对来讲,还是比较复杂的,所以就放弃了,决定吸收很多其他小组的优秀思想,改为了两个队列。

    Task类中共有如下几个方法:

    • AddTask() -- 添加一个新的任务
    • ifEmpty() -- 判断是否有上行或下行任务,也就是列表中是否全空
    • ContainsMore() -- 判断本层后是否还有需要处理的楼层
    • NeedStop() -- 判断在某一层是否需要停止
    • ListAll() -- 打印出现在的电梯呼梯任务,为测试提供输出。今后也可以作为电梯面板的显示而存在

    当外部在某层按了某一个按钮后,系统就会相应AddTask()函数。当电梯由上行、下行准备停下时,也就是stopping的状态时,电梯要判断是否同方向上还有楼层需要处理。如果没有,那么则转向。ifEmpty函数在于判断列表是否为空,其对于整个电梯是否处于Idle空闲状态提供了判断一句。而电梯随着运行会插入一些中间的任务。所以我们判断在某层是否经停的时候,不可能在开始上升前就决定,而是到了某层后决定下层是否需要停。如果需要停,那么则转变为stopping状态减速,等待正常停止后开门。所以NeedStop()这个函数就提供了这样的一个功能。

    其实这几个函数的实现并不是很难。相对而言,ContainsMore() 这个函数稍微复杂一些,判断在同方向上是否还有呼梯任务,那么就要遍历两个列表,还要分上行和下行两种情况。另外,在AddTask中,要判断这个任务是否已经进入了列表。如果已经呼梯了,那么也就没有必要再次加入了。

    明天继续分析我们的项目,即将进入最关键的部分——ElevatorMain类

    July 17

    IE VS Firefox

       刚开始上网的时候,使得是IE5,感觉很神奇,只要输入一个网址,就可以感受互联网的神奇.随着网龄的增长,渐渐觉得IE浏览器无论从从能上还是界面上,都无法满足自己的需要.于是,遨游,TT等接踵而来,感觉确实方便的许多.最近,Paul给我推荐了firefox,刚开始的时候觉得没有什么,但是装上了许多插件以后,渐渐感受了firefox的神奇魅力.
       微软收购雅虎不成,反而成全了Google,微软在互联网时代屡屡让人感到廉颇老矣,就连其IE浏览器也磨磨蹭蹭地不思进取,直到IE7才推出其主要浏览 器对手 Firefox多年就有的Tab标签系统。业界认为互联网时代不属于微软,无论是搜索引擎还是社交网络,微软都被远远地落在后面,微软拿Google没办 法,IE浏览器也在体验和功能上被Firefox领先(呵呵,Firefox还内置着Google的搜索)。

      6月17日,微软 IE浏览器对手Mozilla新推出的Firefox3版在24小时内被下载800多万次,并创下了吉尼斯纪录。Firefox3新版浏览器最有特色的是 官方称作“Awesome Bar”的智能地址栏,有网友称之为“Nice URL”。Mozilla在firefox3中已经实现了中文域名功能,同时还有中文直达功能。在“智能地址栏”中输入中文“徐静蕾”、“老徐”甚或汉语 拼音“xujinglei”回车,即可直接访问徐静蕾(老徐)的博客。而无论是输入中文“徐静蕾.cn”,甚或带有中文标点的“徐静蕾。cn”,都可直接 进入徐静蕾粉丝网站“静盟”。
    微软的IE7已经够笨重了,虽然模仿Firefox等启用了Tab标签,但是占内存资源很大,以至于大多网民还在用IE6。IE7是和微软Vista系统 想配备的浏览器,但是和IE6一样,IE7依然不遵循国际标准,给网站设计者带来很多麻烦。Vista的推广不力也让IE7笨重的身体无法适应中国市场, 同时也让微软IE浏览器的市场份额被Firefox(尤其是Firefox3)抢占了巨大市场份额。

      处于Beta中的IE8跟随 Firefox3前日新闻中报道也将支持中文域名,不过按照微软通常产品周期IE8中文正式版恐怕要一年后才能出世,届时将已经落后对手Firefox一 年。而且据IE8测试者反映IE8依然如IE7这般笨重,即使支持中文域名也难以获得网民认可,何况当IE8正式推出时, Firefox3已经推出此功能至少半年以上,只能眼睁睁地看着被对手领先着。

        微软在过去凭借其操作系统平台优势可以打败先行的竞争对手,但是开放的互联网时代让微软过去的优势变得苍白无力了。像Firefox这样跨平台的产品相比,加上开源和开放,很多志愿者参与了进来,使得产品能够满足大部分用户的需求,这些是微软完全不具备的.可以说,Firefox受益开源,互联网开放精神是王道 !!

    电梯程序设计 代码级程序分析(一)

    根据系统的设计,我们在Deadline之前完成了电梯的程序编码工作。在接下来的几天,我们组将和大家一起分享一下自己的编码和程序设计,和大家一起交流,也希望能够得到您的反馈和建议。

    今天主要谈谈以下几个类或是枚举:

    /// <summary>
    /// 电梯运行的方向
    /// 为了便于区分,放弃了使用bool类型变量转而使用枚举
    /// </summary>
    public enum Direction
    {
        none,   //电梯停在某层的时候处于none状态
        up,
        down
    }

    以上是代表电梯运行方向的枚举。电梯不仅仅有向上和向下,还有处在停止、开门、关门等等的中间状态。
    另外,电梯的运行方向和电梯的状态不完全是一回事。举个简单的例子:电梯停在一层,无任务;这个时候我们在一层按向上的呼梯按钮,电梯的运行方向会指向向上,但是电梯并没有处在往上走的过程中,而是在开门、等待、关门。所以,Direction和Elevator.Status,代表两个东西,不能等同存放。

    /// <summary>
    /// 电梯运行状态
    /// 列举了电梯在其整个生命周期中的各个状态
    /// </summary>
    public enum Status
    {
        Opening,    //正在开门中
        Waiting,    //开门后等待状态
        Closing,    //正在关门中
        Up,         //电梯上行中
        Down,       //电梯下行中
        Stopping,   //到达某层中即将停止前的减速状态
        Out_of_Services,    //电梯不可用
        Idle,       //无任务时的空闲状态
        Maintenance //维修状态
    }

    上述枚举列举了我们所认为的电梯所有可能处在的状态。我们把电梯的开门关门过程细化,分为了Stopping(电梯减速准备停止)、Opening(正在开门)、Waiting(等待进入乘客)、Closing(正在关门)这几种状态。而设置Stopping状态,一来可以代表电梯减速的过程,二来可以在这个时候判断电梯下一步的运行方向。在坐电梯的时候我们就会发现,下一步方向的判断是在电梯开门前。

    /// <summary>
    /// 特殊键位
    /// 电梯内部的各个特殊键
    /// 其它楼层数字键则用int型代替
    /// </summary>
    public enum SpecialKeys
    {
        Open_Door,
        Close_Door,
        Alarm,
        Call_Outside
    }

    这个类定义了几个特殊按键。不同于几个其他组的设计,我们并没有把内部电梯按钮的层算进来算作Keys的枚举。因为我们认为电梯的层数应该是可以变化的,所以那一部分要独立出来响应相关的变化。

    上述几个枚举虽然看起来并不难,但是却十分重要。它们决定了整个电梯的状态,也是体现电梯设计思想的第一步工作——分析好电梯运行流程。

    July 16

    参观微软

          昨天有幸参观了微软公司亚洲研究院,来到我们期盼已久的微软公司心中难掩激动,在这里微软的工程师们向我们展示了微软的一些最新的技术,如微软的搜索引擎,微软的地图,以及一个虚拟的路面导航系统。看见了这么多的微软的新产品,我们唯一的感受就是震撼!!不愧是世界软件业的龙头老大啊!
         记得那天给我印象最深的就是微软推出的微软地球,此前我只知道google地球,但是通过微软员工的展示让我了解了微软地球以及它与google地球相比其自身的优势和特色。
         特色1:带标签的航空照片:允许用户查看航空版本的街道网络展示图,寻找感兴趣的信息。
         特色2:“Locate Me”:利用该功能用户可使用Wi-Fi方式(如掌上电脑、tablet、个人电脑等设备)或互联网方式迅速确定自己的方位并了解周围环境。该功能使用前需要下载特定的“Location Finder”软件。
         特色3:擦写板:用户可以将搜索到的某些信息包括:地名、地址和描述保存到擦写板中,建立文档、发送邮件或是加入博客中。
         特色4:Permalink:博客常用Permalink链接定期从事相同的搜索或是与他人分享特定的搜索。在点击“虚拟地球”服务中的Permalink链结之后,该服务会自动返回一个URL地址,重新创建一个地图效果和搜索服务。Permalink链接可以是书签、电子邮件或是拷贝进剪贴板。 
         特色5:黄页目录:用户在搜索中可以查找到高质量的黄页名单和信息。
         特色6:游戏视觉效果:用户在搜索时,可以有多种空间图片视觉效果,包括:急速上升、全景、环绕效果等。
         特色7:社区:用户随时可提供对“MSN虚拟地球”的反馈意见和建议,参与MSN虚拟地球开发的每一个过程。
         特色8:开发资源中心:第三方开发人员可通过该地址下载“MSN虚拟地球”的相关资源,将自己的网站充分整合到“MSN虚拟地球”服务中。

    走进微软

    软件实现技术第十三次课——走进微软亚洲研究院
        
          昨天是软件实现技术的第十三次课,走进微软。蒋严冰老师组织我们选课的40多位同学,走进微软亚洲研究院,听取微软年轻的工程师的演讲,了解了微软的最新的技术,最后跟我们学院毕业在微软工作以及目前在微软实习的同学进行了座谈。
     
         这是我第一次近距离接触微软的文化,经过在微软亚洲研究院的2个半小时,虽然很短暂,不能深入了解微软的文化细节,但是就这短短的150分钟,使我对神秘的微软有了新的认识。

         微软的工作环境我觉得给人的感觉很舒服,在我们上到3楼的时候最先吸引那个我的是墙上的一面镜子。镜子上有装饰图案的见多了,但是,这面镜子上的图案很特别。上面是一个人的证件照的样子,穿衬衣系领带,但是面部的地方是空的,我觉得放置这面镜子的可能就是为了让大家整理自己的衣装,有什么更深的用意,后来忘记问了。。。但是上面的这个图案确实很特别,我用电话给自己拍了一张照片。后来在办公室过道的墙上、洗手间的墙上都会看到这样的镜子,当然上面的人的图案也是不同的,有穿特别正式的,也有相当休闲的。。。
         
          微软工作的另一个让我很吃惊的地方就是员工可以穿着短裤,拖鞋来上班。这点我觉得是在很多公司看不到的,后来才知道在微软工作,对服装要求不严格,只要员工觉得舒服,穿如何的休闲都是可以的。在参观的过程中还有一个比较拗人性化的地方就是在3楼有一个休息区,那里有面对街道的落地窗,有舒服的沙发更甚者还有一台按摩椅。大家都知道IT员工工作久了都会有颈椎的问题,在工作之余能有一个全身的按摩椅躺上去进行下全身按摩,那因该是。。。。。。恩,肯定很舒服的。
    在跟一个师姐的座谈中,我感觉到,能够进入微软的员工都是很强的。微软最基本的要求是“三好学生”,当然不是学校评比出来的“三好学生”而是要求态度好,数学好,编程好的“三好学生”。各位谁想进入微软,抓紧时间努力啊。。。
         这里有我在微软亚洲研究院用手机拍的一张照片发上来大家看看。。。

    20080715(001)


     
    July 13

    盖茨名言

    1. 生活是不公平的,你要去适应它。
    2. 这个世界并不会在意你的自尊,而是要求你在自我感觉良好之前先有所成就。
    3. 刚从学校走出来时你不可能一个月挣4万美元,更不会成为哪家公司的副总裁,还拥有一部汽车,直到你将这些都挣到手的那一天。
    4. 如果你认为学校里的老师过于严厉,那么等你有了老板再回头想一想。
    5. 卖汉堡包并不会有损于你的尊严。你的祖父母对卖汉堡包有着不同的理解,他们称之为“机遇”。
    6. 如果你陷入困境,那不是你父母的过错,不要将你理应承担的责任转嫁给他人,而要学着从中吸取教训。
    7. 在你出生之前,你的父母并不像现在这样乏味。他们变成今天这个样子是因为这些年来一直在为你付账单、给你洗衣服。所以,在对父母喋喋不休之前,还是先去打扫一下你自己的屋子吧。
    8. 你所在的学校也许已经不再分优等生和劣等生,但生活却并不如此。在某些学校已经没有了“不及格”的概念,学校会不断地给你机会让你进步,然而现实生活完全不是这样。
    9. 走出学校后的生活不像在学校一样有学期之分,也没有暑假之说。没有几位老板乐于帮你发现自我,你必须依靠自己去完成。
    10. 电视中的许多场景决不是真实的生活。在现实生活中,人们必须埋头做自己的工作,而非像电视里演的那样天天泡在咖啡馆里。
    11. 善待你所厌恶的人,因为说不定哪一天你就会为这样的一个人工作。

    工作轻松

    本人正在某家美国的公司实习,常听到有人讲,你的工作太轻松了,中午12点多才到公司,都可以直接去吃饭了……

    可他并不知道,12点之前,其实是在家办公的。

    我现在的这家公司环境的确很好,这里的工作并不在乎工作的地点在哪里,工作的时间是几点,只要每天自己的任务都有进展,就可以了。早晨高峰去挤公共,路上堵的要命,对于员工,反而是在浪费时间。还不如在家先做些事情,等中午人少的时候再来公司。公司里很多同事也是如此,甚至有全职在家办公的。

    这当然要求很高的自控能力和主动性。本人的自控能力并不好,所以经常半夜都会工作;但无论如何,本人在主动性方面还是很有信心的。

    再想想看,如果工作真的太轻松了以至于每天都没什么事情做,是件好事情吗?

    自己或许可以暗爽,但久而久之,自己学习和锻炼的机会也就没有了,在公司中的价值也就失去了。

    公司会为没有价值的员工买单吗?

    当然不会。

    所以,要努力工作才行。

    学习也是一样。

    初尝苹果

    朋友 M 先生新买了一台 MacBook Pro,正巧他出差,先借来玩玩。

    苹果笔记本的硬件外观和软件界面名不虚传,相当漂亮。

    实用性方面……可能是我太习惯 Windows 了,所以 Mac 的操作方式还真不是很舒服。

    鼠标就一个键,还无线的,沉就一个字,还不如触摸板还用。后来听另外一位朋友 J 先生说有右键的,不过要按住 Ctrl 再按鼠标,就是通常意义上的右键了。我倒。

    输入法默认没有显示,要从系统设置里找出来。

    新建帐户开始不知道怎么操作,到处都是灰的,半天才发现居然有个小小的锁型按钮,需要再次输入密码才可以继续操作。

    不过总得而言,第一印象,这个“苹果”不是想象中的那么好吃。

    赶紧装了个 VirtualBox,虚拟回 Windows。

    小就是大

    前不久,朋友推荐给我一本书《小就是大》,书中共有154条改变事业和人生的商业妙想,如获至宝。

    1. AAA 汽车配件公司

    A 在电话簿中永远会排在第一位。前几年很多人在 QQ 昵称前加个空格也是这个道理。

    2. 网络与责任

    匿名本身就是礼貌的大敌。

    3. 被感染的橡子

    随着一名知识工人的技术水平不断提高,他用来积累知识的时间就会越来越长,而使用这些知识的时间却会越来越短。

    写博客,可以用非常短的时间把想法传达给众多目标群体。这种作法的优势是相当明显的,不需要支付任何管理成本。

    通过这种方式,不仅可以推动知识发展,还可以推动组织和本人不断取得进步。

    立即动手,把想法告诉别人,互动起来吧。

    ……

    July 12

    Temporary Post Used For Style Detection (dd5044d6-1a80-4c80-ad30-e47484fe160e - 3bfe001a-32de-4114-a6b4-4005b770f6d7)

    This is a temporary post that was not deleted. Please delete this manually. (06fee68f-b292-4547-a5c8-117438af9854 - 3bfe001a-32de-4114-a6b4-4005b770f6d7)

    第十一次课测验

    软件技术第十一次课
     
         课上邹欣老师表扬了我们小组的前一次测验做的比较好,奖励一本他主编的书《编程之美——微软技术面试心得》。刚拿到书,大概翻了翻,看到了许多代码,以为这本书只是介绍了一些问题的算法。课后仔细读了一下,发现自己当时的想法多么幼稚。书中是介绍了一些算法,但是主要的还是教我们遇到问题,在编程的过程中怎么去思考,怎么一步步去解决问题。现在不都在提倡‘思想’嘛,这本书里就教我们如何去发现问题、思考问题最终解决问题。遇到一个问题首先要分析问题,然后找到最普通的解决方法;当然不能就这样结束,用最普通的方法解决了,不能就此止步,我们还要去思考,这方法有没有什么不妥的地方,有没有能够改进的地方,就这样一步步去完善,一步步去优化自己的解决方案,最终能够高效得解决问题。
     
          课上老师还进行了第二次测验,当拿到这个题目的时候,就觉得不是很容易,由于小组内对C++特别熟悉的同学只有两位,在编写的过程中遇到了困难,不过最终还是写出来了。当时觉得代码写的不是很优,很匆忙的提交了结果,而且只写了4个TEST CASE 。预想这次的结果肯定不会很好。今天晚上看到助教公布的成绩,果然很令自己失望。助教的10个TEST CASE 只通过了2个,而且时间复杂度是n*n。我们小组需要学习的东西还是很多的。希望我们小组的各位成员都能够虚心向其他组的同学学习,提高自己的水平。
     
         本周二要去参观微软了,期待。。。
    July 10

    关于单元测试

          上周的课程我们每个小组都做了关于自己项目的presentation,我做的部分是测试部分的讲解,在最后的教师评定中我们组获得了第一名,而且测试部分也得到了老师的表扬,当然这是与小组其他成员的帮助是分不开的,他们积极的帮助我收集有关测试用例方面的资料,还有吕鹏飞同学的细心讲解是我对测试方面知识的认识又加深了一步,谢谢我的组员们。
          关于单元测试我以前总觉得没有什么必要,自从上了这门课后渐渐的认识到了他的重要性,自己也上网收集了一些这方面资料。通过学习我知道了单元测试的成本效率大约是集成测试的两倍系统测试的三倍!!这是多么惊人的数据!
          一下是我收集和总结的一些关于测试方面,尤其是单元测试的资料,希望同学们能够更加的重视起单元测试。
     
        单元测试的优点
        1、它是一种验证行为。
        程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
        2、它是一种设计行为。
        编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。
        3、它是一种编写文档的行为。
        单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。
        4、它具有回归性。
        自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。
        单元测试的范畴

        如果要给单元测试定义一个明确的范畴,指出哪些功能是属于单元测试,这似乎很难。但下面讨论的四个问题,基本上可以说明单元测试的范畴,单元测试所要做的工作。
        1、 它的行为和我期望的一致吗?
        这是单元测试最根本的目的,我们就是用单元测试的代码来证明它所做的就是我们所期望的。
        2、 它的行为一直和我期望的一致吗?
        编写单元测试,如果只测试代码的一条正确路径,让它正确走一遍,并不算是真正的完成。软件开发是一个项复杂的工程,在测试某段代码的行为是否和你的期望一致时,你需要确认:在任何情况下,这段代码是否都和你的期望一致;譬如参数很可疑、硬盘没有剩余空间、缓冲区溢出、网络掉线的时候。
        3、 我可以依赖单元测试吗?
        不能依赖的代码是没有多大用处的。既然单元测试是用来保证代码的正确性,那么单元测试也一定要值得依赖。
        4、 单元测试说明我的意图了吗?
        单元测试能够帮我们充分了解代码的用法,从效果上而言,单元测试就像是能执行的文档,说明了在你用各种条件调用代码时,你所能期望这段代码完成的功能。
    不写测试的借口

        到这里,我们已经列举了使用单元测试的种种理由。也许,每个人都同意,是的,该做更多的测试。这种人人同意的事情还多着呢,是的,该多吃蔬菜,该戒烟,该多休息,该多锻炼……这并不意味着我们中的所有人都会这么去做,不是吗?
    1、 编写单元测试太花时间了。
        我们知道,在开发时越早发现BUG,就能节省更多的时间,降低更多的风险。
        下图表摘自<<实用软件度量>>(Capers Jones,McGraw-Hill 1991),它列出了准备测试,执行测试,和修改缺陷所花费的时间(以一个功能点为基准),这些数据显示单元测试的成本效率大约是集成测试的两倍,是系统测试的三倍(参见条形图)。
        术语:域测试(Field test)意思是在软件投入使用以后,针对某个领域所作的所有测试活动。
        如果你仍然认为在编写产品代码的时候,还是没有时间编写测试代码,那么请先考虑下面这些问题:
            1)、对于所编写的代码,你在调试上面花了多少时间。
            2)、对于以前你自认为正确的代码,而实际上这些代码却存在重大的bug,你花了多少时间在重新确认这些代码上面。
            3)、对于一个别人报告的bug,你花了多少时间才找出导致这个bug 的源码位置。
        回答完这些问题,你一定不再以“太花时间”作为拒绝单元测试的借口。
    2、 运行测试的时间太长了。
        合适的测试是不会让这种情况发生的。实际上,大多数测试的执行都是非常快的,因此你在几秒之内就可以运行成千上万个测试。但是有时某些测试会花费很长的时间。这时,需要把这些耗时的测试和其他测试分开。通常可以每天运行这种测试一次,或者几天一次。
    3、 测试代码并不是我的工作。
        你的工作就是保证代码能够正确的完成你的行为,恰恰相反,测试代码正是你不可缺少的工作。
    4、 我并不清楚代码的行为,所以也就无从测试。
        如果你实在不清楚代码的行为,那么估计现在并不是编码的时候。如果你并不知道代码的行为,那么你又如何知道你编写的代码是正确的呢?
    5、 但是这些代码都能够编译通过。
        我们前面已经说过,代码通过编译只是验证它的语法通过。但并不能保证它的行为就一定正确。
    6、 公司请我来是为了写代码,而不是写测试。
        公司付给你薪水是为了让你编写产品代码,而单元测试大体上是一个工具,是一个和编辑器、开发环境、编译器等处于同一位置的工具。
    7、 如果我让测试员或者QA(Quality Assurance)人员没有工作,那么我会觉得很内疚。
        你并不需要担心这些。请记住,我们在此只是谈论单元测试,而它只是一种针对源码的、低层次的,为程序员而设计的测试。在整个项目中,还有其他的很多测试需要这些人来完成,如:功能测试、验收测试、性能测试、环境测试、有效性测试、正确性测试、正规分析等等。
    8、 我的公司并不会让我在真实系统中运行单元测试。
        我们所讨论的只是针对开发者的单元测试。也就是说,如果你可以在其他的环境下(例如在正式的产品系统中)运行这些测试的话,那么它们就不再是单元测试,而是其他类型的测试了。实际上,你可以在你的本机运行单元测试,使用你自己的数据库,或者使用mock 对象。
     
     
    July 08

    微软推Expression2中文版 欲挑战Adobe

       微软去年(2007)大张旗鼓推出设计软件套件Expression Studio后,即打算和Adobe在开发工具市场一较高下,继4月份推出英文版Expression 2后,现在中文版本也已经在台上市。

       新版Expression最大不同就是开始支持PHP语法,虽然还未正式在台发表中文版,不过在微软产品网站上已经默默挂上产品价格,台湾微软开发工具暨 平台推广处产品营销经理胡德民表示,该产品现在已经可取得。而同样是在去年发表、用来打造丰富网络应用程序(RIA)的使用者端播放技术 Silverlight,新版本现在也已进入Beta 2。

       微软规划将在今年10月发表Silverlight 2正式版,在正式版本推出后还会紧接着发表更新版的Expression 2.5、甚至第3版。今年3月微软在美国举办的Mix08首度发表Silverlight 2的技术蓝图,最大的改变就是支持.NET架构,也就是说其支持的程序语言除了1.0版原有的JavaScrip、Ajax之外,还可支持.NET的 VB、C#、IronPython、IronRuby等等。微软期望靠这项播放技术搭配开发工具,和Adobe在RIA市场一较高下。

       双方今年在RIA的技术、工具上都有重大发表,动作相当积极。而Adobe寄望以其在网络应用上耕耘较久的优势,来对抗微软Silverlight。

       事实上Adobe早已开始研发代号为Apollo的技术AIR(Adobe Integrated Runtime),今年2月终于推出正式版,让网络应用程序可透过AIR转换为桌面应用。不过当时甫推出的AIR只支持英文,预计本月(7)推出的AIR 1.1版本才会支持多国语言,而Silverlight 2也会开始支持中文。

       在微软的产品规划中,Expression、Silverlight分别是前后端互相搭配的开发及呈现工具,微软要藉此打入以前较不熟悉的开发市场。因此 从去年首度开推出开始,便积极规划新版工具的发展。今年微软在台的技术大会Mix08预计在8月底登场,到时开发技术也将是大会的重头戏。
      
        让我们试目以待!!

    Silverlight能否让微软看到曙光?

        前几次课上,我们看了关于Silverlight的一些演示,课后找了些资料,和大家分享一下。 
       
        当前市场上可以用来创建富互联网体验应用(RIA)的框架有不少,其中有来自Adobe公司的Flex(前身来自于Macromedia)和基于AJAX 的框架,为了满足开发者的需求,也为了抢占未来RIA开发领域的市场,微软也推出了Silverlight技术,并计划借助北京2008奥运会的机会对该 技术进行推广部署,目前微软正在说服美国国家广播公司(NBC)使用Silverlight 2.0实现在线转播。
     
        通过以上提到的三种技术的工具,还有市场上的其他一些影响力略小的工具,让一个Web开发者可以创建一个以Web页面为用户界面的应用程序,这种应用比以 前传统的基于HTML的网页要更加强大。这些技术扩展了HTTP/HTML的局限性,它们使用了运行在浏览器内的专有展现引擎,并且使用XML作为与服务 器之间进行通讯的主要语言。微软的进军富互联网应用说明,这个公司渴望创建属于它自己的技术来扩展它们的.NET框架的应用范围。
     
        富互联网应用提供给终端用户这样一个界面:与传统应用相比可以更快的响应用户。目前有许多框架可以用来创建这些应用,其中比较知名的有微软的 Silverlight和Adobe的Flex,这些框架将更多过程交给客户端,而不是像以前一样将其集中限制在一个服务器上。用户的浏览器不交换比较大 的单独信息块,而是通常以异步的方式每次发送一部分体积较小的数据。这意味着界面中只有相关的部分进行更新,从而可以让用户进行更多其它的操作,并且可以 保障其它操作的速度,这目前在所有富互联网应用框架中,Flex拥有最大的市场份额,其市场渗透率高达90%左右,是微软Silverlight的最大竞争对手。Flex建立在 Flash技术之上,Flash最初是被设计用来管理多媒体功能。Flash插件可以被今天的多数浏览器所支持,可以免费下载,应用程序在一个所谓的“沙 盒”中运行,即在浏览器本身之外的一个单独对象中,这是一个可以保护用户的安全的运行环境。如果运用合适的话,Flex可以让一个网站像一个瘦客户端一样 运行。
     
        对于任何客户端技术,都有自己的缺点。并非所有的浏览器默认安装Flash插件,而且Flash也不时的进行更新。无论是哪种情况,如果终端用户访问的页 面需要最新版本的时候,都需要他们下载最新的版本。其它富互联网应用框架也存在同样的问题,说其是一个缺点的原因还因为,并不是所有的用户会下载这个插 件,因此在很多情况下不能正常的访问这个页面。是传统的Web应用所不能完成的。因此,越来越多的Web应用开始选择使用某种类型的富互联网技术来让最终用户获得一个更佳的访问体验.
     
        微软推出了Silverlight来挑战Flex和其他已经在使用的富互联网应用框架。它基于.NET技术和Windows展现层(WPF),后者 是.NET 3.0的一部分。而.NET是一个以Windows为中心的技术,微软的Silverlight提供了一个轻量级的.NET版本,可以跨平台(除了 Windows之外还可以运行在苹果的Mac操作系统上),并且运行在一个浏览器沙盒中.
     
        目前,微软的Silverlight的主要缺点是,它不像Flex一样被广泛的使用,因此可能导致用户不去下载必要的插件,而是离开使用这个技术的网页。 使用微软的Silverlight还可能让开发者暂时放弃拥有大量客户的应用程序,而去接受一个新的、未经验证的应用程序。不过,必须承认的是,微软还是 一个值得尊敬和信赖的公司。
     
        但是,对于那些已经熟悉.NET的开发者来说,Silverlight还是非常具有吸引力的。如果使用Flex技术,开发者可能不得不从头学习Flex和 Flash技术;而对于一个有.NET开发经验的开发者来说,可以轻松的开始使用Silverlight。由于.NET已经在Web开发领域应用的非常普 遍,因此Silverlight可以在将来轻松的提高市场占有率。
     
        即时那些对AJAX或Flex已经非常满意的Web开发者,也将不久需要研究微软的Silverlight技术,而对于新的开发者则将需要从一开始就学习 微软的Silverlight。与其他用来创建富互联网应用的框架相比,微软的Silverlight因为以.NET为基础,所以需要一个更小的学习曲 线。