软件构造2

1.   
数据类型

Primitive types 基本数据类型(8种)

1. 
Object types 对象数据类型(reference types 引用数据类型)

Short、int 、long、float、double、boolean、char、byte

如:String、BigInteger

只有值,没有ID(无法与其他值区分),不能赋值为null;

immutable

有值,也有ID;
部分mutable,部分immutable

在栈中分配内存,代价低

在堆中分配内存,代价高

静态类型检查&动态类型检查

静态类型检查

Java是一种静态类型语言

在编译阶段进行类型检查

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

关于“类型”的检查,不考虑值 动态检查:关于“值”的检查

语法错误, 类名/函数名错误,参数数目错误,参数类型错误, 返回值类型错误

动态类型检查

在动态类型语言(如Python)中,这种检查被推迟到运行时(程序运行时)

在运行阶段进行类型检查

非法的参数值. 非法的返回值,越界,空指针

一种语言可以提供三种自动检查:

Static checking静态检查:在程序运行之前自动发现错误。

Dynamic checking动态检查:执行代码时自动发现错误

No checking无检查:语言根本不能帮助您找到错误。你必须自己小心,否则你会得到错误的答案

静态检查 >> 动态动态

无检查

Mutability and Immutability

改变一个变量、改变一个变量的值,二者有何区别?

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

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

Immutability 不变性

不变性:重要设计原则

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

如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变指向其他对象

要使引用不可变,请使用关键字final声明它

编译器进行静态类型检查时,如判断final变量首次赋值后发生了改 变,会提示错误

尽量使用final变 量作为方法的输入参数、作为局部变量。

final类无法派生子类

final变量无法改变值/引用

final方法
无法被子类重写

不变对象:一旦被创建,始终指向同一个值/引用

可变对象:拥有方法可以修改自己的值/引用

字符串是不可变类型的一个示例。

不可变对象:用双线
椭圆

StringBuilder是可变类型的一个示例

可变类型:修改值

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

不可变类型更“安全”,
在其他质量指标上表现更好

可变类型最
少化拷贝以提高效率

使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据

如果有多个引用(别名),使用可变类型就非常不安全

防御式拷贝

不可变类型
不需要防御式拷贝

Snapshot diagrams(快照图)

快照图显示程序在运行时的内部状态—其堆栈(正在进行的方法及其局部变量)和堆(当前存在的对象)。用于描述程序运行时的内部状态

便于程序员之间的交流,便于刻画各类变量随时间变化,便于解释设计思路

Primitive values 基本类型的值

Object values 对象类型的值

可变引用:单线箭头

不可变的引用:用双线箭头

引用是不可变的,但指向的值却可以是可变的

可变的引用,也可指向不可的值

例子:针对可变值的不可变 引用

针对不可变值的可变引用

Designing Specification 设计规约

1.       
作用

1.       
规约可以隔离"变化",无需通知客户端

2.       
规约可以提高代码效率

3.       
规约扮演"防火墙"角色

4.       
解耦,不需要了解具体实现

2.       
内容:只讲"能做什么",而不讲"怎么实现"

Behavior
equivalence 行为等价性

是否可以相互替换

1.       
站在客户端的视角看行为等价性,不同的行为,对用户来说(根据用户需求)可能等价!

2.       
根据规约判断行为等价,两个方法符合同一个规约,则等价

规约的结构:

1.       
Pre-condition

2.       
Post-condition

3.       
Exceptional behavior 异常行为,如果违背了前置条件,会发生什么 (throw an exception)

前置条件:对客户端的约束,在使用方法时必须满足的条件

后置条件:对开发者的约束,方法结束时必须满足的条件

契约:如果前置条件满足了,后置条件必须满足

前置条件不满足,则方法可做任何事情。

. 除非在后置条件里声明过,否则方法内部不应该改变输入参数

尽量不设计mutating的spec,否则容易引发bugs。

尽量避免使用mutable对象。

避免使用可变的全局变量。

规约的强度与替换

Spec变强:更放松的前置条件(前置条件更弱)+更严格的后置条件(后置条件你更强),

两条件同时变强或变弱则无法比较。

若规约强度S2>=S1,则可以用S2替换S1。

越强的规约,意味着implementor的自由度和责任越重,而client的 责任越轻。

Diagraming specification

规约定义一个区域,该区域包含所有可能的实现方式。

空间中的每个点表示一种方法的实现。

对于某个具体实现,若满足规约,则落在其区域内。

更强的规约表达为更小的区域。

1.       
Quality of specification 规约质量

1.       
内聚性:spec描述的功能应单一、简单、易理解

2.       
运行结果信息丰富(可能的改变,以及返回值等),不能让客户端产生理解上的歧义

3.       
足够强(如postcondition中充分阐述各种情况)

4.       
适当弱(太强的规约,在很多特殊情况下难以达到)

5.       
在规约里使用抽象类型(在java中,经常使用interface,如Map、List,而不是HashMap、ArrayList),可以给方法的实现体和客户端更大的自由度

6.       
使用前置条件和后置条件?

客户端不喜欢太强的pre-condition,不满足precondition的输入会导致失败

So:不限定太强的precondition,而在postcondition中抛出异常:输入不合法,

fail fast,避免fail大规模扩散

是否使用前置条件取决于:

1.       
check(检查参数合法性)的代价

2.       
方法的使用范围:

1.       
如果只在类内部使用(private),则可以不使用precondition,在使用该方法的各个位置进行check

2.       
如果在其他地方使用(public),则必须使用precondition,若client不满足则抛出异常

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值