Effective Java:可変性を最小化

16335 ワード

1.可変クラスとは
  • に必要なすべてのパラメータは、インスタンス化時に渡さなければなりません.
  • オブジェクト内のすべての情報は、オブジェクトのライフサイクル全体で一定に保たれます.

  • 2.クラスを可変にする原則
  • は、オブジェクトの状態を変更する方法を提供しないでください.
  • は、クラスが継承されないことを保証します.
  • は、すべてのドメインをfinalタイプにします.
  • は、すべてのドメインをプライベートタイプにします.
  • は、任意の可変コンポーネントに対する反発性を保証する.すなわち、クラスの外部では、クラス内の可変オブジェクトの参照が取得されないことを確認する.例えば次の例:
  • public class MyObject{
      private final List<String> list = new ArrayList<>;//    
      public List<String> getList() {
        return new ArrayList(list);
      }
      public void setList(List<String> list) {
        this.list = new ArrayList(list);
      }
    }
    

    3.可変クラスのメリット
  • 可変クラスは簡単です.可変クラスには1つのステータスしかありません.それは作成されたときのステータスです.このクラスに基づいて複雑な操作を行う場合は、この操作はいつでも結果が同じなので、結果を直接キャッシュして、次の操作を実行するときに取り出すことができます.次の操作を行う必要はありません.
  • 可変クラスは本質的にスレッドが安全であり、同期ロックは必要ありません.
  • 可変クラスでは、コピーメソッドを実装する必要はありません.コピー方法はそれにとって意味がありません.
  • 可変クラスは自由に共有することができる.

  • 4.可変クラスの欠点
    異なる値ごとに新しいオブジェクトを作成する必要があります.
    5.欠点の補い方
  • は、まず、どのようなマルチステップの操作が頻繁に使用されるかを推測し、その後、それらを基本データ型として提供する.たとえばIntegerは、-128~127の値を持つオブジェクトをキャッシュしますが、valueOf(int i)を呼び出すと、キャッシュから直接取り出し、繰り返し作成しなくても効率が向上します.
  • 可変クラスを作成できます.たとえばStringは、StirngBuilderとStringBufferの可変クラスです.次の例では、次のような加算方法を提供する複数のクラスを実装します.
  • public class Complex {
      private final double re;//  
      private final double im;//  
    
      private Complex(double re, double im) {
        this.re = re;
        this.im = im;
      }
    
      public static Complex valueOf(double re, double im) {
        return new Complex(re, im);
      }
    
      public double realPart() {
        return re;
      }
      public double imaginaryPart() {
        return im;
      }
    
      //    
      public Complex add(Complex c) {
        return new Complex(re + c.re, im + c.im);
      }
    }
    

    クラスを作成できます.
    public class ComplexBuilder {
      private double re;
      private double im;
    
      private ComplexBuider(double re, double im) {
        this.re = re;
        this.im = im;
      }
      public static ComplexBuider newInstance(Complex c) {
        return new ComplexBuilder(c.realPart(), c.imaginaryPart());
      }
    
      public void add(Complex c) {
        this.re = this.re + c.realPart();
        this.im = this.im + c.imaginaryPart();
      }
    
      public Complex toComplex() {
        return Complex.valueOf(this.re, this.im);
      }
    }
    

    クライアントで1つの複素数と別の複素数を100回加算する必要がある場合、ComplexBuilderを使用しない場合は、次のように、最初の着用の2つのインスタンスを計算して102のインスタンスを作成します.
    public class Test {
      @Test
      public void addNoBuiderTest() throws Exception{
        Complex c1 = Complex.valueOf(1, 2);
        Complex c2 = Complex.valueOf(2, 3);
        for (int i = 0 ; i < 100 ; i++) {
          c1 = c1.add(c2);
        }
      }
    }
    

    ComplexBuilderを使用すると、4つのインスタンスしか作成できません.
    public class Test {
      @Test
      public void addNoBuiderTest() throws Exception{
        Complex c1 = Complex.valueOf(1, 2);
        Complex c2 = Complex.valueOf(2, 3);
        ComplexBuilder cb = ComplexBuider.newInstance(c1);
        for (int i = 0 ; i < 100 ; i++) {
          cb.add(c2);
        }
        c1 = cb.toComplex();
      }
    }
    

    6.まとめ
  • は、getterごとにsetterを生成しないでください.
  • クラスを可変にできるものは可変にできます.
  • 一般的に小さい値クラスは可変にする必要があります.
  • は、いくつかの比較的大きな値クラスについて、できるだけ可変クラスに実現することを考慮する.
  • 性能面で必要な場合にセットクラスを提供する必要があります.
  • 本当に可変クラスとして機能しない場合は、その可変性をできるだけ制限します.
  • クラスの初期化は、コンストラクタまたは静的ファクトリでのみ実行できます.すなわち,クラスの初期化操作(付与など)は,コンストラクタや静的ファクトリで1回しか実行できない.この点はjavaクラスライブラリのTimerTaskクラスを参照します.