行为模式(Behavioral Pattern)中的不变模式

行为模式(Behavioral Pattern)中的不变模式

行为模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。行为模式不仅仅是关于类和对象的,而且是关于它们之间的相互作用的。

行为模式分为类的行为模式和对象的行为模式两种。

类的行为模式   类的行为模式使用继承关系在几个类之间分配行为。

对象的行为模式  对象的行为模式则使用对象的聚合来分配行为。

常见的行为模式:

不变模式、策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、解释器模式、调停者模式。

不变模式(Immutable):一个对象的状态在对象对象被创建之后就不再变化,这就是所谓的不变模式(Immutable Pattern,一般来讲,一个对象要么是可变对象(Mutable Object),要么是不变对象(Immutable Object)。一个可变对象的状态可以改变,而一个不变对象的状态不可改变。不变模式缺少改变自身的状态行为,因此它是关于行为的,所以可以把它归为行为模式。

     不变模式的结构和实现

不变模式可增强对象的强壮性(robustness)。不变模式允许多个对象共享某一对象,降低了对该对象进行并发访问(Concurrent Access)时的同步化开销,如果需要修改一个不变对象的状态,那么就需要建立一个新的同类型的对象,并在创建时将这个新的状态存储在新的对象里。

不变模式只涉及到一个类。一个类的内部状态创建后,在整个生命期间都不会发生变化时,这样的类称作不变类。这种使用不变类的做法叫做不变模式。不变模式不需要类图描述。

不变模式有两种形式: 一种是弱不变模式, 另一种是强不变模式。

弱不变模式:一个类的实例的状态时不可变的;但是这个类的子类的实例具有可能会变化的状态。这样的类符合弱不变模式的定义。要实现弱不变模式,一个类必须满足下面条件:

     所考虑的对象没有任何方法会修改对象的状态;这样一来,当对象的构造子将对象的状态初始化之后,对象的状态便不再改变。

     所有的属性都应当是私有的。不要声明任何的公开的属性,以防客户端对象直接修改任何内部状态。

     这个对象所引用的其他对象如果是可变对象的话,必须设法限制外界对这些可变对象的访问,以防止外界修改这些对象。如果可能,应当尽量在不变对象内部初始化这些被引用到的对象,而不要再客户端初始化,然后再传入到不变对象内部来。如果某个可变对象必须在客户端初始化,然后再传入到不变对象里的话,就应当考虑在不变对象初始化的时候,将这个可变对象复制一份,而不要使用原来的拷贝。

弱不变模式的缺点:

   一个弱不变对象的子对象可以是可变对像;换言之,一个弱不变对象的子对象可能是可变的。

   这个可变的子对象可能可以修改父对象的状态,从而可能会修改父类对象的状态。

强不变模式:一个类的实例的状态不会改变;同时它的子类的实例也具有不可变的状态。这样的类符合强不变模式。要实现强不变模式,一个类必须首先满足弱不变模式所要求的所有所有条件,并且还要满足下面条件之一:

所考虑的类的所有方法都应当是final;这样这个类的子类不能够置换掉此类的方法。

这个类本身就是final的。那么这个类就不可能会有子类,从而也就不可能有被子类修改的问题。

不变与只读的区别:

“不变(Immutable)”与“只读(Read Only)”是不同的。当一个变量是“只读”时,变量的值不能直接改变,但是可以在其他变量发生改变的时候发生改变。

比如,一个人的出生年月日是“不变”属性,而一个人的年龄便是“只读的”但是不是“不变”属性。随着时间的变化,一个人的年龄会随之发生变化,而人的出生年月是不会变化的。这就是“不变”和“只读”的区别。
不变模式在Java语言中的应用:不变模式在Java中有很重要的应用,最著名的应用便是java.lang.String

String  JavaString是一个强不变类型,在出现如下的语句时:

String youSay = “Object-oriented design means we design, and users object”;

String iSay = “Object-oriented design means we design, and users object”;

String weSay = “Object-oriented design means we design, and users object”;

Java虚拟机其实只会创建这一个字符串的实例,而这三个String对象都共享着一个值。

如果程序所处理的文字串有频繁的内容变化时,就不宜使用String类型,而应当考虑使用StringBuffer类型;如果需要对文字串做大量的循环查询时,也不宜使用String类型,而应当考虑使用bytechar数组。

封装类

String类实际上是一个封装类(Wrapper),因为它包装了一个char的数组。在Java语言中,java.lang库里还有其他的封装类,如Integer, Float, Double, Byte, Long, Short, BooleanCharacter,为什么需要这些封装类型呢?

一个Long类型的对象所起的作用在于它把一个long原始类型的值封装在一个对象里。如果读者使用过Vector对象就会知道,存放在Vector对象里面的必须是对象,而不可以是原始类型。有了封装类,就可以把原始类型包装起来作为对象处理。如果要将一个long类型的值存放在一个Vector对象里面,就可以把这个long类型的值包装到Long对象里面,然后再存放到Vector对象里。比如:

Vector v = new Vector(); v.addElement(new Long(100L));  v.addElement(new Long(101L)); 这些封装类实际上都是强不变类,因为在这些类都是final的,而且在对象被创建时,它们所蕴含的值(也就是它们的状态)就确定了。如果看一看Long类型的结构图就会发现,Long类型根本没有提供修改内部值的方法。

不变模式的优点和缺点:

不变模式有很明显的优点:

     因为不能修改一个不变对象的状态,所以可以避免由此引起的不必要的程序错误;换言之,一个不变的对象要比可变的对象更加容易维护。

     因为没有任何一个线程能够修改不变的内部状态,一个不变对象自动就是线程安全的(Thread Safe),这样就可以省掉处理同步化的开销。一个不变对象可以自由地被不同的客户端共享,不变模式唯一的缺点是,一旦需要修改一个不变对象的状态,就只好创建一个新的同类对象。在需要频繁修改不变对象的环境里。会有大量的不变对象作为中间结果被创建出来,再被Java语言的垃圾收集器收集走。这是一种资源上的浪费。在设计任何一个类的时候,应当慎重考虑其状态是否有需要变化的可能性。除非其状态有变化的必要,不然应当将它设计成不变类。

为了说明不变模式的使用,本书在此准备了一个复数类的例子。与前面所讨论过的Java语言所提供的封装类,如IntegerLong类一样,这个复数类是一个不变类。为简化起见,只考虑支持以下的操作:加、减、乘、除;取实部、取虚部、取复数共轭、取相反数;绝对值、相位角、是否相等的测试类

由于Java语言不支持运算符的重载,这些运算符必须以静态方法的形式实现,仔细看下面得代码,你会发现所有的方法都不修改复数值的实部和虚部。(代码后续)

 

 

 

 

 

你可能感兴趣的:(java,vector,String,Integer,语言,immutable)