継承のフラット化に関する簡潔なガイドライン
継承をフラット化するには、いくつかの簡潔なガイドラインに従ってください。
- タイプ階層内のクラスごとに、クラスの名前を
[Name]
から[Name]Impl
に変更します。 - これらの各クラスからインターフェースを抽出し、それらのクラスに使用される元の名前をそれぞれ付けます。 すべての public メソッドおよび constant フィールドを各インターフェースに組み込みます。
- 元のクラス・セットと同一の抽出インターフェースを使用して、タイプ階層を構築します。 例えば、
BImpl
extendsAImpl
の場合、B
はA
を拡張します。 - 各親クラスのメソッドおよびフィールドを、その直接の子にコピーします。 タイプ階層全体で再帰的に繰り返します。
- 親クラスからコピーされたメソッドおよびフィールドの名前を変更することにより、オーバーライドされたメソッドまたはシャドーイングされたフィールドの名前の競合を解決します。
- フラット化されたクラスに組み込まれたメソッドを直接呼び出すことにより、
super.method()
の呼び出しを置き換えます。 - ステップ 5 および 6と同様に、コピーされたスーパー・コンストラクターのエラーを解決します。
- 継承を除去するには、実装クラスから型拡張を除去します (つまり、
BImpl
がAImpl
を拡張する場合は、extends AImpl
を除去します)。 - (オプション) デッド・コード、抽象クラス、または参照されなくなったクラスまたはメソッドを削除します。
継承のフラット化の簡単な例として、以下のクラス階層について考えてみます。
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";
}
}
継承をフラット化するために以下のガイドラインを適用することにより、この一連のインターフェースが生成されます。
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();
}
さらに、以下の各インターフェースの実装クラスを生成します。
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";
}
}
その結果、元のタイプ階層が維持され、継承が除去されます。 このアーキテクチャーにより、リファクタリングされたクラスのコード生成をパーティション間で分散させることができます。
親クラスのメソッドおよびフィールドをコピーする代わりに、拡張から包含へクラスの関係を変更することによって、委任を使用してフラット化を実現することができます。 例えば、 BluejayImpl
クラスと CardinalImpl
クラスは、 BirdImpl
のインスタンスを持つように変更され、そのメソッドへの呼び出しを委任します。
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";
}
}