Linee guida sintetiche per il flattening dell'ereditarietà

Seguire diverse linee guida concise per appiattire l'eredità.

  1. Per ciascuna classe nella gerarchia di tipi, ridenominare la classe da [Name] a [Name]Impl.
  2. Estrarre un'interfaccia da ciascuna di queste classi, dando a ciascuna i nomi originali utilizzati per quelle classi. Includere tutti i metodi pubblici e i campi statici costanti in ogni interfaccia.
  3. Creare una gerarchia dei tipi attraverso le interfacce estratte che sono identiche alla serie di classi originale. Ad esempio, se BImpl estende AImpl, B estende A.
  4. Copiare metodi e campi da ogni classe principale agli elementi secondari diretti. Ripetere ricorsivamente in tutta la gerarchia dei tipi.
  5. Risolvere i conflitti di denominazione per i metodi sostituiti o i campi shadow ridenominando i metodi e i campi copiati dalla classe principale.
  6. Sostituire le chiamate a super.method() richiamando direttamente i metodi che sono stati raggruppati nella classe appiattita.
  7. Risolvere gli errori dai super-costruttori copiati analogamente ai passi 5 e 6.
  8. Rimuovere le estensioni dei tipi dalle classi di implementazione (ovvero, se BImpl estende AImpl, rimuove extends AImpl) per eliminare l'eredità.
  9. (Facoltativo) Eliminare il codice inattivo, le classi astratte o qualsiasi classe o metodo a cui non si fa più riferimento.

Come semplice esempio di appiattimento dell'eredità, considerare la seguente gerarchia di classi.

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";
  }
}

Applicando queste linee guida per l'appiattimento dell'eredità, si produce questa serie di interfacce:

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();
}

Inoltre, si fornisce una classe di implementazione per ognuna di queste interfacce:

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";
  }
}

Di conseguenza, la gerarchia dei tipi originale viene mantenuta e l'ereditarietà viene eliminata. Questa architettura consente la creazione del codice per le classi refactoring da distribuire tra le partizioni.

In alternativa alla copia di metodi e campi dalla classe principale, il flattening può ancora essere raggiunto tramite delega modificando la relazione delle classi da estensione a contenimento. Ad esempio, le classi BluejayImpl e CardinalImpl vengono modificate in modo da avere un'istanza di BirdImpl e delegano le chiamate ai relativi metodi.

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";
  }
}