Effective Java読書ノート3(Java Tips.Day.3)


TIP 13.クラスとメンバーのアクセス性を最小化(カプセル化)
  • インスタンスドメインは、決して共通の
  • ではありません.
  • は、公有の静的final配列ドメインを有するべきではなく、またはそのようなドメインを返す方法である.代替として、公有配列をプライベートにし、公有の可変リストを追加することができます:
  • private static final Thing[] VALUES[] = {
        ...};
    public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

    または、配列のバックアップを返すための共通のメソッドを追加します.
    public static final Thing[] values(){
        return VALUES.clone();
    }

    TIP 14.パブリックドメインではなく共有クラスでアクセスメソッドを使用する
  • 共有クラスは、可変ドメイン
  • を常に露出すべきではない.
  • クラスがパケットプライベート、またはプライベートネストクラスである場合、そのデータドメインに本質的なエラーがないことを直接暴露する
  • TIP 15.可変性を最小化(可変クラス)
  • 可変クラス、すなわちインスタンスが変更できないクラス.すべての情報は、インスタンスの作成時に提供され、オブジェクトのライフサイクル全体にわたって固定されている必要があります.
  • オブジェクトの状態を変更する方法
  • は提供しないでください.
  • 保証クラスが
  • に拡張されない
  • すべてのドメインをfinalの
  • にする
  • すべてのドメインをプライベートな
  • にする
  • は、任意の可変コンポーネントへの反発アクセス
  • を保証する.
    functional方式を使用して新しい状態のオブジェクトを取得する
    可変クラスPointが2つのドメインxとyを持つことを考慮して、新しいPoint(x+deltaX,y+deltaY)を得る必要がある場合は、次のように処理する必要があります.
    pulibc Point add(float deltaX,float deltaY){
        return new Point(this.x+deltaX,this.y+deltaY);
    }

    可変クラスのいくつかの特性
  • スレッドは安全なので、自由に共有できます.
  • 頻繁に再利用:よく使用されるいくつかの値に対して、共通のstatic final定数を提供します.例えば、public static final Point PointZero = new Point(0.0,0.0)
  • は、cloneメソッド、またはコピーコンストラクタを可変クラスに提供する必要はありません.
  • 可変オブジェクトは、他のオブジェクトに大量のコンポーネントを提供する.
  • の唯一の欠点は、異なる値ごとに個別のオブジェクトが必要であり、パフォーマンスの問題を引き起こす可能性があることです.

  • 拡張防止
  • final修飾可変クラス
  • を用いる
  • コンストラクタがプライベートであるか、またはパケットがプライベートであるかは、その後、コンストラクタ
  • の代わりに静的ファクトリ法を用いる.
    可変クラスは使用できません
    一部のクラスは可変ではなく、できるだけ彼の可変性を制限しなければならない.ドメインを非finalにするのに十分な理由がない限り、各ドメインはfinalであるべきです.コンストラクタや静的工場のほかに、納得できる理由がない限り、初期化方法を追加しないでください.
    TIP 16.複合は継承より優先する
    継承の問題
    継承はパッケージ性を破り、サブクラスは親の特定の機能の実装の詳細に依存します.次のInstrumentedHashSetクラスを考慮してHashSetを拡張し、addCountで追加された要素の数を記録します.
    import java.util.*;  
    
    public class InstrumentedHashSet extends HashSet {  
        // The number of attempted element insertions  
        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;  
        }  
    
        public static void main(String[] args) {  
            InstrumentedHashSet s =  
                new InstrumentedHashSet();  
            s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));      
            System.out.println(s.getAddCount());  
        }  
    } 

    sには確かに3つの要素しか追加されていませんが、s.getAddCount()が返す値は6です.所望の呼び出しプロセスは、
    s.addAll(e1,e2,e3) -> 
        [
            addCount+=3; 
            s.super.addAll(e1,e2,e3) -> 
                [
                    s.super.add(e);
                ] * loop(3)
        ]

    しかし、サブクラスは親クラスのadd(e)メソッドを上書きするため、マルチステートメカニズムのため、サブクラスの実装が呼び出されます.実際の呼び出しプロセスは次のようになります.
    s.addAll(e1,e2,e3,....) -> 
        [
            addCount+=3; 
                [
                    s.add(e1,e2,e3) -> 
                        [
                            addCount ++; 
                            s.super.add(e);
                        ]
                ] * loop(3)
        ]

    サブクラスがaddAllメソッドを上書きしない場合、このクラスを修正できます.しかしながら、このようなクラスは正常に動作するが、その機能の正確性は、親クラスのaddAllがそのaddメソッド上で実現されることに依存する.このような「自己使用性」は、実現の詳細であり、約束ではない.親は、この実装が永遠に変わらないことを約束しません.変更が発生すると、子は正常に動作しない可能性があります.サブクラスがaddAllメソッドを上書きしてパラメータセットを巡回し、各要素に1回(上書きされた)addメソッドを呼び出すと、親クラスのaddAllがadd実装に依存しているかどうかにかかわらず、正しい結果が得られます.親クラスのaddAllインプリメンテーションは呼び出されないためです.これは実際にはスーパークラスを再実現する方法に相当する.親のプライベートドメインにアクセスできないため、実装できない方法がある可能性があります.
    複合の使用(composition)
    転送(forwarding):新しいクラスの各インスタンスメソッドは、含まれる既存のインスタンスに対応するメソッドを呼び出し、その結果を返すことができます.このプロセスを転送と呼びます.
    次に、上記の例を再実装します.
    import java.util.*;  
    //       
    public class ForwardingSet implements Set {  
        private final Set s;  
        public ForwardingSet(Set s) { this.s = s; }  
    
        public void clear()               { s.clear();            }  
        public boolean contains(Object o) { return s.contains(o); }  
        public boolean isEmpty()          { return s.isEmpty();   }  
        public int size()                 { return s.size();      }  
        public Iterator iterator()     { return s.iterator();  }  
        public boolean add(E e)           { return s.add(e);      }  
        public boolean remove(Object o)   { return s.remove(o);   }  
        public boolean containsAll(Collection> c)  
                                       { return s.containsAll(c); }  
        public boolean addAll(Collection extends E> c)  
                                       { return s.addAll(c);      }  
        public boolean removeAll(Collection> c)  
                                       { return s.removeAll(c);   }  
        public boolean retainAll(Collection> c)  
                                       { return s.retainAll(c);   }  
        public Object[] toArray()          { return s.toArray();  }  
        public  T[] toArray(T[] a)      { return s.toArray(a); }  
        @Override public boolean equals(Object o)  
                                           { return s.equals(o);  }  
        @Override public int hashCode()    { return s.hashCode(); }  
        @Override public String toString() { return s.toString(); }  
    }  
    
    
    import java.util.*;  
    //   
    public class InstrumentedSet<E> extends ForwardingSet<E> {
            
        private int addCount = 0;  
    
        public InstrumentedSet(Set s) {  
            super(s);  
        }  
    
        @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;  
        }  
    
        public static void main(String[] args) {  
            InstrumentedSet s =  
                new InstrumentedSet(new HashSet());  
            s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));      
            System.out.println(s.getAddCount());  
        }  
    }  

    これがまさにデコレーションモード(Decorator).
    is-aかhas-aかの問題
    不確実であれば、複合的な方法を考慮します.Javaプラットフォームクラスライブラリには、この原則に違反する点があります.例えばstackはvectorではないので、StackはVectorを拡張するべきではありません.