Infoq: 持续集成:从六个层次加速测试执行

 

在持续集成领域,一个产品的发布往往都有自己的过程周期(lifecycle),大体都会划分为:构建->部署->测试->发布等几个重要阶段,其中测试是发布产品前不可或缺的重要阶段,是产品质量的保证。而能让持续集成奏效,除了要求测试脚本更充分健壮,还要求测试脚本运行得更快更好。这点对于小型项目而言可能显得无关紧要,毕竟大多小项目的测试脚本不过百条,验证点不过千“点”;但对于一个大型项目而言,测试代码源文件可能成百上千,执行完所有的测试可能要等很久,而苦等之后的结果却可能是满眼的failure, 于是如果提高测试执行速度成为迫切需要解决的问题,试想把测试阶段从2小时压缩到1小时,再从1小时压缩到30分钟,每次时间压缩带来的不仅是技术人员本身的成就感,更是对整个产品发布过程体验的改善。

那么如何加速测试的执行呢?提起速度,我们立马可能联想到“性能”调优的步骤:先进行tuning,然后找到问题的瓶颈所在,最后逐个击破。本文暂不讨论如何进行这些步骤, 而是基于C++和Java为语言案例,TestNG和Google test为测试框架,Jenkins为持续平台做分析,从以下六个层次提出提高测试执行的一般方法:

硬件资源层次

工欲善其事必先利其器,提高硬件(CPU、内存、磁盘等)配置是改善执行速度的“硬”方法,硬件资源的优化不应仅仅局限在单机自身的各项指标提升,在需求不断提高的情况下,可以考虑实施虚拟机、分布式集群等方式来进一步获取更优的硬件资源,当然,涉及分布式执行时,可以借助以下持续集成平台层次的“软”实施来共同作用。

另外,在硬件资源紧张的情况下,不同项目或者不同团队可能不得不复用一套测试环境,造成可利用资源更为紧张,此时可以错开时间测试(例如A项目组测试定时在凌晨0点启动,B项目组定时在凌晨2点)以提高速度。

语言编码实现层次

测试代码本身也是代码,显而易见,如果代码编写时注重效率,速度上肯定有所收益。这点可能需要“纠结”于一些日常的编码细节:例如Java中Stringbuffer和Stringbuilder的比较;C++中是i++和++i的比较。这种语言层次提高效率的文章书籍很多,这里不做过多描述。在语言编码层次上最重要的不是这些语言细节,而是避免一些消费时间的测试代码设计,减少不必要的耗时操作,例如以下几点:

(1)  冗余的日志信息,不合理的日志级别设置等

输出日志带来的磁盘频繁访问必然让速度下降,所以在保证日志信息充足的前提下,尽量减少日志,或者只记录失败测试的日志(毕竟对于测试者而言很少去关注成功日志),可以让测试加快。

(2)  不合理的等待

用户执行完某个操作,必须等待某条件的发生(例如DB里面插入一条新数据)进而执行后续动作是测试中经常面对的场景,那么等待多久成为需要考虑的问题,假设用TimeUnit.MINUTES.sleep(1)等待一分钟,在10秒即可满足条件的场景下浪费的就是50秒,所以这里必须去考虑合理sleep的时间来兼顾对资源的消耗和运行速度的影响,同时在等待方式上也可以考虑是采用循环短时间条件等待或异步通知的方式去进行。

(3)  用例的过程

先执行完所有测试步骤,然后做对所有步骤做一次性校验,还是做完一步校验一步,这两种方式的速度在不同场景下有所不同,所以需要权衡;相类似的,对于需要获取DB连接的用例,是每条都执行获取DB连接然后释放连接,还是所有用例执行之前获取连接,所有case执行完之后释放连接也会对执行速度有所影响。

构建测试脚本层次

对于一个大型项目,源文件的数目庞大或依赖的dependency过多导致代码编译占用大量时间,如何提高编译代码的速度?除了使用更好的磁盘,注重代码编写时对编译速度的影响,还可以针对不同的语言采取不同的有效策略,例如针对C++, 使用make命令编译项目时,可以加上参数-j来并行编译项目。-j参数的含义可以参考下文:

-j [jobs], –jobs[=jobs]

    指定同步运行的作业(命令)的数量。如果有一个以上-j选项,那么只有最后一个有效。如果-j选项没有参数,那么编译过程就不会限制能够同步运行的作业的数量。

需要说明的是,编译过程可能要求特定的顺序而导致并行编译失败,如果遇到这种问题,可以先并行、后串行(去掉-j)重复执行一次以解决。

而对于java,Maven 3 开始支持并发build,提供了以下几种常见方式:

  1. mvn -T 4 clean install # Builds with 4 threads
  2. mvn -T 1C clean install # 1 thread per cpu core
  3. mvn -T 1.5C clean install # 1.5 thread per cpu core

同时使用maven管理java项目常出现时间消耗在依赖jar的下载上,此时可以检查是否有冗余失效的repository配置、较长的下载timeout时间设置、所选择repository的连接速度等,甚至在不同测试环境下可以使用 profile来管理repository来加速测试脚本构建。

测试框架支持层次

在测试框架支持层次上,应该充分运用框架本身提高的丰富功能来提高测试执行速度,以Java测试框架TestNG为例:

(1)  利用timeout控制失效等待

如果某个测试用例等待某条件的触发而陷入长时间等待,等待的时间过长往往对于用例本身而言已失效,特别是当条件永远无法满足时。因此需要控制用例执行允许的最大timeout时间。TestNG可以给test或者test suite设置 timeout时间,分别控制具体某个或一组(testing.xml配置)自动化测试用例执行的最大允许时间:

  1. @Test(timeout = 1000) 
  2.  testng.xml : <suite name=”Module Test” parallel=”none” time-out=”200000″>

 (2)  利用@BeforeTest、@BeforeClass等条件注解,减少无意义测试

测试的顺利完成都需要满足很多基础条件,例如需要测试环境就绪, 如果不使用@before类标签,则当条件不具备时,仍然会执行完所有的用例,必然带来巨大的时间浪费,因此使用@before类标签可以避免无意义的测试,@before标记的方法一旦失败,后续的相应的测试不会继续进行。

(3)  使用框架自带的多线程支持

例如对于TestNG自身,可以在testng.xml中设置parallel参数来指定是否并发以及并发的级别:methods|tests|classes,除了测试框架自身外,软件项目管理工具也可以提供多线程支持,例如maven的测试组件maven-surefire-plugin,提供了并发参数的设置:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.16</version>
        <configuration>
          <parallel>methods</parallel>
          <threadCount>10</threadCount>
        </configuration>
 </plugin>

持续集成平台层次

