欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

软件构造月总结2

程序员文章站 2024-02-09 18:18:40
...

续接上文:https://blog.csdn.net/qq_42991073/article/details/105221557

1.数据类型和类型检查

基本数据类型:int,long,byte,short,char,float,double,boolean
只有值,没有ID;Immutable;在栈中分配内存;代价低

对象数据类型:Class,interface,arrays,enums,annotations
既有ID也有值;有的是Immutable,有的是mutable;在堆中分配内存;代价高

静态类型检查:没有运行代码(在编译阶段),然后系统对代码的检查
动态类型检查:运行代码时进行的代码检查
静态类型检查>动态类型检查>无检查

静态类型语言:在编译阶段进行类型检查
动态类型语言:在运行阶段进行类型检查

静态类型检查(关于类型的检查):语法错误;类名、函数名错误;参数数目错误;返回值类型错误
动态类型检查(关于值的检查):非法的参数值;非法的返回值;越界;空指针

Mutable(创建后,值是可以被改变的)
Immutable(一旦被创建,值是不能改变的)

老师给的例子:

String t = s;
t = t + 'c';
StringBuilder tb = sb;
tb.append("c");

软件构造月总结2我们由这个snapshot图就能很清楚的看到,String是一个Immutable的类型,而StringBuilder是一个mutable的类型(这两个类型在多个引用同时指向的时候回有很大差别)
Immutable的安全,但是mutable的性能好

2.snapshot

基本类型的值
软件构造月总结2
对象类型的值
软件构造月总结2
不可变对象:用双线的椭圆
软件构造月总结2
不可变得引用(final):
软件构造月总结2

3.Complex data types

Array:这个跟C语言等其它语言的应用方式差不多(直接举出老师给的例子)

int[] a = new int[100];
int i = 0; 
int n = 3; 
while (n != 1) { 
	a[i] = n; 
	i++; 
	if (n % 2 == 0) { 
		n = n / 2; 
	} 
	else { 
		n = 3 * n + 1; 
	} 
} 
a[i] = n; 
i++;

List:这个就是一个可变长度的数组(是一个接口)
有add,set,size,get等方法

 list.get(2);
 list.set(2, 0);	
 list.size();

Set:集合类(是一个接口)

s1.contains(e) //test if the set contains an element
s1.containsAll(s2)//test whether s1 ⊇ s2
s1.removeAll(s2) //remove s2 from s1

Map:储存键值对的一个数组(是一个接口)

map.put(key, val) //add the mapping key → val
map.get(key) //get the value for a key
map.containsKey(key) //test whether the map has a key
map.remove(key) //delete a mapping

Iterator:迭代器(删除某元素的时候非常有用)其实跟循环没啥差别

Iterator iter = lst.iterator();
while(iter.hasNext()){
	String str = iter.next();
	System.out.println(str);
}

4.抽象数据类型(ADT)

抽象数据类型就是指,我们使用一些数据,但是我们不能直接使用这些值,我们只能调用一些的方法来间接地使用这些数据

可变类型的对象:提供了可改变其内部数据的值的操作
不变数据类型:其操作不改变内部值,而是构建新的对象

Creator(构造器):t*->T
Producer(生产器):T+,t->T
Observer(观察器):T+,t*->t
Mutator(改变器):T+,t*->void/t/T

老师在课件中举出过几个例子:

Integer.valueOf() 		//Creator
BigInteger.mod()		//Producer
List.addAll()			//Mutator
String.toUpperCase()		//Producer
Set.contains()			//Observer
Map.keySet()			//Observer
Collections.unmodifiableList()	//Producer
BufferedReader.readLine()	//Mutator

设计ADT的时候:
设计好的ADT,靠“经验法则”,提供一组操作,设计其行为规约 spec
设计简洁、一致的操作
要足以支持client对数据所做的所有操作需要,且用操作满足client需要的难度要低

表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。除非ADT的操作指明了具体的pre- 和post-condition,否则不能改变ADT的内部表示——spec规定了client和implementer之间的契约。

测试ADT:
测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec; 
测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。

不变量(在任何时候总是true,不应该改变,由ADT来负责其不变量,与client端的任何行为无关)

5.AF和RI(Rep Invariant and Abstraction Function)

ADT开发者关注表示空间R,client关注抽象空间A
抽象函数:R和A之间映射关系的函数,即如何去解释R中的每一个值为A中的每一个值。
AF : R → A 满射、非单射、未必双射
RI : R → boolean

即使是同样的R、同样的RI,也可能有不同的AF,即“解释不同”。

设计ADT:(1) 选择R和A;(2) RI — 合法的表示值;(3) 如何解释合法的表示值 —映射AF
做出具体的解释:每个rep value如何映射到abstract value

!!!随时检查RI是否满足:
在所有可能改变rep的方法内都要检查,Observer方法可以不用,但建议也要检查,以防止你的“万一”

6.Beneficent mutation

对immutable的ADT来说,它在A空间的abstract value应是不变的。
但其内部表示的R空间中的取值则可以是变化的。
这种mutation只是改变了R值,并未改变A值,对client来说是immutable的 “AF并非单射”,从一个R值变成了另一个R值 ,但这并不代表在immutable的类中就可以随意出现mutator!

7.Documenting the AF, RI, and Safety from Rep Exposure

在代码中用注释形式记录AF和RI
要精确的记录RI:rep中的所有fields何为有效
要精确记录AF:如何解释每一个R值
表示泄漏的安全声明给出理由,证明代码并未对外泄露其内部表示——自证清白
ADT的规约里只能使用client可见的内容来撰写,包括参数、返回值、异常等。
如果规约里需要提及“值”,只能使用A空间中的“值”。
ADT的规约里也不应谈及任何内部表示的细节,以及R空间中的任何值
ADT的内部表示(私有属性)对外部都应严格不可见
故在代码中以注释的形式写出AF和RI而不能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏
在对象的初始状态不变量为true,在对象发生变化时,不变量也要为true
构造器和生产器在创建对象时要确保不变量为true
变值器和观察器在执行时必须保持不变性。
在每个方法return之前,用checkRep()检查不变量是否得以保持。
表示泄漏的风险:一旦泄露,ADT内部表示可能会在程序的任何位置发生改变(而不是限制在ADT内部),从而无法确保ADT的不变量是否能够始终保持为true。

用这三个标准来检查你的ADT是否保持不变量?
established by creators and producers;
preserved by mutators, and observers;
no representation exposure occurs,