软件构造复习内容(3)--ADT

一。可变类和不可变类(Mutability and Immutability)

  可变类有Mutator方法,不可变类没有Mutator方法,该类一旦被实例化,不能再被改变

二。SnapShot Diagram(程序快照图)

  用于描述程序运行时的内部状态。

    用于描述程序运行时的内部状态

    便于程序员之间交流

    便于刻画各类变量随时间变化

    便于解释思路

  基本类型的值的表示

  

  对象类型的值的表示  

   

  不可变对象:使用双线椭圆

  

    String s= “a”;
    s=s+"b";

  表示:

  

  可变对象使用:单线椭圆

    StringBuilder sb= new StringBuilder("a");
    sb.append("b");

  表示:

  

不可变的引用(使用final进行限制):

  使用双箭头指向

 三。Designing Specification(设计规约)

  (1)规约和实现的区别:

  

   规约只说要做什么,不说怎么实现

  (2)行为等价性(Behavioral Equivalence)

  判断2个方法是否可以相互替换需要站在客户端视角看行为等价性,即如果规约等价,则这两个方法等价

  (3) 规约结构

    pre-condition (前置条件) 和  post-condition(后置条件)

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

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

    契约:如果前置条件满足了,那么后置条件也应该满足,前置条件不满足,则方法可以做任何事

    方法的静态类型说明也是一种规约(方法签名)

      例如: public void change(int a)

    除非在后置条件里声明过,否则不应该修改输入参数

  测试用例也需要符合规约

  (4)规约的强度

  首先要比较的方法签名要相同,这样才可以进行比较

  然后,前置条件越弱,后置条件越强,则桂规约强度越强

  (5)设计规约的要求

  1.内聚(coherent)

    Spec描述的功能应该单一,简单,易理解

  2.后置条件应该信息丰富

  3.规约要足够强

    太弱的 spec client 不放心、不敢用 因为没有给出足够的承诺 。

  4.也不能太强    

    太强的 spec ,在很多特殊情况下难以达到,给开发者增加了实现的难

    度( client 当然非常高兴)。

  5.在规约里使用抽象类型,可以给方法的实现提与客户端更大的自由度。

  6.For programmer:

  是否应该使用前置条件?在方法正式执行之前,是否要检查前置条件已被满足?(要满足前置条件还是可以不需要前置条件,在方法内直接check前置条件是否满足)

  归纳: 是否使用前置条件取决于 (1) check 的代价 ;(2) 方法的使用范围

  如果只在类的内部使用该方法 ( private),那么可以不使用前置条件,在使用

  该方法的各个位置进行 check 责任交给内部 client

  如果在其他地方使用该方法 ( public),那么必须要使用前置条件,若 client 端

  不满足则方法抛出异常。

ADT(Abstract Data Type)

  (1)抽象类型:强调“作用于数据上的操作”,程序员和

  client 无需关心数据如何具体存储的,只需设计 使用操作即可。

  (2)分类:

    可变和不可变数据类型(有无Mutator方法)

    操作类型分类:

      Creator(构造器):从无到有构造一个对象

        可能为构造函数或者静态函数,也可以是工厂方法

      Producaer(生产器):从有到有,根据已有对象产生一个新对象,

      Observer(观察器):观察ADT的侧面属性,根据ADT返回其侧面属性

      Mutator(变值器):改变对象属性的方法

        变值器通常返回void,如果返回值为 void ,则必然意味着它改变了对象的某些内部状态,但是也可以返回非空类型

  (3) 设计ADT

    设计好的 ADT ,靠“经验法则”,提供一组操作,设计其行为规约 spec

  (4)表示独立性Representation Independence

    client使用ADT时无需考虑其内部如何实现,ADT内部的变化不应该影响外部spec和客户端。

  (5)测试ADT

    测试 creators, producers, and mutators :调用 observers 来观察这些operations 的结果是否满足 spec

    测试 observers :调用 creators, producers, and mutators 等方法产生或改变对象,来看结果是否正确。  

    风险:如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效。

  (6)Invariants of an ADT(保持不变量)

    在任何时间均为true,由ADT来负责其不变量,与client端的任何行为无关

    为什么需要不变量:保持程序的“正确性”,容易发现错误

  (7)表示泄露(Rep exposure)

    把ADT的内部引用返回到了cilent,可以让其随意修改,不仅影响不变性,也影响表示独立性,无法在不影响客户端的情况下改变其内部表示

    防止Rep exposure:

      1.私有属性(private)

      2.final限制 private final int a =4;

      3.返回对象引用的复制到客户端

      4.最好的办法就是使用 immutable 的类型,彻底避免表示泄露

    保持不变性和避免表示泄露,是ADT最重要的一个Invariant

  (8)AF抽象函数和RI表示不变量

    AF是一个从R空间(表示空间)映射到A空间(抽象空间)的函数

      抽象值构成的空间是client看到和使用的值

      表示空间是实际的值

      用表示空间内的一个值表示抽象空间内的一个值

    ADT开发者关注表示空间R,client关注抽象空间A

    AF性质:满射,未必单射,未必双射

      性质的意思就是在R空间内不是所有值都可以对应到A空间中,但是A空间中的值必须都被对应。

    RI表示不变量  

      表示不变性 RI :某个具体的“表示”是否是“合法的

      也可将 RI 看作:所有表示值的一个子集,包含了所有合法的表示值

      也可将 RI 看作:一个条件,描述了什么是“合法”的表示值

      RI从R空间中筛选出了一部分合法值

    设计ADT:

    (1)选择R和A

    (2)RI----合法的表示值

    (3)如何解释合法的表示值---映射AF

    做出具体的结束:每个rep value 如何映射到abstract value

    需要随时检查RI是否满足CheckRep();

    在所以可能改变rep的方法内都要检查

  (9)有益的改变  

    对 immutable 的 ADT 来说,它在 A 空间的 abstract value 应是不变的。

    但其内部表示的 R 空间中的取值则可以是变化的。

    不可变类可以出现有益的改变,并不影响其不可变的属性,但是不要随意出现

  (10)注释AF和RI(AF和RI是给程序员看的,所以可以涉及R空间,并且不能展示给client看)

    //AF
    //说明AF
    //RI
    //说明RI
    //Rep exposure
    //给出理由,说明没有表示泄露

  (11)ADT规约要求

    ADT的规约里只能使用client可见的内容来撰写,包括参数,返回值,异常等

    ADT的规约内不应该涉及任何内部表示,ADT的内部表示(私有属性)对外部应该严格不可见

  (12)确保Invariants为true

    在对象初始状态不变量为true时,在对象变化时,不变量也应该为true

    •   Creator和producer在创建对象是保证不变量为ture
    •   Mutator和observer在观察和变值时保证不变量为ture
    •   在每个方法return时,用checkRep()检查不变量是否得以表示
    •   没有表示泄露

  

  

猜你喜欢

转载自www.cnblogs.com/guiququ/p/13176140.html