代码检查(3)

3. 父类引用子类

这是说一个父类保存了其子类的引用,返回具体的其子类类型,以具体的其子类类型做为方法的参数,或者在方法中以本地变量的方式使用、初始化了具体的其子类实例。一句话,父类的任何地方都不应该出现“以其子类型”定义的静态变量、实例变量和本地变量(Local variables)。

组合关系

父类组合(Composite)其子类的关系不能存在,因为这会导致运行时刻的递归初始化,产生StackOverflow错误,代码如下:

public class Base {
  Derived d = new Derived();

  // 或者在构造方法中调用new Derived();
  public Base() {
    d = new Derived();
  }
}

public class Derived extends Base {
}

只要存在父类组合其子类的关系,即要在父类初始化时刻同时初始化其子类,必定导致这样的问题,无论是在定义实例变量的时候初始化,还是在构造器中初始化。当然,即使实例变量定义为父类,如果实际初始化的是其子类,也会产生同样的错误。

聚合关系

父类聚合(Aggregate)其子类的关系也不应该存在。聚合关系和组合关系的不同就在于初始化过程,在聚合关系中,其子类的初始化不在父类初始化过程中,这不会导致递归问题。但这并不是说这样做就是好的。

直接以其子类型定义静态变量、实例变量,等于把抽象的父类绑定到了具体的子类型。这是对“继承关系是一种抽象关系”的破坏,它使更抽象的父类不再具有抽象的涵义。

在父类方法的实现中,以其子类型定义本地变量,和父类聚合其子类“效果”是一样的。

以父类型或者更抽象的类型来定义变量

在父类中,可以使用父类型或者更抽象的类型来定义变量。虽然不是一种很好的实践,但有时的确需要把其子类型的实例付值给这些变量。一般情况下,这样做的时候会选择动态加载子类的实例:

public abstract class Factory {
  private static Factory instance;

  public synchronized static Factory getInstance() {
    // 从配置文件中读取子类型名称
    String factoryName = readFromConfig();
    instance = Class.forName(factoryName).newInstance();
    ....
  }

  public Object createSomething();
}

即使如此,我个人的看法是,尽可能避免使用子类的实例。


subclass, reference, abstraction, composite, aggregate

你可能感兴趣的:(代码检查(3))