现在市场上存在不少持续集成平台,大多持续集成平台支持并发执行用例然后汇总、发布测试结果,从而最大化提高测试执行速度。而并发执行的前提是测试代码本身及测试代码的组织支持并发,如果测试本来就含有多个模块,那么直接并发运行多个模块,最后汇总结果即可。如java以testng.xml为模块,gtest以makefile为模块,所以相比较顺序执行4个模块,并发使用4个Job并发执行,那么时间压缩可以达到4倍。在实际应用中,即使在同一个模块,我们仍然面对自动化测试用例数目过多运行速度过慢的问题,此时,可以考虑将单一模块拆分成子模块,对于Java而言较简单,配置下测试套件的xml即可;而对C++而言,如果不允许直接复制粘贴原有的makefile,就需要重新设计makefile以复用, 例如将makefile中编译的test cases定义分拆到多个makefile(如下图测试模块1的makefile引用了共用的makefile并添加了自己的测试用例)中,然后并发执行多个makefile。

0502020

为并发执行多个job, 持续集成平台必须提供必备的支持,以Jenkins为例,可以使用multijob插件来实施,配置多个测试模块同时进行.

0502021

并发完测试后,讲所有测试结果汇总到一个地方,然后使用xunit plugin来汇总结果(如下图),它可以汇总多个文件,且支持cpptest、gtest等输出结果格式。

0502022

过程改进层次

在产品的持续集成生命周期中,可以将测试拆分成两部分放在两个阶段:基本功能快速校验阶段(fast fail)和基本功能之外的全面测试阶段。如果产品在第一阶段最基本的功能都无法通过,那么部署之后进行全面测试纯属浪费时间,这个阶段的引入可以快速的校验产品是否有必要开展全面测试。这点类似与测试用例中添加了@before类标签所带来的收效,不过更宏观且阶段划分的更清晰。

原有过程:构建阶段->部署阶段->测试阶段->发布阶段

细化后过程:构建阶段->部署阶段->基本功能快速校验阶段->全面测试阶段—>发布阶段

这种过程优化可以利用持续集成平台来支持,例如对于Jenkins系统,可以使用multijob插件,将基本功能快速校验和全面测试阶段分列在不同的phase即可,执行效果如下:

0502023

结论

通过上面由微观到宏观六个不同层次的分析可知,要加速测试用例的执行是一个系统的过程,单靠某一方面分析可能有所偏失,并不能将测试用例的执行速度发挥极致。同时,本文针对不同层次的分析也没有提供step by step的方式描述每一个细节,只是点到为止,所以读者可以根据自己采用的语言、测试框架、持续集成平台做类似更有针对性的分析,相信在综合不同层次的综合调优后,可以让持续集成实施的更快、更好。

Infoq: 代码覆盖的16种典型情景

代码覆盖(Code Coverage)为何物?相信程序员特别是测试人员不陌生,很多人都喜欢用代码覆盖来驱动测试的开展和完善。确实代码覆盖可以找出测试疏漏和代码问题,但是单纯的代码覆盖率高低并不能直接反映代码质量的好坏。大多我们的努力方向都是找出那些没有覆盖到的代码,然后补充用例,完善测试。而摆在我们面前的问题是:是否我们已经充分认识到哪些不需要、不能、必须被覆盖?只有对代码覆盖的各种情景了然于胸,才能不盲目乐观于代码覆盖率之高,悲观于代码覆盖率之低。在实践中(本文面向主要Java语言,基于emma工具),梳理可知,对于代码覆盖我们可能都会遇到以下16种典型情景:

1. 代码覆盖

即代码所有路径被经过,这种需要注意的是:不应该覆盖而被覆盖的情况。例如某种特殊异常就是不期望遇到的,但是遇到了,异常处理的代码也覆盖了,这时,我们应该追溯异常产生的根本原因,而不因覆盖了就直接忽略。

提示:不仅要关注未覆盖的代码,也要关注覆盖的,特别是偶然覆盖的代码。

2. 废弃的功能

一些功能点随着产品版本不断更新,可能会被取消,这部分功能可以直接移除,保留只会让代码看起来越冗余。如果某天需要参考或找回删除的那些代码,CVS/SVN工具就搞定了。

提示:不用的功能不需要覆盖,要及时删除,不通过保留或者注释的方式残留在代码中。

3. 工具类(助手类)、常量类等的私有构造器

工具类和常量类共有的特征是对外开放的都是静态方法,调用方法的时候,无需创建实例,所以推荐实践是创建一个private的构造器方法。这导致类的构造器代码无法覆盖(不考虑反射等方式)。

相反,如果某天发现对于这样的类覆盖率为100%,那检查下是否代码写的不规范: 用默认构造器,然后通过实例来调用静态方法。

例1:工具类

public final class StringUtil {

    public static String concatWithSpace(String... strings) {
        return concat(MarkConstants.SPACE, strings);
    }


    public static String concatWithSemicolon(String... strings) {
        return concat(MarkConstants.SEMICOLON, strings);
    }

    private StringUtil() {
    }

}

例2:常量类

public final class MarkConstants {
    /**
     * {@value}
     */
    public static final String SEMICOLON = ";";

    private MarkConstants() {
    }

}

提示:工具类(助手类)、常量类等的私有构造器不能被覆盖

4. 日志级别配置

日志级别不同,覆盖率高低也不同。在产品部署中,很少将日志的级别设成debug,因为日志占用磁盘空间会增长很快。只在做一些问题跟踪、调试时才会调高日志级别。

所以环境使用不同的日志级别,也会导致一些日志代码没有覆盖。如以下示例程序,不打开debug级别无法覆盖部分代码:

public static String formatPath(String path) {
     ValidationUtil.checkString(path);
     String returnPath = path.trim();
     if (!returnPath.startsWith(SPLIT))
         returnPath = SPLIT + returnPath;
     if (returnPath.endsWith(SPLIT))
         returnPath = returnPath.substring(0, returnPath.length() - 1);

     if (LOGGER.isDebugEnabled())
         LOGGER.debug(String
                 .format("[util]convert [%s] to [%s]", path, returnPath));
     return returnPath;
}

那么这部分代码需要覆盖嘛?需要。 假设代码误写成:

    LOGGER.debug(String.format("[util]convert [%] to [%s]", path, returnPath));

某天日志级别设为debug,就会发现报错。类似的还有日志中经常输出某个对象信息,但是该对象可能是null,从而抛出空指针异常。

提示:日志也是代码的一部分,需要通过调整日志级别来覆盖。

5. JVM等参数

程序的配置参数会直接影响代码路径覆盖,不仅包括业务上的一些配置,也包括依赖平台的参数,例如JVM参数除了会影响性能,也会影响代码的覆盖情况,例如断言相关参数:

    -ea[:...|:] 和-da[:...|:]

分别是启用和关闭用户断言(-esa/-eda,针对系统断言),在JAVA中断言是默认关闭的,所以涉及断言的代码默认无法覆盖。

提示:一些代码路径能否覆盖与JVM等参数有关,需要通过调整参数来覆盖

6. main()方法

一些程序员喜欢临时写一个main()方法方便于测试,完成测试后寻思以后还能方便测试就留了下来。导致产品代码中的这些代码无法被覆盖。在产品代码中,应该删除这些,部署的毕竟是产品代码,不是测试代码。

