设计不可变类

1、介绍不可变类
不可变类指的是其实例不能被修改的类,每个实例所包含的信息必须在创建该实例的时候就确定下来,并且在对象的整个生命周期内不可修改!Java类库中有比如String、包装类等都是不可变类。
2、设计原则
2.1、不要提供任何修改对象状态的方法,比如set方法。
2.2、保证类不被拓展,因为无论是粗心还是恶意地使用子类是可以假装对象的状态已经改变。一般来说有两种方式解决这个问题,其一将类设计成final,这个最简单有效但是有一个缺点,设计者无法在同一个包中拓展该类。所以第二种方案,私有化构造器或者包级私有,提供静态工厂地方式。因为在该包中由设计者亲自拓展,客户端无法拓展,这样既灵活又能保证不变!
2.3、声明所有的域都是私有的。防止客户端直接访问并修改,虽然像基本类型或者值对象这样的域,本身无法被修改,但是也不要使用public来修饰,因为客户端可以随意赋值可能存在数据安全问题。
2.4、声明所有的域都是final。虽然私有化加上上面的不提供设值方法可以保障域不被改变,但是如果使用final会更加清晰地表明你的意图。
2.5、如果域中含有可变对象,则要确保这些引用不被客户端所能获取。这是因为客户端获取了这些可变引用,可以用来修改该引用的对象中的值,比如数组。可以采取保护性拷贝的方式!
3.不可变类的优点
3.1、不可变对象比较简单,因为对象的域都是不可变的,所以维护起来很简单!
3.2、是线程安全的,不需要同步!多个线程并发访问时,不会产生诸如数据不一致的问题,所以它是获取线程安全的最简单的方式!
3.3、可以被自由地共享。这样的话,客户端可以尽量地重用对象了,而且如果有些值是重复出现地,你甚至可以提供静态工厂缓存频繁使用的实例,让客户端直接获取。
3.4、对于整个对象不需要进行保护性拷贝(创建值相同的不同对象)。因为它是不变的,重用对象即可,没有必要进行拷贝!这个和前面说的域的拷贝不同,因为单个域可能是可变的对象,所以要进行保护性拷贝,如果可变的域进行了保护性拷贝,那么整个对象就是不变的和安全的,可以共享,所以就不用进行保护性拷贝,尽量重用对象即可。
3.5、提供了无偿的失败原子性。不存在状态的临时不一致,因为是不变的。
4.缺点及解决方案
缺点在于,对于不同的值,就要产生新的对象,比如String两个不可变对象很有可能只是后面拼接了一个字符而已,但是他们是不同的对象!如果在循环中拼接,就会导致性能问题!解决方案:提供一个配套类,如String提供了StringBuilder使用!
5、设计样例
下面展示一些 内联代码片

// An highlighted block
/**
 * 使可变性最小化
 * 不可变类指其实例不能被修改的类,每个实例的所有信息必须在创建该实例的时候就提供
 * 并在整个对象生命周期内不变
 * final 复数类  注意:1.不提供设置方法 2.类不该被拓展 3.所有域都是final
 * 4.所有域都private 5.确保对于可变性组件互斥访问
 * 不可变对象本质上是线程安全的,不需要同步  当然也不需要拷贝
 * 不可变对象唯一的缺点就是当一个大对象,你稍微改动一点就需要重新创建一个大对象
 * 尤其当有多步骤修改的时候会产生大量无用的大对象
 * 解决方法:1.若能预料客户端要进行哪些复杂的操作,提供一个包级私有的可配套类即可
 * 2.无法预测则提供一个共有的可配套类 如String  StringBuilder
 * 当然还有另一种实现不可变类的方式是私有化构造方法 并提供一个静态工厂 这种方式更灵活
 */
public final class Complex {
    private final double re;//实部
    private final double im;//虚部
    /**
     * 不可变对象安全所以自由共享 尤其对于频繁用到的值
     */
    public static final Complex ZERO = new Complex(0, 0);
    public static final Complex ONE = new Complex(1, 0);
    public static final Complex I = new Complex(0, 1);

    /**
     * 不可变类可以被进一步拓展,如静态工厂,用以缓存实例
     * @param re
     * @param im
     */

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public double realPart() {
        return this.re;
    }

    public double imaginaryPart() {
        return this.im;
    }

    /**
     * 函数方法:方法返回一个函数结果但不影响原来的对象
     * 方法名不应该叫add
     *
     * @param c
     * @return
     */
    public Complex plus(Complex c) {
        return new Complex(this.re + c.re, this.im + c.im);
    }

    public Complex minus(Complex c) {
        return new Complex(this.re - c.re, this.im - c.im);
    }
    
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值