第15条:クラスとメンバーのアクセシビリティを作っ最小化
-モジュールが他のモジュールの内部の仕組みを知っている必要はありません情報隠蔽:ソフトウェア設計の基本原則の一つ。
この原則を実装すると、各クラスのメンバーかどうかを外部からアクセスできるようにすることを可能な限り、あること、非常に簡単です。
パッケージレベルのプライベート(パッケージプライベート)、公共(パブリック):クラスの先頭には、アクセスの唯一の2つのレベルがあります。
クラスは、プライベートパケットレベルとして宣言された場合、それは将来のバージョンでは、外部インターフェイスを自由に変更または削除することができるのではなく、実際にはこのパッケージの実装の一部です。publicとして宣言した場合、我々は常にその互換性を維持するために、それをサポートする必要があります。
- パブリッククラスインスタンスフィールドは、公開してはなりません。公的に設定した場合、インスタンスフィールドは、このフィールドの容量に制限することを記憶された値を与えるために、非最終又は最終的なオブジェクト参照変数を指す場合。
- クラスはパブリックドメインの配列を持っているか、確定申告のためにしてみましょうフィールドの配列復帰方法が間違っている、クライアントは、配列の内容を変更することができます。この問題を解決する方法は2つあります。
国民は民間、公共の配列は不変のリストを増加となり
private static final String[] PRIVATE_VALUES = {...}
public static final List<String> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES);
アレイは、プライベートとなり、公共のaddメソッドは、配列のコピーを返します。
private static final String[] PRIVATE_VALUES = {...}
public static final String[] values(){
return PRIVATE_VALUES.clone();
}
第16条:代わりに、パブリックドメインの公開授業でのアクセス方法を使用するには
パブリッククラスの可変ドメインを公開すべきではありません。そこに問題はまだですが、不変クラスはパブリックドメインを公開せますが、制約を追加することにより、被害を減らすことができます。時々、このクラスは、変数または不変であるかどうか、プライベートまたはプライベートパケットレベルのネストされたクラスの露光領域を必要とされています。
第17条:増加は、ばらつきを最小限に抑えます
不変クラス次の5つの原則:
- 任意の方法の修正を(また、セッターとしても知られる)オブジェクトの状態を提供しません
- カテゴリーが拡張されないことを確認してください
- すべてのフィールドがの最後の文です
- すべてのフィールドはprivateとして宣言されています
- 任意の変数コンポーネントの排他的アクセスを確認してください。オブジェクトを指し示す変数とクラスフィールド場合は、このようなクライアントの使用は、これらのオブジェクトへの参照を取得することはできません、オブジェクトはこのドメインを初期化するために、クライアントによって設けられていない使用しないことを確認する必要があります。
不変の機能をオブジェクト:
- 状態不変オブジェクトの時に1つだけの状態の、すなわち創造比較的簡単です
- スレッドセーフな自然の中で不変オブジェクト上で、同期が自由に共有することができる必要はありません。
- 不変オブジェクトは他のオブジェクトのメンバの数を提供します
- 短所:各異なる値の個別のオブジェクトを必要とし、ビットのBigInteger万人が存在する場合、しかしオリジナルと、百万の上にありますが、新しいBigIntegerのインスタンスを作成する必要があり、その低いを変更したいです唯一異なるオブジェクト。複雑な操作は、クライアント不変クラスに実行すべきかどうかを予測できない(この問題BigSetを解決するために使用することができ、一定時間内に単一ビットの状態を変更することができる)、公衆提供することができる可変整合クラスようなストリング]のように、 - >のStringBuilder
二つの方法で構成された不変クラス、1つの方法は、最終的なクラスになることであり、他の方法は、クラスのコンストラクタがプライベートでなり、その代わりにパブリックコンストラクタのパブリック静的メソッドを提供させることです。
public class Point{
private final int x;
private final int y;
private Point(int x,int y){
this.x = x;
this.y = y;
}
public static Point valueOf(int x,int y){
return new Point(x,y);
}
}
要約:
- クラスクラス変数にする正当な理由がある場合を除き、それ以外の場合は不変でなければなりません
- クラスは不変行うことができない場合は、その可能な変動を制限します
- コンストラクタは完全に初期化オブジェクトを作成し、すべての制約を設定する必要があります
第18条:相続オーバーコンポジット優先
親の変更、サブクラスは親クラスの変化が続かなければなりませんが更新された場合、別の継承とメソッド呼び出しは、ブレークのカプセル化を継承している親クラスを拡張し、設計することである場合を除き、カジュアル継承されたクラスが明確に文書化されず、あなたは期待どおりの結果を得ることはできません。
例:完全にあまりにも多くの要素を追加するには、創業以来、いくつかのプログラムを知りたいセットのコレクション。
作成InstrumentedHashSetクラス
class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0;
public InstrumentedHashSet() {
}
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
mainメソッド
public static void main(String[] args) {
InstrumentedHashSet instrumentedHashSet = new InstrumentedHashSet();
instrumentedHashSet.addAll(Arrays.asList("a", "b", "c"));
System.out.println(instrumentedHashSet.getAddCount());
}
ランの出力は6であるが、我々は3つのしか要素を追加のaddAll HashSetのメソッドが実行される方法の追加に基づいているため、所望の結果は、3であり、プログラムの最初の呼び出しのaddAll方法addCountは、次いで三回サイクル呼び出し方法増分を追加し、3増加しました1。
あなたがこの方法を修正したい場合は、我々は結果の精度を確保することができますがaddAllメソッドをカバーすることができますが、我々はそのルールとは、Javaで保証未来をしないという事実「に基づいて実施される方法を追加するためのaddAll道のHashSetの」に基づいありません我々はInstrumentedHashSetは壊れやすい、貧しい堅牢性であるうちに設計されてリリースは、変更されません。
もう一つの理由は、そのサブクラス壊れやすい:親クラスは新しいメソッドを追加することもできます。要素を挿入する機能の前提条件のセットが満たされなければならないされていますが、実際に基づいた安全プログラムがあると仮定すると。我々は、すべての補間方法は、添加元素の条件を満足するときことを保証することである覆うようにサブカテゴリを設定することができます。親は、将来のリリースで新しい挿入方法を追加しない場合、我々はそれがセキュリティを確保することができますが、親クラスが新しい挿入方法を追加した場合、プログラムが挿入されます。この方法で条件を満たしていないんカバーのサブクラスではありませんデータなので、セキュリティ侵害がありました。
「コンポジット」何のパターンは、既存のクラスを拡張していないが、既存のクラスのインスタンスに新しいクラスのプライベートドメインを追加することを、すべての問題は、前述したこの設計手段を解決することができます。コンポーネントの新しいクラスに既存のクラスを、既存の方法クラスの新しいクラスのすべてのメソッドは、このようにして達成呼び出すことによって含めることができますも前方に呼ばれています。
InstrumentedHashSetクラスにちょうど例えば、複合的に使用しました:
class InstrumentedHashSet2<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedHashSet2(Set<E> set) {
super(set);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> set) {
this.s = set;
}
@Override
public int size() {
return s.size();
}
@Override
public boolean isEmpty() {
return s.isEmpty();
}
@Override
public boolean contains(Object o) {
return s.contains(o);
}
@Override
public Iterator<E> iterator() {
return s.iterator();
}
@Override
public Object[] toArray() {
return s.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return s.toArray(a);
}
@Override
public boolean add(E e) {
return s.add(e);
}
@Override
public boolean remove(Object o) {
return s.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return s.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return s.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return s.removeAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}
@Override
public void clear() {
s.clear();
}
}
別の設定された設定にInstrumentedHashSet2の対応がカウント機能に変換され、コンストラクタセットのいずれかと互換性がある、このモードはまた、「モードによって修飾された」と呼ばれる、InstrumentedHashSet2例に包装セット、InstrumentedHashSet2のでまた、パッケージとして知られています。
パッケージングは、補正フレームには適していないことに注意してくださいは、コールバックオブジェクト内のフレームは、それ自体が渡されているように、その外側包装を知らないオブジェクトをパッケージ化することが、他のオブジェクトに自分自身への参照を渡します参照。
第19条:どちらかが継承され、設計文書を提供し、または継承を禁止します
特別に継承するクラスを設計し、各方法の影響の明確な記述で覆われては、文書にもたらします。オーダーと呼ばれるものをメソッド呼び出しは、各呼び出しの結果は、その後の効果をどのように処理するかである場合には、各パブリックまたは保護された方法について、説明する方法をカバーすることができます。
コンストラクタが呼び出し方法をカバーすることができない必要がありますコンストラクタサブクラスの前に、親クラスのコンストラクタの実行を、サブクラスのバージョンをカバーする方法は、サブクラスのコンストラクタの実行前に実行されます。
例えば:
class Super {
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
final class Sub extends Super {
private final Instant instant;
Sub() {
instant = Instant.now();
}
@Override
public void overrideMe() {
System.out.println(instant);
}
}
主な方法:
public static void main(String[] args) {
new Sub().overrideMe();
}
出力:
ヌル
2020-03-22T08:43:29.201Z
最初の行は、それがnullである、最初の親クラスのコンストラクタを呼び出し、その後、サブクラスのコンストラクタが実行されていない、その場合にはoverrideMeサブクラスを、呼ぶプログラムは、インスタントが初期化されていないヌルを印刷します。
プライベートコンストラクタメソッドによって呼び出され、最終メソッドまたは静的メソッドは、これらのメソッドがカバーされていない、安全です。