提示:main()方不需要被覆盖,产品代码不保留测试代码

7. 编码习惯写法

在编码过程中,常常有一些习惯写法,最常见的比如:(1) 覆盖toString()方法; (2) 以意义配对形式写一些方法:比如数据连接中Connect()搭配 DisConnect(), 枚举中常用的 toString()搭配fromString(),这些惯用的写法告诉读者一些涵义,但是不见得所有的方法都必须被调用,例如在产品应用中,我们可能启动起一个周期性的job,但是本身尚未添加“取消“功能(或本来就不需要停止)。自然也就无法调用: 但是它应该不应该存在? 笔者认为作为完整的功能应该存在。 类似的还有异常定义的时候,会定义很多重载的方法,虽然不见得每个都调用,但是不定某天就会被调用。

提示:编码习惯写法造成的未覆盖代码需要被覆盖,是代码的一部分。

8. 项目的使用方式

下面两种使用方式会造成代码不能全部被覆盖:

  1. 客户端Jar方式:部分代码作为客户端Jar包形式提供给他人使用;
  2. 分布式系统交互:分布式系统之间存在交互时,例如从系统1复制文件到系统2,如果始终按照从1到2的顺序,又仅仅统计系统1的代码覆盖肯定不能覆盖全部。 需要覆盖的代码虽出于一处,但是使用方式不同也会导致在不合并覆盖数据情况下代码未覆盖。

提示:项目使用方式造成的代码覆盖统计数据分散需要通过合并数据来覆盖。

9. 常用最佳实践

一些很难覆盖的最佳实践:例如对于一些资源(IO,lock)的释放,可能直接 try…catch然后记录异常,这些异常一般很难发生。

    public static void close(InputStream inputStream)
    {
         try {
             inputStream.close();
         } catch (Exception e) {
             LOGGER.warn("fail to close inputstream");
         }
    }

提示:常用最佳实践可以不覆盖。

10. 被拒绝的馈赠

在接口/抽象类定义的时候,有时候定义的一些方法子类并没有都实现,即常说的被拒绝的馈赠(Refused Bequest),这种问题如果是为了短期扩展需求多加了一些方法也可接受,否则还是需要重新继承体系设计。

提示:子类未使用“馈赠”,无需覆盖,需重新审视继承体系结构。

11. 代码覆盖工具未做合并

做代码覆盖时,往往工具本身不支持“合并”的功能,这导致以下问题存在:

时间上:

  1. 例如对于拥有cache的系统: 系统经过一段时间运行后,重新测试得到的代码覆盖往往不包括cache miss的情况。
  2. 手工测试问题:每次统计都需要重新完成全部手工测试,否则将丢失数据。

空间上:

  1. 负载均衡:现在大多系统应用都采用负载均衡技术,如果测试时间不够长且只统计一台系统的代码覆盖情况,往往不全面。

提示:代码覆盖本身要支持“合并”功能,对多个系统、不同时间的数据进行合并,才能覆盖的完整全面。

12. 系统逻辑重复

这里可分为两种情况:

  1. 组件之间重复:上层系统可能会对数据合法性做检验,但是下层系统出于系统的独立性目标,也可能对数据做二次校验,但是作为一个完整系统进行end-to-end测试时,就无法覆盖二次校验的代码;针对这种情况,需要拆开成独立组件进行测试。
  2. 组件内部重复:同一组件内多层重复逻辑确实可以纠正代码,例如在对于某个数据做多次同一类型校验,这种问题常出现于多人协同编码又缺乏沟通的情况中。

提示:逻辑重复导致的部分未覆盖要分辨是组件之间还是组件内部冗余,组件之间则需要覆盖,组件内部则要修改代码。

13.代码写法

有时候某些代码的写法,也会导致无法覆盖,例如对于代码调用顺序:多个类调用读取配置文件,而稍晚些调用的再次判断配置文件是否初始化,自然为已初始化。再如对于单例,某些代码写成这样:

    private static SingleInstance INSTANCE = new SingleInstance();

    public static SingleInstance getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SingleInstance(); 
        }
        return INSTANCE;
    }   

提示:代码写法造成的未覆盖,需要审查下是代码问题。

14. 隐式的分支

当代码中含有隐式的分支时,往往很难100%覆盖,例如上文提到的断言assert,貌似只有一句,但是即使启用断言仍然无法100%覆盖,例如下例还是显示黄色:分支未覆盖。

    public class AssertCodeCoverage {

        public void verify(Boolean b) {
         assert b;
        }

    }

究其原因,查看编译后的class可知,存在第三条指令:判断是否启用断言。在实际应用中,要么启用要么关闭,所以不可能覆盖所有分支。只能说启用断言,或许能提高指令覆盖率,下图为启用及关闭断言的覆盖率对比:

    public void verify(java.lang.Boolean b);
     0  getstatic com.test.coverage.AssertCodeCoverage.$assertionsDisabled : boolean [16]
     3  ifne 21
     6  aload_1 [b]
     7  invokevirtual java.lang.Boolean.booleanValue() : boolean [28]
    10  ifne 21
    13  new java.lang.AssertionError [33]
    16  dup
    17  invokespecial java.lang.AssertionError() [35]
    20  athrow
    21  return
    }
0x9a ifne 当栈顶int型数值不等于0时跳转。

因此,从这个角度来说,想覆盖断言,不仅要关闭断言完成测试用例,还要在开启断言情况下完成测试。

提示:隐式的分支(黄色)需要分析未覆盖分支。

15. 不在覆盖范围内

下面两种类型的代码不在代码覆盖统计范围内:

  1. Java接口,接口里面都是抽象方法的结合,不含有任何代码细节;
  2. 不含有可执行java字节码的方法:抽象方法和本地native方法。
    abstract class AbstractService {
        abstract String getString();  //不做统计
        String getName() {  return getString().trim();    }
    }

    public class BaseService extends AbstractService {
                  @Override
                  String getString() {      return "zookeeper";    }
    }

提示:不在统计范围内的直接忽视。

16. 应用程序在线实时收集的局限

对于一个在线服务的应用程序的代码覆盖统计,需要实时,但是如果关闭服务,显然收集不到。所以关闭服务的代码无法被统计,因为关闭服务的时间很短暂,很难介入。

protected void stopService() throw Exception()

提示:对于这种需求可以采用单元测试的方式去覆盖,否则对于应用程序的在线实时收集不加任何特殊代码处理的话是收集不了的。

小结:

通过对上面15种典型情况的概括,相信大家对代码覆盖的常见情景已有大概印像,在实际分析中,可以按照以下规则进行:

  1. 内容:包->类->方法->代码;
  2. 优先级: 核心业务类->普通业务类->工具助手类->常量类;

经过不断的分析和推敲,相信大家得到的不仅是一个较高的代码覆盖率,更是对代码质量的一份信心。

Infoq: Java编码易疏忽的十个问题

在Java编码中,我们容易犯一些错误,也容易疏忽一些问题,因此笔者对日常编码中曾遇到的一些经典情形归纳整理成文,以共同探讨。

