使可变性最小

作者: 想飞的僵尸 | 来源:发表于2016-07-18 00:31 被阅读67次

    第十五条:使可变性最小


    1.什么是不可变类?

    (1)需要的所有参数必须在实例化的时候都传进去。
    (2)对象中所有信息在对象的整个生命周期中都保持不变。

    2.使类不可变的原则

    (1)不要提供任何修改对象状态的方法。
    (2)保证类不会被继承。
    (3)使所有的域都是final类型的。
    (4)使所有的域都是私有类型的。
    (5)确保对于任何可变组件的互斥性。意思就是,确保在该类的外部不会获取到该类中可变对象的引用。比如下面这个例子:

    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)不可变类简单。
    不可变类只有一种状态,就是它被创建出来时候的状态,如果你要根据这个类进行一系列复杂操作,那么这个操作无论在什么时候结果都是相同的,所以你可以直接将结果缓存起来,在下一次执行同样操作的时候取出来,而不必再进行下一次操作。
    (2)不可变类本质上是线程安全的,它不需要同步锁。
    (3)对于不可变类,你永远都不需要实现拷贝方法。拷贝方法对它来说是没有意义的。
    (4)不可变类可以被自由的共享。

    4.不可变对象的缺点

    (1)对于每一个不同的值都需要创建一个新的对象。

    5.缺点的弥补办法

    (1)先猜测一下经常用到哪些多步骤的操作,然后将它们作为基本数据类型提供。比如Integer,它将值为-128到127的对象缓存起来,但调用valueOf(int i)的时候,直接从缓存中拿,不用再重复创建提高效率。

    (2)我们可以创建一个可变配套类。例如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);
      }
    }
    

    在客户端中我们如果需要用一个复数和另一个复数相加100次,我们如果不用ComplexBuilder的话就像下面这样,算上最开始穿件的两个实例,我们将会创建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.总结

    (1)坚决不要为每个getter都生成setter。
    (2)能将类做成不可变的就做成不可变的。
    (3)一般比较小的值类都是需要做成不可变的。
    (4)对于一些比较大的值类尽量考虑实现成不可变类。
    (5)性能方面很有必要的时候才需提供配套类。
    (6)如果真的不能作为不可变类,那就尽量限制其可变性
    (7)对于一个类的初始化只能在构造器或是静态工厂中完成。也就是说类的初始化操作(赋值之类的)只能执行一次,就是在构造器或是静态工厂中。这一点参考java类库中的TimerTask类。

    相关文章

      网友评论

        本文标题:使可变性最小

        本文链接:https://www.haomeiwen.com/subject/tfcpjttx.html