【软件构造】第三章知识整理(下)

ADT 操作的四种类型

构造器(creator):方法构造新的对象,可能实现为构造函数或静态函数

生产器(producer):方法从已存在的对象中创建一个新的对象,e.g.String.concat(String a)

观察器(Observer):方法返回一个对象的相关值,该值与对象不是一个类型,e.g.List.size()

变值器(Mutator):改变对象属性的方法,e.g.List.add(int a),通常返回值为void

 

 

 

 

表示独立性

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

 

 

 

 

表示泄露

  表示泄露指外部类的代码会改变该类内部的表示。这样不仅影响不变性,也影响了表示独立性,同样也无法在不影响客户端的情况下改变其内部表示。

如何解决:

(1)对于只在该类中使用的函数使用private关键字

(2)使用final关键字

(3)使用防御式拷贝

 

 

 

 

不变量、表示不变量RI

不变量:在任何时候总是true(为了保持程序的“正确性”,容易发现错误)

***在对象的初始状态不变量为true,在对象发生变化时,不变量也要为true,即:构造器和生产器在创建对象时要确保不变量为true,变值器和观察器在执行时也必须保持不变性。

表示不变量RI:某个具体的“表示”是否是“合法的”,也可看做所有表示值得一个子集,包含了所有合法的表示值,也可将其看做一个条件,描述什么是“合法”的表示值。

 

 

表示空间、抽象空间、AF

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

ADT实现者关注表示空间R,用户关注抽象空间A

AF(R-->A):抽象函数——R和A之间映射关系的函数

 

 

 

 

以注释的形式撰写AF、RI

***AF和RI都应该记录在代码中,紧挨着rep的声明。首先选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射到抽象空间中的值。

***若以注释的形式撰写AF、RI,则该注释中需要包含:选择R和A + RI(合法的表示值) + 映射AF(如何解释合法的表示值)需要做出具体的解释:每个rep value 如何映射到abstract value

格式如下图所示:

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

 

 

 

 

接口、抽象类、具体类

  1接口:是ADT方法的集合,但是没有具体实现方法的代码。

          其具体方法的实现在类中,用implements关键字连接接口

          接口之间可以继承,一个类可以实现多个接口,一个接口可以有多种实现

     接口:确定ADT规约,类:实现ADT

  2抽象类

     ***抽象方法:具有方法名字但是没有具体实现的方法,需要有关键字

abstract

     ***抽象类:一个类至少包含一个抽象方法。

     *** 抽象接口:只含有抽象方法的抽象类

    

 

 

 

 

继承、override

***严格继承:子类只能添加新方法,无法重写超类中的方法。若要防止重写超类中的方法,超类中的方法需要用关键字final修饰。

***override:重写的函数:完全同样的signature;实际执行时调用哪个方法,运行时决定,在重写的方法前,需要使用@Override关键字。若想在重写的函数中使用超类中的方法,使用super关键字。

***重写的时候,不要改变原方法的本意

 

 

 

 

多态、Overload

***多态:特殊多态 + 参数化多态 + 子类型多态、包含多态

         多个函数有同一个函数名,但是其参数类型/个数不同。

***Overload:多个方法具有同样的名字,但有不同的参数列表或返回值类型。

   价值:方便client调用,client可用不同的参数列表,调用同样的函数。

   重载(Overload)是一种静态多态,根据参数列表进行最佳匹配(静态类型

检查 + 在编译阶段时决定具体要执行哪种方法)

与Override方法相比:在运行阶段进行动态检查

***Overload的规则:不同的参数列表

                    相同/不同的返回值类型

                    相同/不同的public/private/protected

                    异常

                    可以在同一类内重载,也可以在子类中重载

e.g

***OverrideOverload的区别

 

 

 

 

泛型

若一个接口声明了变量类型,那该接口被称作泛型接口;若一个方法声明了变量类型,那该方法被称作泛型方法

e.gpublic void add(Titem){…}

    public T get(int index){…}

     publicclass PaperJar<T>{…}

***泛型接口的两种实现类:

   泛型接口,非泛型的实现类

     e.gpublic interfaceSet<E>{…}

         public class CharSet1 implements Set<Character>{…}

   ②泛型接口,泛型的实现类

     e.gpublic interfaceSet<E>{…}

         public class HashSet<E> implements Set<E>{…}

***通配符(?)只在使用泛型的时候出现,不能再定义中出现

   e.gList<?> list =new ArrayList<String>();

       List<? extends Animal>

       List<? super Animal>

 

 

 

 

等价性equals==

***在自定义ADT时,需要override Objectequals(),这是由于Object内的equals()是判定引用等价性

***对于基本数据类型,使用==判定相等,这是由于==是在判断两个对象的身份标识ID是否相等,即是否指向内存里的同一段空间

   对于对象,使用equals()判定相等

 

 

 

 

equals()的自反、传递、对称

***自反:对任意非空的引用变量xx.equals(x)必须返回true

***对称: 对任意非空的引用变量xy,若x.equals(y)返回true,则y.equals(x)返回true

***传递:对任意非空的引用变量xyz,若x.equals(y)返回truey.equals(z)返回true,则x.equals(z)也返回true

***此外,对于任意非空的引用变量x,则x.equals(null)返回false

 

 

 

 

hashCode()

***若两个对象相等,则它们的hashCode相等;反之,若两个对象不相等,则它们的hashCode不相等

***当重写Object中的equals()时,也要重写hashCode()

e.g

 

 

 

 

可变对象的观察等价性、行为等价性

***观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致

***行为等价性:调用对象的任何方法都展示出一致的结果

***对可变类型来说,往往倾向于实现严格的观察等价性

***对可变类型,实现行为等价性即可(使用equals()方法)即只有指向同样内存空间的object,才是相等的;因此对可变类型来说,无需重写equals()方法和hashCode()方法,直接继承Object这两个方法即可。若一定要判断两个可变对象看起来是否一致,最好定义一个新的方法

猜你喜欢

转载自blog.csdn.net/qq_41772794/article/details/80772206
今日推荐