1. 纠结的同名

现象

很多类的命名相同(例如:常见于异常、常量、日志等类),导致在import时,有时候张冠李戴,这种错误有时候很隐蔽。因为往往同名的类功能也类似,所以IDE不会提示warn。

解决

写完代码时,扫视下import部分,看看有没有不熟悉的。替换成正确导入后,要注意下注释是否也作相应修改。

启示

命名尽量避开重复名,特别要避开与JDK中的类重名,否则容易导入错,同时存在大量重名类,在查找时,也需要更多的辨别时间。

2. 想当然的API

现象

有时候调用API时,会想当然的通过名字直接自信满满地调用,导致很惊讶的一些错误:

示例一:flag是true?

boolean flag = Boolean.getBoolean("true");

可能老是false。

示例二:这是去年的今天吗(今年是2012年,不考虑闰年)?结果还是2012年:

Calendar calendar = GregorianCalendar.getInstance();
calendar.roll(Calendar.DAY_OF_YEAR, -365);

下面的才是去年:

calendar.add(Calendar.DAY_OF_YEAR, -365); 

解决办法

问自己几个问题,这个方法我很熟悉吗?有没有类似的API? 区别是什么?就示例一而言,需要区别的如下:

Boolean.valueOf(b) VS Boolean.parseBoolean(b) VS Boolean.getBoolean(b);

启示

名字起的更详细点,注释更清楚点,不要不经了解、测试就想当然的用一些API,如果时间有限,用自己最为熟悉的API。

3. 有时候溢出并不难

现象

有时候溢出并不难,虽然不常复现:

示例一:

long x=Integer.MAX_VALUE+1;
System.out.println(x);

x是多少?竟然是-2147483648,明明加上1之后还是long的范围。类似的经常出现在时间计算:

数字1×数字2×数字3… 

示例二:

在检查是否为正数的参数校验中,为了避免重载,选用参数number, 于是下面代码结果小于0,也是因为溢出导致:

Number i=Long.MAX_VALUE;
System.out.println(i.intValue()>0);

解决

  1. 让第一个操作数是long型,例如加上L或者l(不建议小写字母l,因为和数字1太相似了);
  2. 不确定时,还是使用重载吧,即使用doubleValue(),当参数是BigDecimal参数时,也不能解决问题。

启示

对数字运用要保持敏感:涉及数字计算就要考虑溢出;涉及除法就要考虑被除数是0;实在容纳不下了可以考虑BigDecimal之类。

4. 日志跑哪了?

现象

有时候觉得log都打了,怎么找不到?

示例一:没有stack trace!

 } catch (Exception ex) {
    log.error(ex);
 }

示例二:找不到log!

} catch (ConfigurationException e) {
    e.printStackTrace();
}

解决

  1. 替换成log.error(ex.getMessage(),ex);
  2. 换成普通的log4j吧,而不是System.out。

启示

  1. API定义应该避免让人犯错,如果多加个重载的log.error(Exception)自然没有错误发生
  2. 在产品代码中,使用的一些方法要考虑是否有效,使用e.printStackTrace()要想下终端(Console)在哪。

5. 遗忘的volatile

现象

在DCL模式中,总是忘记加一个Volatile。

private static CacheImpl instance;  //lose volatile
public static CacheImpl getInstance() {
    if (instance == null) {
        synchronized (CacheImpl.class) {
            if (instance == null) {
                instance = new CacheImpl (); 
            }
        }
    }
    return instance;
}

解决

毋庸置疑,加上一个吧,synchronized 锁的是一块代码(整个方法或某个代码块),保证的是这”块“代码的可见性及原子性,但是instance == null第一次判断时不再范围内的。所以可能读出的是过期的null。

启示

我们总是觉得某些低概率的事件很难发生,例如某个时间并发的可能性、某个异常抛出的可能性,所以不加控制,但是如果可以,还是按照前人的“最佳实践”来写代码吧。至少不用过多解释为啥另辟蹊径。

6. 不要影响彼此

现象

在释放多个IO资源时,都会抛出IOException ,于是可能为了省事如此写:

public static void inputToOutput(InputStream is, OutputStream os,
           boolean isClose) throws IOException {
    BufferedInputStream bis = new BufferedInputStream(is, 1024);
    BufferedOutputStream bos = new BufferedOutputStream(os, 1024);  
    ….
    if (isClose) {
       bos.close();
       bis.close();
    }
}

假设bos关闭失败,bis还能关闭吗?当然不能!

解决办法

虽然抛出的是同一个异常,但是还是各自捕获各的为好。否则第一个失败,后一个面就没有机会去释放资源了。

启示

代码/模块之间可能存在依赖,要充分识别对相互的依赖。

7. 用断言取代参数校验

现象

如题所提,作为防御式编程常用的方式:断言,写在产品代码中做参数校验等。例如:

private void send(List< Event> eventList)  {
    assert eventList != null;
}

解决

换成正常的统一的参数校验方法。因为断言默认是关闭的,所以起不起作用完全在于配置,如果采用默认配置,经历了eventList != null结果还没有起到作用,徒劳无功。

启示

有的时候,代码起不起作用,不仅在于用例,还在于配置,例如断言是否启用、log级别等,要结合真实环境做有用编码。

8. 用户认知负担有时候很重

现象

先来比较三组例子,看看那些看着更顺畅?

示例一:

public void caller(int a, String b, float c, String d) {
    methodOne(d, z, b);
    methodTwo(b, c, d);
}
public void methodOne(String d, float z, String b)  
public void methodTwo(String b, float c, String d)

示例二:

