java中谈多线程的话必然要牵扯到java.lang.Runnable和java.lang.Thread。从这两个中任选一个都能实现想要的功能。但是二者的意义可谓天壤之别。
Thread的使用比较久远,从面向对象的角度来看,这种方式通过类型的继承来实现代码重用,而Runnable则是比较新的,通过组合来实现代码重用。
与继承相比,组合更接近于“黑箱”式的代码重用。例如,当我们扩展了Thread的run方法,那我们要不要调用基类的run方法以保证一切都运行正常呢?如果要的话,我们是在自己的执行代码之前、之后还是之中来调用呢?开发人员在使用这样的模块时,必须知道模块的内部行为,这肯定是破坏了面向对象中封装的原则。
继承还会带来另外一个问题,缺乏面对需求变更时的灵活性。例如,库开发者想对库作些改进,他们会发现库使用者依赖于某些成员变量的初始化,或者库使用者正通过改变一些成员变量的值来影响基类的行为。这都使得双方的开发不够敏捷。那Thread来说,例如开发了Thread的一个子类ThreadSubclass,当Thread类被更新后,ThreadSubclass类要想重用新基类的代码,就必须作相应变化,这就会破坏原有代码。
继承的第三个麻烦就是,当一个新需求出现时,我们就需要一个新的子类。
组合可以避免上面的问题。
Thread的使用比较久远,从面向对象的角度来看,这种方式通过类型的继承来实现代码重用,而Runnable则是比较新的,通过组合来实现代码重用。
与继承相比,组合更接近于“黑箱”式的代码重用。例如,当我们扩展了Thread的run方法,那我们要不要调用基类的run方法以保证一切都运行正常呢?如果要的话,我们是在自己的执行代码之前、之后还是之中来调用呢?开发人员在使用这样的模块时,必须知道模块的内部行为,这肯定是破坏了面向对象中封装的原则。
继承还会带来另外一个问题,缺乏面对需求变更时的灵活性。例如,库开发者想对库作些改进,他们会发现库使用者依赖于某些成员变量的初始化,或者库使用者正通过改变一些成员变量的值来影响基类的行为。这都使得双方的开发不够敏捷。那Thread来说,例如开发了Thread的一个子类ThreadSubclass,当Thread类被更新后,ThreadSubclass类要想重用新基类的代码,就必须作相应变化,这就会破坏原有代码。
继承的第三个麻烦就是,当一个新需求出现时,我们就需要一个新的子类。
组合可以避免上面的问题。