[アイテム19]継承を考慮して設計し記録する.そうでなければ継承は禁止されます
12074 ワード
設計と記録の継承
メソッドを再定義する場合は、何が起こったのかを正確に特定するために、ドキュメントに残しておく必要があります.
継承クラスは、内部で再定義可能なメソッドをどのように使用(自己使用)するかをドキュメントとして保持する必要があります.
クラスのAPIで開示されているメソッドからクラス独自の別のメソッドを呼び出すこともできますが、呼び出されたメソッドがちょうど機能メソッドを再定義している場合は、そのメソッドを呼び出すAPIの説明に明記する必要があります.
呼び出し順序および各呼び出し結果が後続の処理に及ぼす影響も含まなければならない(
public
およびprotected
メソッドのすべてのメソッドが再定義され、final
ではない)再定義可能なメソッドを呼び出すことができるすべての状況をドキュメントとして保持する必要があります.
クラスが安全に継承されていることを確認するには(継承されていない場合は説明すべきではない)、内部実装方法を説明する必要があります.
内部メカニズムをドキュメントとして保持することは、設計のすべてを継承するものではありません.
効率的なサブクラスを簡単に作成するには、クラスの内部操作中にクラスに挿入できるhook(hook)をフィルタリングし、
protected
の方法で公開する必要がある場合があります.protected
フィールド継承するクラスを設計する際には、実際のサブクラスを作成し、実験によって予測し、方法を
protected
に暴露する必要があります.各
protected
の方法は内部実施に適しているため、これらの方法をできるだけ少なく使用するとともに、露出が少なすぎることによる継承優位性の回避にも注意しなければならない.継承クラスをテストする唯一の方法は、独自のサブクラスを作成することです.
必要な
protected
メンバーを逃した場合、サブクラスの作成時に空の場所が表示されます.protected
を全く使用しないメンバーは、複数のサブクラスを作成する前にprivate
である可能性が高い.広く使用されているクラスを継承するように設計されている場合、
protected
メソッドおよびフィールドを実装する過程で、決定は常に責任を負います.継承のために設計されたクラスは、デプロイ前にサブクラスを作成して検証する必要があります.
Hook(Hook)
Hook(Hook)は、抽象クラスでアクションを実行したりデフォルトの動作を定義したりするのではなく、サブクラスで上書きできる定義です.
これを選んで、本の例から見ると
public class Super {
// 잘못된 예 - 생성자가 재정의 가능 메서드를 호출함
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
public final class Sub extends Super {
// 초기화되지 않은 final 필드. 생성자에서 초기화한다.
private final Instant instant;
Sub() {
instant = Instant.now();
}
// 재정의 가능 메서드. 상위 클래스의 생성자가 호출한다.
@Override public void overrideMe() {
System.out.println(instant);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}
}
ここで、親クラスの作成者も呼び出されたときにoverrideMe
を呼び出します.この方法は再定義によって埋め込むことができますが、それ以前は何もしていなかった方法です.上記の場合は、内部操作中にhookが挿入される可能性があることを意味し、実際には上記の例
overrideMe
の方法は何もしないので、親の構造関数はnullとして出力されることがよく理解され、protected
を処理することを意味する継承を許可するクラスが守らなければならない制約
継承クラスの作成者は、再定義可能なメソッドを直接または間接的に呼び出すべきではありません.そうしないと、プログラムがエラーになります.
親クラスの作成者は、子クラスの作成者よりも先に実行されるため、子クラスで再定義されたメソッドは、子クラスの作成者よりも前に呼び出されます.
再定義されたメソッドが、サブクラスの作成者で初期化された値に依存する場合、予想通りに動作しません.
例は以下の通りです
public class Super {
// 잘못된 예 - 생성자가 재정의 가능 메서드를 호출함
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
public final class Sub extends Super {
// 초기화되지 않은 final 필드. 생성자에서 초기화한다.
private final Instant instant;
Sub() {
instant = Instant.now();
}
// 재정의 가능 메서드. 상위 클래스의 생성자가 호출한다.
@Override public void overrideMe() {
System.out.println(instant);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}
}
上記の親クラスの作成者は、子クラスの作成者がインスタンスフィールドを初期化する前に
overrideMe
を呼び出し、nullを最初に出力するprintln
はnull入力も受け付けているのでNullPointerExceptionは放出されませんprivate
、final
、static
メソッドは再定義できないので、安心してジェネレータから呼び出すことができるCloneable
およびSerializable
インタフェースは、継承性設計の難易度をさらに高め、継承可能なクラスを設計することは通常、このクラスを拡張したいプログラマーに大きな負担をもたらす悪いアイデアである.clone
メソッドとreadObject
メソッドの効果はジェネレータと似ています(新しいオブジェクトを作成)継承クラスで
Cloneable
およびSerializable
を実装するように指定する必要がある場合は、それらを実装する際に従う制約もコンストラクション関数と同様であることに注意してください.clone
およびreadObject
は、再定義可能なメソッドを直接または間接的に呼び出すことはできません.readObject
では、サブクラスのステータスは、複数のドメインにシーケンス化されるまで、再定義されたメソッドから呼び出されます.clone
の場合、サブクラスのclone
メソッドは、レプリカのステータス(正しいステータス)を変更する前に再定義されたメソッドを呼び出す.どちらの場合もプログラムエラーが発生します
clone
も元のオブジェクトを損なうSerializable
を実装する継承クラスがreadResolve
またはwriteReplace
メソッドを有する場合、これらのメソッドはprivate
ではなくprotected
として宣言されるべきである.一般的なクラス
これらのクラスは
final
ではなく、継承のために設計または記録されたものではありません.これは,一定に保つとクラスが変化するたびにサブクラスにエラー動作が発生するためである.
この場合、継承として設計されていないクラスの継承を禁止することが最善の方法です.
継承を禁止する方法
宣言クラスは
final
ですすべてのジェネレータを
private
またはpackage-private
と宣言し、public
の静的ファクトリを作成する方法アイテム18に記載されたRapperクラスモードも、機能を強化する際に継承を代替するより良い代替案である.
どうしてもやるなら。
クラスで再定義可能なメソッドを無効にし、この事実をドキュメントとして保持します.
再定義可能なメソッドを呼び出す磁気コードを完全に削除
クラスの動作を維持しながら再定義可能なメソッドを使用するコードを削除する機械的メソッドは、まず、各再定義可能なメソッドがその本文コードを
private
ヘルプメソッドに移動し、このヘルプメソッドを呼び出すように変更することです.その後、呼び出し再定義可能なメソッドの他のすべてのコードを変更して、このアシスタントメソッドを直接呼び出すことができます.
Reference
この問題について([アイテム19]継承を考慮して設計し記録する.そうでなければ継承は禁止されます), 我々は、より多くの情報をここで見つけました https://velog.io/@gang_shik/아이템-19-상속을-고려해-설계하고-문서화하라.-그러지-않았다면-상속을-금지하라テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol