Directrices concisas para aplanar la herencia

Siga varias directrices concisas para aplanar la herencia.

  1. Para cada clase de la jerarquía de tipos, cambie el nombre de la clase de [Name] a [Name]Impl.
  2. Extraiga una interfaz de cada una de estas clases, proporcionando cada uno de los nombres originales utilizados para dichas clases. Incluya todos los métodos públicos y los campos estáticos constantes en cada interfaz.
  3. Cree una jerarquía de tipos a través de las interfaces extraídas que sea idéntica al conjunto original de clases. Por ejemplo, si BImpl amplía AImpl, B amplía A.
  4. Copie los métodos y campos de cada clase padre a sus hijos inmediatos. Repita recursivamente a lo largo de toda la jerarquía de tipos.
  5. Resuelva los conflictos de denominación para métodos alterados temporalmente o campos sombreados cambiando el nombre de los métodos y campos que se han copiado de la clase padre.
  6. Sustituya las llamadas a super.method() llamando directamente a los métodos que se han convertido en la clase plana.
  7. Resuelva los errores de los superconstructores copiados de forma similar a los pasos 5 y 6.
  8. Elimine las extensiones de tipo de las clases de implementación (es decir, si BImpl amplía AImpl, elimine extends AImpl) para eliminar la herencia.
  9. (Opcional) Suprima el código inactivo, las clases abstractas o cualquier clase o método al que ya no se haga referencia.

Como ejemplo sencillo de aplanar la herencia, tenga en cuenta la siguiente jerarquía de clases.

public class Bird {
  private String description;
  public void setDecription(String description) {
    this.description = description;
  }
  public String getDescription() {
    return description;
  }
}

public class Bluejay extends Bird {
  public String getName() {
    return "Bluejay";
  }
}

public class Cardinal extends Bird {
  public String getName() {
    return "Cardinal";
  }
}

Al aplicar estas directrices para aplanar la herencia, se genera este conjunto de interfaces:

public interface Bird {
  public void setDecription(String description);
  public String getDescription();
}

public interface Bluejay extends Bird {
  public String getName();
}
public interface Cardinal extends Bird {
  public String getName();
}

Además, se genera una clase de implementación para cada una de estas interfaces:

public class BirdImpl implements Bird {
  private String description;
  public void setDecription(String description) {
    this.description = description;
  }
  public String getDescription() {
    return description;
  }
}

public class BluejayImpl implements Bluejay {
  private String description;
  public void setDecription(String description) {
    this.description = description;
  }
  public String getDescription() {
    return description;
  }
  public String getName() {
    return "Bluejay";
  }
}

public class CardinalImpl implements Cardinal {
  private String description;
  public void setDecription(String description) {
    this.description = description;
  }
  public String getDescription() {
    return description;
  }
  public String getName() {
    return "Cardinal";
  }
}

Como resultado, se mantiene la jerarquía de tipos original y se elimina la herencia. Esta arquitectura permite que la generación de código para las clases refactorizadas se distribuya entre particiones.

Como alternativa a la copia de métodos y campos de la clase padre, el aplanado todavía se puede lograr mediante delegación cambiando la relación de las clases de extensión a contención. Por ejemplo, las clases BluejayImpl y CardinalImpl se cambiarían para tener una instancia de BirdImpl y, a continuación, delegarían las llamadas a sus métodos.

public class BluejayImpl implements Bluejay {
  private Bird bird = new BirdImpl();
  public void setDecription(String description) {
    bird.setDecription(description);
  }
  public String getDescription() {
    return bird.getDescription();
  }
  public String getName() {
    return "Bluejay";
  }
}

public class CardinalImpl implements Cardinal {
  private Bird bird = new BirdImpl();
  public void setDecription(String description) {
    bird.setDecription(description);
  }
  public String getDescription() {
    return bird.getDescription();
  }
  public String getName() {
    return "Cardinal";
  }
}