《软件构造》 第三章 抽象数据类型和面向对象的编程 上

写在最开头,个人认为这是java或者是软件构造里面最核心的部分。如何面对给出的要求,构造出结构清晰合理的类和接口,我觉得是最有难度的,也是最充满艺术性的。

本章小结:

数据类型:基本数据类型,对象数据类型

可变性与不可变性:可变与不可变,防御性编程

设计规约:SPEC

》》数据类型

基本数据类型和对象数据类型的区别

《软件构造》 第三章 抽象数据类型和面向对象的编程 上_第1张图片

java中有8个封装了的对象类型表示基本类型:– Boolean, Integer, Short, Long, Character, Float, Double,Byte(尽量避免使用)

类型检测:Java是一种静态类型的语言,所有变量的类型在编译的时候就已经知道了(程序还没有运行),所以编译器也可以测出每一个表达式的类型。在动态类型语言中(例如Python),这种类型检查是发生在程序运行的时候。

——静态类型检查:可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性

静态分析检查的类型:一般来说只检查变量的类型

  • 语法错误,例如多余的标点符号或者错误的关键词。即使在动态类型的语言例如Python中也会做这种检查:如果你有一个多余的缩进,在运行之前就能发现它。
  • 类名\函数名错误,例如Math.sine(2) . (应该是 sin )
  • 参数数目错误,例如 Math.sin(30, 20) 
  • 参数类型错误 Math.sin("30") 
  • 返回值类型错误 ,例如⼀个声明返回 int 类型函数 return "30”

—动态类型检查:倾向于检查“值”

动态分析检查的类型:

  • 非法的变量值。例如整型变量x、y,表达式x/y 只有在运行后y为0才会报错,否则就是正确的。
  • 非法的返回值。例如最后得到的返回值无法用声明的类型来表明。
  • 越界访问。例如在一个字符串中使用一个负数索引。
  • 空指针,使用一个null 对象解引用

》》Mutability and Immutability(可变性和不可变性)

改变一个变量:是将该变量指向另一个值得存储空间

改变一个变量的值:是将该变量当前指向的值的存储空间中写入一个新的值

1.不变性:不变数据类型一旦被创建,其值不能改变。

——final 变量能被显式地初始化并且只能初始化一次。如果编译器不能确定final变量不会改变,就提示错误,这也是静态类型检查的一部分。注意:final类无法派生子类,final变量无法改变值/引用,final方法无法被子类重写

——基本类型及其封装对象类型都是不可变的

——不可变的引用是指一旦指定引用位置后,不可再次指定。

2.可变性:可以有方法改变对象的值

StringBuilder是一个可变类型:

String s="a";                     StringBuilder sb=new StringBuilder("a");

s=s.concat("b");                 sb.append("b");

《软件构造》 第三章 抽象数据类型和面向对象的编程 上_第2张图片

3.可变和不可变的优缺点

——可变数据类型最小化的拷贝以提高效率

——使用不可变类型,对其频繁修改会产生大量的临时拷贝 (需要垃圾回收 )

——可变数据类型,可获得更好的效能;可变数据类型也适合在多个模块之间共享数据

——不可变数据类型更安全,更易于理解,也更方便改变;

》》快照图:不可变对象用双层椭圆;不可变引用(final)用双线箭头

在用final修饰可变类型的时候,引用不能变,值能变。

《软件构造》 第三章 抽象数据类型和面向对象的编程 上_第3张图片

在用可变去引用不可变时,可能会出现预期外的结果。

《软件构造》 第三章 抽象数据类型和面向对象的编程 上_第4张图片

》》防御式拷贝:方法返回的时候不要直接返回可变的变量,那样可能会导致变量改变。应该返回一个拷贝之后的变量(不直接引用原来的变量)

》》设计规约

——为什么要有设计规约:很多bug来自于双方之间的误解;没有规约,那么不同开发者的理解就可能不同代码惯例增加了软件包的可读性,使工程师们更快、更完整的理解软件可以帮助程序员养成良好的编程习惯,提高代码质量没有规约,难以定位错误

——使用设计规约的好处规约起到了契约的作用。代表着程序与客户端之间达成的一致;客户端无需阅读调用函数的代码,只需理解spec即可。精确的规约,有助于区分责任,给“供需双方”确定了责任,在调用的时候双方都要遵守

行为等价性:站在客户端视角看等价性,根据规约判断功能是否等价

规约的结构:一个方法的规约常由以下几个短句组成契约:如果前置条件满足了,后置条件必须满足。如果没有满足,将产生不确定的异常行为

——前置条件(precondition):对客户端的约束,在使用方法时必须满足的条件。由关键字 requires 表示

——后置条件(postcondition):对开发者的约束,方法结束时必须满足的条件。由关键字 effects 表示

——异常行为(Exceptional behavior):如果前置条件被违背,会发生什么

方法前的注释也是一种规约,参数用@param表示,前置条件写在@param后面;后置条件写在@return和@throws(可能发生的异常)后面

《软件构造》 第三章 抽象数据类型和面向对象的编程 上_第5张图片

尽量不要设计可变的(mutating)规约:除非后置条件声明,否则方法内部不应该改变输入参数;避免使用可变对象

》》规约的评价:有三个标准,规约的正确性、规约的陈述性、规约的强度

——规约的确定性:确定的规约是给定一个满足前置条件的输入,其输出是唯一的、明确的;欠定的规约是同一个输入可以有多个输出

——规约的陈述性:操作式规约:伪代码;声明式规约:没有内部实现的描述,只有 “初-终”状态; 声明式规约更有价值,内部实现的细节不在规约里呈现,而放在代码实现体内部注释里呈现。

——规约的强度:通过比较规约的强度来判断是否可以用一个规约替换另一个;如果规约的强度 S2>=S1,就可以用S2代替S1,体现有二:一个更强的规约包括更轻松的前置条件和更严格的后置条件;越强的规约,意味着实现者的自由度和责任越重,而客户的责任越轻。S2的前置条件更弱 || S2的后置条件更强

图表规范:某个具体实现,若满足规约则落在范围内;否则,在其之外






你可能感兴趣的:(《软件构造》 第三章 抽象数据类型和面向对象的编程 上)