public boolean remove(String key, long timeout) {
             Future< Boolean> future = memcachedClient.delete(key);
public boolean delete(String key, long timeout) {
             Future< Boolean> future = memcachedClient.delete(key);

示例三:

public static String getDigest(String filePath, DigestAlgorithm algorithm)
public static String getDigest(String filePath, DigestAlgorithm digestAlgorithm)

解决

  1. 保持参数传递顺序;
  2. remove变成了delete,显得突兀了点, 统一表达更好;
  3. 保持表达,少缩写也会看起来流畅点。

启示

在编码过程中,不管是参数的顺序还是命名都尽量统一,这样用户的认知负担会很少,不要要用户容易犯错或迷惑。例如用枚举代替string从而不让用户迷惑到底传什么string, 诸如此类。

9. 忽视日志记录时机、级别

现象

存在下面两则示例:

示例一:该不该记录日志?

catch (SocketException e)
{
    LOG.error("server error", e);
    throw new ConnectionException(e.getMessage(), e);
}   

示例二:记什么级别日志?

在用户登录系统中,每次失败登录:

LOG.warn("Failed to login by "+username+");

解决

  1. 移除日志记录:在遇到需要re-throw的异常时,如果每个人都按照先记录后throw的方式去处理,那么对一个错误会记录太多的日志,所以不推荐如此做;但是如果re-throw出去的exception没有带完整的trace( 即cause),那么最好还是记录下。
  2. 如果恶意登录,那系统内部会出现太多WARN,从而让管理员误以为是代码错误。可以反馈用户以错误,但是不要记录用户错误的行为,除非想达到控制的目的。

启示

日志改不改记?记成什么级别?如何记?这些都是问题,一定要根据具体情况,需要考虑:

  1. 是用户行为错误还是代码错误?
  2. 记录下来的日志,能否能给别人在不造成过多的干扰前提下提供有用的信息以快速定位问题。

10. 忘设初始容量

现象

在JAVA中,我们常用Collection中的Map做Cache,但是我们经常会遗忘设置初始容量。

cache = new LRULinkedHashMap< K, V>(maxCapacity);

解决

初始容量的影响有多大?拿LinkedHashMap来说,初始容量如果不设置默认是16,超过16×LOAD_FACTOR,会resize(2 * table.length),扩大2倍:采用 Entry[] newTable = new Entry[newCapacity]; transfer(newTable),即整个数组Copy, 那么对于一个需要做大容量CACHE来说,从16变成一个很大的数量,需要做多少次数组复制可想而知。如果初始容量就设置很大,自然会减少resize, 不过可能会担心,初始容量设置很大时,没有Cache内容仍然会占用过大体积。其实可以参考以下表格简单计算下, 初始时还没有cache内容, 每个对象仅仅是4字节引用而已。

  • memory for reference fields (4 bytes each);
  • memory for primitive fields
Java type Bytes required
boolean 1
byte
char 2
short
int 4
float
long 8
double

启示

不仅是map, 还有stringBuffer等,都有容量resize的过程,如果数据量很大,就不能忽视初始容量可以考虑设置下,否则不仅有频繁的 resize还容易浪费容量。

在Java编程中,除了上面枚举的一些容易忽视的问题,日常实践中还存在很多。相信通过不断的总结和努力,可以将我们的程序完美呈现给读者。

Paper: 远程混合学习方式及支持平台构建方案研究

 

傅健1  杨雪1  陆绍圆2

(1吉林大学高等教育研究所吉林长春130012  2无锡联企网络科技有限公司 江苏无锡214000)

【摘要】新兴的移动学习与传统的网络学习具有很强的互补性,可以预见传统的网络学习与移动学习的混合使用将成为未来远程学习的主流方式。提出两种学习方式的混合形式和三个发展阶段,并对目前可行的支持平台的构建方案进行分析,为远程学习的进一步发展提供参考。

【关键词】混合学习,移动学习,传统网络学习,平台构建

中图分类号              文献标识码              论文编号

Reserch for Distance Blend-learning and Supportting Platform Construction’s Method

FU Jian1   YANG Xue1  LU Shao-yuan2

1 Institute of Higher Education, Jilin University, Jilin,Changchun, 130012, China;2 Wuxi Cmswe Technology Co.,ltd,Jiangsun,WuXi,21400,China)

Abstruct: M-learning and traditional E-learning have more complementarity,so the blended learning conbine traditional E-learning and M-learning will become the leading learning style in the future.This article present the blending forms and three development phases based M-learning and E-learning,also analyzes the feasible technology methods to constrcut supportting platform,provides references for the development of distance learning.

Keywords: Blended learning, M-Learning, E-leraning, Platform construct

 

随着通信技术的发展和移动设备性能的提高,将手机、PDA等移动设备应用于学习,不仅能提高学习效率而且更加新颖时尚,WAP浏览、短信收发等多种可选方式,使其成为远程学习的有效补充,但目前移动学习所受局限性较大,表现在屏幕尺寸、终端性能、可利用资源等诸多方面,而相对而言,传统网络学习虽然没有移动学习便捷,但技术应用较为成熟,资源丰富,学生认可度也较高,因此在提倡移动学习方式的同时,若能结合传统网络学习已有理论、实践成果及优势来弥补移动学习当前或与生俱来的“不足”,可以构建较好的远程学习方式,通过分析传统网络学习与移动学习的互补性及结合的教育应用价值,进而提出将传统网络学习与移动学习两种学习方式混合的方式及具体方案,为远程学习方式研究与实践者提出参考。

  移动学习与传统网络学习互补性分析

目前移动学习的定义版本很多,定义的角度涉及到技术、学习等多方面,但较为认同的是“移动学习是一种在移动计算设备帮助下的能够在任何时间、任何地点可以进行的学习, 移动学习所使用的移动计算设备必须能够有效地呈现学习内容, 并提供教师与学习者之间的双向交流”。[1]

移动学习,是传统网络学习的有效扩展和补充,它与传统网络学习以是否使用移动设备来划分,两种学习方式具体特性比较见表1:

表1 传统网络学习与移动学习比较

  传统网络学习 移动学习
学习类型 正式学习 非正式学习
主要面向对象 在校学生 工作学习时间分散的人群,追求新颖的青少年
使用设备 普通电脑:笨重 手机、UMPC、PDA等移动设备:轻,便捷
学习时间 时间可较集中,但对时间

无缝学习支持不好

24×7可随时按需学习
学习地点 教室、机房、私人场所等室内 无线网络覆盖地
资源开发难度与成本 容易,费用稍低 终端多样性及局限性等导致开发难度大,开发费用高
学习感 较熟悉 “个人拥有”感较强、新颖时尚
对情景学习的支持 对真实场景下的学习支持较差 移动性好,更支持真实情景学习与体验式学习
学习方式特点 稳定、不灵活、资源丰富 不稳定、灵活、资源不丰富
学习效果评价 容易 学习者分散等导致学习效果跟踪难,不易评价
当前主要应用形式 网课等 成人英语学习(如行学一族)、教学管理、师生、生生短信交流

从上图可以看出,移动学习与传统网络学习具有较强的互补性,若将两种学习方式结合,则具有以下优势:

(1)应用时空的互补性:传统网络学习可用于集中时间的学习,多在教室机房或私人场所进行,效率较高,但是对特定情景下的学习支持欠佳,而移动学习多发生在零散的时间和非正式的场合,虽效率不高,但对情景学习、体验学习支持较好,两者互为补充;

(2)当前应用的互补性:传统网络学习资源及平台功能完善,终端性能也较好,适合做复杂的功能处理,但却不便捷,移动学习的学习设备虽然局限性较大,但较为便捷;

(3)学习“感受”的互补性:传统网络学习使学生具有更强的“熟悉感”和“支持感”,移动学习下,移动设备使学生获得更强的“拥有感”和“自主感”;

(4)主要面向对象的互补性:传统网络学习要求时间较为集中,因此对于在校学生更适合,而对于在职成人等学习时间分散的学习者而言,移动学习更为适合。

综上,将移动学习和传统网络学习结合,可实现功能互补的教育应用,满足随时随地按需学习需求,例如:对于室外作业人员,工作中可以用手机随时记录自己的工作“心得”,工作之余时间较宽裕情况下则可在普通电脑上接入互联网来集中整理日常零散收集的的“心得”,同时,也可以将自己的“心得”筛选加工后作为日后移动学习的材料。下面介绍如何将两种学习方式进行混合以支持类似教育应用。

  移动学习与传统网络学习混合的阶段及方式

移动学习方式主要包括短信方式、WAP( Wireless Application Protocol,即无线应用协议)浏览方式和非标准移动学习(无线网络方式),而传统网络学习方式是指在普通PC机上进行的Web页面浏览方式,因此可将移动学习与传统网络学习按照具体学习方式的混合进行结合,按照混合程度的高低可划分为三个阶段(如图1):

111

图1 传统网络学习与移动学习混合方式及阶段

1 组合阶段:即提倡两种学习方式一起使用,但两者相应的支持平台没有直接联系,这种方式没有统一的平台,因此混合度较低,学习缺乏设计,学习效果欠佳;

2 集成阶段:作为标准的移动学习,主要是短信方式和WAP浏览方式,而传统网络学习是以WEB站点浏览方式实现,因此在集成阶段,需要一个混合平台能在保证数据统一情况下支持短信方式/WAP浏览方式和WEB站点的混合,具体如下:

(1)基于WAP和WEB的混合平台:手机等移动终端通过WAP页面(即WML等)浏览的方式访问平台,而普通PC通过传统WEB页面浏览访问平台,目前较常见的混合方案是使用XML技术开发移动设备及PC内容自适应的平台;

(2)基于SMS和WEB的混合平台:短信方式(SMS,EMS,MMS等)的学习不仅可以传递学习内容、教务信息,还可以作为师生、生生之间情感交流的重要工具,但短信学习与传统网络学习相比不易跟踪管理,因此将其与传统网络学习混合,开发整合短信功能的WEB平台,通过短信方式将网页与学习者联系,更易跟踪与管理,也便于突破传统的短信群发学习方式,体现个性化学习;

3 统一阶段:主要以无线网络方式的实现为主,即使用笔记本、UMPC等高性能移动设备学习传统网络资源,这种方式虽然能支持两种学习方式,但目前对移动设备要求较高,对移动学习的优势体现不明显,初期投资成本较大,推广存在困难。

  支持平台构建方案关键技术探究

    从上面可知,将两种学习方式根据混合程度不同,可划分为不同的阶段,但就目前而言,集成阶段更能体现对传统网络学习与移动学习的混合支持,因此下面对该阶段的两种混合方式下支持平台的构建进行研究:

    1 基于WAPWEB的混合平台构建

构建支持多种类型终端的学习平台传统的方案往往是采用XML技术开发终端自适用的学习平台(如图2[2]),XML,即可扩展标记语言,作为一种标准通用标记语言,它是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具[3]。将学习平台开发成采用XML技术的通用平台,优势是可以自适用终端、开发效率高,但目前开发难度较大,且不区分客户端类型:同时给性能悬殊的客户端(手机、普通PC)提供相同功能,缺乏对传统网络学习和移动学习本身特点的考虑及优势利用,同时也不利于内容的针对性调整,当前开发与应用的“通用”网络课程更多的是以移动学习方式特点来开发,缺乏对传统网络学习的优势体现。

222

图2 XML技术原理

因此针对以上方案的弊端,笔者结合传统网络学习和移动学习本身特点及终端的的差异性提出“功能分流、分层开发、站点复用”的方案,该方案技术原理是:根据实际学习内容依据两者学习方式的特点将平台功能进行分化(例如将管理功能多由传统网络学习完成),然后采用分层开发技术开发可复用部分(如图2)”的两套站点,最后以“站点复用”(如图3)方式部署在同一服务器中,以分别面向移动学习和传统网络学习,这样的开发不仅能到达一定的复用(开发复用、站点复用),也能在保证数据统一的前提下根据移动学习和传统网络学习的特点及实际需要进行针对性设计和内容的调整,例如对于资源的管理功能可更多集成到传统网络学习接入方式下完成;而用于移动学习的内容,可以使用简洁的形式呈现,弱化传统网络学习一些管理功能,强调知识的易获取性,这样一定程度上强化了移动学习的易用性,该方案涉及以下两方面技术应用:

(1)开发复用的应用:开发复用是指在开发中可以对程序的代码或者数据库进行一定程度的复用,以JSP语言为例,如图3,若采用MVC方式,则在控制层和模型层及数据库都可以在一定程度复用,站点的区分交给视图层完成:使用JSP动态生成HTML或WML的方式来标识不同终端可访问页面,这种方式提高了开发的效率也保证了数据库数据的一致性。

333

图3 “开发复用”的应用

(2)站点复用的实现:如图4,在同一台服务器上架设不同的站点,可以节约成本,而且每个站点具有独立的虚拟实体,通过确保站点具有不同的标识符即可使用户能正确访问站点,通常采用主机头、IP、端口号三种标识符来标识,例如:使用主机头标识时,可用http://wap.xxx.com和http://web.xxx.com来分别标识移动学习与传统网络学习的访问地址。

444

图4 “站点复用”的应用

以上方案若在对系统要求接入和处理速率要求较高的情况下,还需要进一步的强化:一般情况下,如图5(左),可以将数据库存储到单独的服务器上,以提高数据调用速度和数据安全性,以上方案,若需进一步提高平台的页面访问速度,可以将Wap和Web站点部署到不同的服务器上,如图5(右),但这种方案虽提高页面速度却较低了复用效率,需考虑实际成本等条件,谨慎使用。

555

图5 优化的设计方案

小结:以上提出的方案,虽然较传统的XML方案部署成本有所提高,但开发难度较低,易于根据实际需要对内容进行调整,也易于体现两种学习方式本身的特点和优势,可作为一种参考。

2 基于SMSWEB的混合平台构建

短信息服务(SMS)以其及时性强、使用快捷成为移动学习主要学习方式之一,当前开发混合SMS功能的WEB平台(类似twitter[4]平台),实际上是在传统的网页式学习上加入短信收发功能,需从移动服务商租用短信接口,并对其进行调用,如图5所示,具备SMS短信功能的平台即可以让普通PC机进行浏览,也可以在短信网关帮助下通过调用URL方式,利用移动、联通等无线通信网络给注册学习者的手机等移动设备发送信息,相反,移动设备也通过与指定短信中心号码收发信息来将短信内容呈现在属于私人或公共的网页上。这样的混合应用可以利用传统的网络学习解决短信息内容学习难于跟踪和管理的缺点,也可以兼顾短信息学习的便捷优势。例如在知识管理上,用户工作时可以通过短信方式将自己的灵感性知识发送到指定号码,这样就可以在工作之余登录WEB平台,对已收的灵感知识进行集中处理和学习。

666

图5 混合SMS功能的WEB平台功能示意

结语

通过以上对移动学习和传统网络学习互补性分析及混合方式研究,即可将两种学习方式进行有效混合,通过相应的技术方案构建的混合学习平台,可以在技术上提供支持,无论是新兴的移动学习还是传统网络学习,都各有优势与弊端,在实际教育应用中,还应多将两者优势结合而不是顾此失彼,方能提高学习效率,可以预见,随着教育信息化步伐的推进,兼顾传统网络学习与移动学习的优势的混合学习将成为的远程学习的趋势。

参考文献

[1]叶成林,徐福荫,许骏.移动学习研究综述[J].电化教育研究,2004,(3):12-19.

[2]黄荣怀,Jyri Salomaa.移动学习-理论·现状·趋势[M].北京:科学出版社,2008

[3]孟祥光.基于移动学习的网络课程研究[D].北京:首都师范大学,2005,11-12

[4]冀鹏飞,江玲.基于Twitter的移动学习策略研究[M].现代教育技术,2008,(9):106-108.

 

作者简介:

傅健,1985年,男,安徽巢湖人,吉林大学高等教育研究所教育技术学硕士在读

杨雪,教授,辽宁大连市人,吉林大学高等教育研究所(130012)

陆绍圆,男,江苏无锡人,无锡联企网络科技有限公司(214000)

 

 

Paper: 远程学习中浅学习问题及解决策略探究

傅健,  杨雪

(吉林大学高等教育研究所长春 130012)

摘要:浅学习问题(浅阅读、浅协作、浅自主等)一直是远程学习中存在的问题,通过对浅学习问题产生原因的透析,结合当前最新的一些设计理念和机制,从而在宏观上提出了浅学习问题的解决策略,以提高远程学习的学习效率。

关键词:远程学习;网络课程;浅学习;浅阅读;浅自主;策略

中图分类号                      文献标识码A                        文章编号

 

The Shallow Learning Problem in Modern Distance Education and the Counter Strategy

FU Jian,  YANG Xue

( The Institute of Higher Education of Jilin University, Changchun 130012, China)

[Abstract] Shallow learning is a universal problem for distance learners. By analyzing the causes of

shallow learning and introducing the latest design concept and mechanism, the article puts forward a

solution to the problem in order to improve the efficiency of E-learning.

[Key words] distance learning; online courses; surface learning; surface reading; surface independent; solution strategy

 

引言

随着网络教育应用的推广,远程学习资源已经随处可获:数量庞大、设计学科领域多、传播范围广,但远程学习方式并没深入人心,通过学生学习时的行为观察发现一些学生的学习不是太投入,很多学生虽然开始投入热情,但很快出现急躁等行为,即使一些学习目标明确、态度端正的学生有时学习效果也不尽如人意,究其原因主要是远程学习中的“浅学习“问题存在,本文试图分析浅学习问题产生的内外部原因,并试图从外部原因(教师、课程)解决此问题,从而为以后的远程学习设计提供参考。

  浅学习概念及在远程学习中的表现 

浅学习问题的研究起源于电子媒体学习过程中学习效率低下问题的研究,目前尚无统一定义,但比较认同的是:浅学习是一个不改变学生深刻思考的过程,即只是理解和记忆现有资料,且热衷于新的资料并在简单的层面理解它。下面通过对比浅学习和深学习来揭示浅学习的特征,如表1:

表1 浅学习与深学习过程比较

学习类别 特征 学习效率
浅学习 1 用已有的概念或想法表述内容,不加入个人的评论或不提升观点;2 学习过程中不善于提出问题;

3 询问一些与问题无关或是对理解没有帮助的信息;

4对某个问题提供若干解决方案,但不能判断最佳解决方案;

5 学生不能对学习材料和自身学习成绩进行评价

在总学习内容信息量中,高水平认知的信息比较少
深学习 1 在做解释、提出建议和判断时能联系事实、自己的  思想和概念,并能从收集到的资料中创造新资料;2 能提出一个或多个解决方案,知道自己设想条件或解决方案的有优缺点,判断最佳方案;

3 能以例子的方式阐述自己的理解

4 善于提出问题,并以更广的角度处理问题;

5 学生能对学习材料和自身学习成绩进行评价

在总学习内容信息量中,高水平认知的信息比较多

通过以上对比,可管窥浅学习一般特点,其在远程学习中具体表现为以下三方面:

1 浅阅读

阅读是最基本的学习方式。然而,网络阅读可能不象书本阅读进入的快、投入的深,不仅抓不住课程重点而且容易疲劳,极易走神,这便是远程学习的最大缺陷——浅阅读。浅阅读的最大特点是“浅”,即浮光掠影般只停留在文字的言语层面,不带任何思考。因此以浅阅读方式阅读时有碍知识的获取。

2 浅自主

传统课堂的自主学习仅突出了学习者的自主性和主动性,而网络下的自主学习能力还包括高独立性、强自制性、分离性,对课程的控制性以及自定目标等。但学习者往往不适应这种分离的特性,对课程的控制不强,在学习时自制性不高,独立性不强,不能发现问题,对所学材料不能作出正确的评价,也无法评估自己学习的效果,甚至不能独立的完成学习。

3 浅协作

基于网络的协作学习是指利用计算机网络以及多媒体等技术构建协作学习环境,使教师与学生、学生与学生针对同一专题内容彼此进行交流、讨论和合作,以达到对内容的深刻理解和掌握的过程。1目前网络学习中的浅协助主要表现为:组内活动不同步;主题讨论不积极;异步教学系统下师生、生生交互协作不到位。

  远程学习中浅学习问题原因透析

远程学习中,教师、学生、课程是远程学习的关键要素,因而浅学习问题产生原因脱离不了模式中的要素。以下从这三个要素分析问题产生原因:

1 教师原因

教师在远程学习中仍扮演着极为重要的角色,但在远程课堂上,教师存在以下问题可能导致学生浅学习问题的产生:

(1)思想观念错误  有的教师认为网上课程就是脱离教师的高效学习,所以在课程学习中要么缺席,要么放任学生自由学习,使网课的学习缺乏指导和组织。殊不知,教师的在场对维持学生特别是中小学学生学习动机水平的重要性。

(2)思维定势  未完全接受这种新的教学方式,在远程教学中,很多教师摆脱不了传统课堂教学习惯:自己讲的多,学生做的少,这使得面对电脑的学生动手的机会少,参与不了或浅参与课程。

2 学生原因

(1)学习动机过弱或过强  据研究内部动机与学业成就存在正相关,外部动机则与学业成就存在负相关。2因此内部动机过弱或外部动机过强,都不利于学习的保持。一方面,由于学习者本身内部动机水平不高,所以参与远程学习的积极性不强;另一方面科技进步带来了媒介信息的批量生产,现代社会发展又加快了生活节奏, 生活压力的加重以及对于信息需求量的猛增使得外部动机显著增加,面对庞大的信息量人们无暇一一应对, 只能根据需求进行筛选或做“ 浅学习” 式了解。

(2)学习习惯尚未改观  近年来远程学习课程层出不穷,但网络学习作为一种新的学习方式时间并不长,而对于长期以书本等纸质媒体为主要媒体的学生(特别是大龄学习者)对电子媒体有时表现的不适应,阅读、参与协作等对于他们而言并“不真实”,这都造成了他们的“浅学习”。

(3)信息技术能力比较差  作为农村或者偏远欠发达地区的学生,信息技术对于他们而言并不熟悉,虽然有远程网课,但也是“公开课”场合的特权,由于操作技能缺乏,仍然不能很好的参与网课学习。

3 学习课程本身原因

(1)界面设计不科学  教育网站界面的设计理论不系统,在色彩搭配、版面布局等方面没有可参考原则,照搬商业网站(不具备教育性,色彩混乱,功利性高),都导致了学习易疲劳、学习效率低;

(2)学习过程缺乏情感交流  一些课程不能把界面设计的教育性、科学性和艺术性很好的结合,缺乏情感,使学生只感受到与机器的交流,提高不了学习的积极性;

(3)课程内容的交互性不强  内容仍是书本搬家,页面缺乏交互,信息传输呈现传统的单向传播的特点,这使得学生在学习中表现的很被动,基本上点击“下一页”就能完成所有课程的学习,缺乏互动;

(4)课程的反馈、评价欠缺  缺乏反馈和评价环节使学生的学习效果无法得到正确和及时的反馈;使得一些学生特别是需要激励的中小学生学习的积极性提不起来,也无法正确评估自己的学习;

(5)课程内容组织的非良构,导航不明确  很多课程都在一个“简洁”的平面内完成,白底黑字,最多有几张图片或视频,忽视了学习氛围的构造,缺乏科学规划的内容组织和导航使得学生容易迷航,学习效率降低。

  浅学习问题的解决策略探究

在远程学习过程中,要想很大程度的改变学生浅学习问题,教师和远程课程是首要因素,具体策略如下:

1 人性化的设计

课程界面是学生和课程内容”交流”的平台,因此课程界面设计要遵循以学生为主的人性化设计原则:界面不仅要美观,而且要满足学生的生理和心理需要。设计时尽量做到使界面自定义:例如界面布局自定义——学生可以依据自己的喜好用鼠标拖拽就能改变课程界面的布局;界面颜色自定义——可以选择喜欢的颜色作为界面的主色调,随意搭配出自己想要界面风格,虽然色彩本身不具备情感性,但不同色彩与人的视觉作用会产生情感效果,创造愉悦的学习环境;鼠标形状自定义——学生可以通过下拉列表选择喜欢的鼠标形状,一改课程呆板的机器特性。

2 情感化设计

学生学习是求知的过程,同时也是身心发展过程,因而要将情感融入课程,凸显其 “性格”,使之有生命,充分发挥情感因素的效能:一方面将可供学生选择的色彩从心理学层面加以科学化、艺术化,使其为学习者带来积极的正面心理。另一方面可将“虚拟”场景迁移进远程课程,使课程环境虚拟化如图1,加强学生的沉浸感,产生身临其境的课堂教学感。

111

图13  伴有辅助教师的“情感化”远程课程

3 游戏积分制引进评价机制

网络游戏吸引众多玩家不只是因为其漂亮的、人性化的界面和生动的游戏情节,还在于它将积分用作评价体系。对教学而言,“积分制”就是通过采用一些特定规则,来贯彻“既要竞争又要协作”的教育理念因此将积分制用于教学评价不仅能更人性化的完善评价体系,调动学习者的积极性,还有利于协作型学习团队的建设。

4 课程中的问题设计

问题能激发学生的学习欲望,集中学生的注意力和深度信息处理能力,迫使学生评测自己对材料的掌握程度并提供重新复习材料的机会。提问在深学习中是一种鼓励,并促使学生成长为一名独立的学习者。研究表明教师提问问题的水平和学生成就之间成正比例关系[4,因此课程中必须存在“问题”,不论这些问题是来自教师还是学生都能促进学习材料的深加工。问题的设计要依据课程目标、学习者特性以及实际需要等因素,具体设计可依据表2进行。

表2 问题分类

依据 类型
所需资料的类型 低层次事实性问题
高层次问题
认知水平(思考水平)5 有意识回忆的问题
会聚问题(可预见的)
分支问题(注:具有创意和想象力的问题)
评价问题(判断、评价和选择)
对象的性质 个人独立完成型
小组合作型
教师提问层级 讨论阶段的低层次重复性问题
深启发性的高层次问题

5 交互“实时”与“异步”相结合

远程教育交互是指在师生相分离的环境特性下师生间的互动行为,这种分离的特性所产生的交互距离不易于师生的交流,因此远程学习中的交互是使学习顺利进行的必要因素,主要有同步交互和异步交互两种模式,Candace Chou的研究指出:异步交互中, 8%的讨论内容是关于社会情感, 92%是关于学习任务;而同步交流中, 33%的内容是关于社会情感, 67%是关于学习任务。6可见, 异步交互对学习任务的影响显著高于同步交互, 而同步交互对建立社会关系的作用明显高于异步交互,又是学习资料同化所需要的深学习过程存在的前提。因此实施远程教学时要将同步、异步交互结合,才能达到教学目的,同步系统样例如图2:

222

图27结合“同步交互”的远程课程

6 教师、长者的亲临及角色转变

附属内驱力是指为了保持长者们或集体的赞许或认可,表现出要把工作做好的一种需要。这种动机虽在年幼儿童的学习活动中比较突出,但是在各个年龄皆有涉及。教师的亲临不仅为学生提供了情感支持并鼓励学生对交互的参与,而且能使高思维能力的学生的学习动力得到维持,因此远程学习环境下学习者需要一个支持和鼓励他们的教师或长者的在场。同时这些在场者必须改变思想观念,成为学习的组织者和管理者和合作者。

结语

通过以上对浅学习问题解决策略的探究,一定程度上解决了远程网络学习中的浅学习问题,但是远程学习仍然有自身无法克服的限度,并非精心设计的远程课程都能为所有对象所使用:例如就使用对象来说,年龄较小、认知力、自制力较差的学生就不适合进行网络学习,因此在解决浅学习问题的同时,我们仍然要注意将传统学习方式与远程学习方式在一定程度上进行结合,以获得教学的最佳效率。

 

参考文献:

[1]王坦.协作学习——原理与策略[M].北京:学苑出版社,2001.10.

[2]梁海梅, 郭德俊等成就目标对青少年成就动机和学业成就影响的研究, 心理科学,1998,(5):332—335

[3][7]杨雪. 吉林省教育科学“十一五”规划重点研究课题(ZC0096)

[4]Redfield, D. L., &Waldman-Rousseau, E. W. (1981). A meta-analysis of experimental research on teacher questioning behavior. Review ofEducational Research, 51, 237–245.

[5]Baruch Offir , Yossi Lev, Rachel Bezalel .Surface and deep learning processes in distance education: Synchronous versus asynchronous systems.Computers & Education,Volume 51.2008

[6]C. Candace Chou , A Comparative Content Analysis of StudentInteraction in Synchronous and Asynchronous Learning Networks. Journal of Interactive Learning Research, 12(2/3), 170- 188. 2001

 

 

*基金项目:吉林省教育科学“十一五”规划重点研究课题(ZC0096);吉林大学教学研究与实践项目资助