13.1引言继承可以指定类从父类中获取一些特性,再添加它自己的独特特性从编程的观点来看,需要继承是因为:它支持更丰富、更强大的建模它可以在一个类中定义信息和行为,在相关的子类中共享这些定义。这样需要编写的代码就减少了。继承是很自然的。它是面向对象的主要动机之一。23.1引言33.1引言43.1引言53.2设计类层次结构例子:要给集合(Collection)建模,集合是可以包含其它对象的对象。需求中需要四类集合List:该集合把所有的对象按照插入的顺序放置Bag:该集合中的对象没有排序LinkedList:该集合中的对象使用序列对象来排序,每个对象指向序列中的下一个对象ArrayList:该集合中的对象使用数组来排序,数组是相邻内存位置的序列63.2设计类层次结构73.2设计类层次结构从一般类到特殊类(普通类到抽象类)首先确定层次结构的底部有哪些类(Bag,LinkedList,ArrayList)接着找出比较一般的概念,以丰富模型,共享元素定义。因此把LinkedList,ArrayList组合到List中,把List和Bag组合到Collection中。在开发层次结构时,要查找消息——可以放置消息的层次结构越高越好。在查找其它类元素前,应先查找消息,因为消息表示对象给外界显示的接口,这是它们最重要的特性。83.2设计类层次结构考虑下面三个消息,将消息放在已有类层次结构中的哪个类中呢?contains(:Object):boolean//在集合中搜索对象elementAt(:int):Object//参数指定的位置检索对象numberOfElement():int//返回集合中的对象数。9添加实现代码已经有了类层次结构,还确定了消息的位置,下面必须添加实现元素(字段、构造函数和方法)方法放在哪里,这会引出两个重要的概念:抽象和重定义(与重载区别?)不可能在Collection类中编写contains实现方法,因为有序集合和无序集合的搜索算法是不同的。所以,必须先在Bag上实现contains方法。但List类怎么办呢?10添加实现代码11添加实现代码12添加实现代码13添加实现代码现在就能获得继承的好处:只编写一个方法,它可用于List的任何直接或间接子类。对于LinkedList,ArrayList来说,elementAt消息的实现代码是不同的。给numberOfElements编写实现代码:把元素数存储为字段需要时,再计算元素数14153.4抽象类抽象类是至少有一个抽象方法的类——抽象方法可以是该类本身的方法,也可以是从超类继承来的。若类中包含了抽象方法,那么该类只能声名为抽象类(或接口),抽象类中可以有抽象方法或具体方法,也可以没有抽象方法,甚至可以什么都没有。在抽象类中可以为部分方法提供默认的实现,从而避免在子类中重复实现它们,提高代码的可重用性,这是抽象类的优势所在而接口中只能包含抽象方法,扩展功能时,在抽象父类中加入具体方法不会影响子类;但在接口中加入抽象方法会影响到所有实现类。一个类只能继承一个直接的父类,这个父类有可能是抽象类;但一个类可以实现多个接口,这是接口的优势所在。163.4抽象类173.4抽象类抽象类用来定义类需要执行哪些行为而不必提供每一个行为的明确实现抽象类不能直接被实例化。抽象类的子类也可能仍然是一个抽象类。关键在于子类中是否提供了所有抽象方法的实现代码。抽象类是相对于接口而言是一个更加优秀的类的抽象模型。它除了具有接口一样的功能之外,还能将具体类中公共的方法实现集中到抽象类中,而不用在每一个具体类中重复。183.4抽象类抽象类有如下优点它们允许更丰富、更灵活地建模它们可以共享更多的代码,因为可以编写具体的方法来使用抽象的方法更便于封装变化点193.4抽象类在设计类层次结构是,应记住,大多数超类都是抽象的。下面说明了继承层次结构是从底向上派生的:1、在问题域中查找具体的概念,推导出它们的知识和行为2、在具体的类中找出共同点,以便引入更一般的超类3、把超类组合到更一般的超类中,直到找出根类在表示泛化(超类)时,我们希望它是抽象的,否则就可能表示为第1步中的具体概念。20抽象方法和虚方法抽象方法和虚方法的区别(abstract)抽象方法和(virtual)虚方法的区别在于:虚方法有一个实现部分可以为子类实现有共同的方法,并为派生提供了覆盖该方法的选项,相反,抽象方法没有提供实现部分,强制派生覆盖方法(否则派生类不能成具体类)(abstract)抽象方法只能在抽象类中声明,(virtual)虚方法不是。(abstract)抽象方法不能声明方法实体,虚方法可以。包含抽象方法(只有抽象类才可以包含抽象方法)的类不能实例化(也就是说只可以使用predected和private修饰符),虚方法可以213.5重定义方法面向对象可以重新定义继承来的元素,重定义允许子类修改继承方法的实现代码——消息看起来是相同的,但代码行被替换了。覆盖(overriding)技术。重定义可以使消息在子类中的可见性更高。重定义可以改变属性的名称和类型。(一般没必要这么做)223.5重定义方法重定义方法的三个原因:继承的方法是抽象的,我们希望给他一些代码,把它变成具体的。子类中的方法需要完成一些额外的工作。可以为子类提供更好的实现代码(更高效更准确)例如给LikedList类添加一个索引,就可以重新定义contains,使之比List使用的算法更快。为了完成额外的工作,每种面向对象的语言都允许重定义的方法调用超类的方法。233.5重定义方法243.6实现栈类实现Stack类,它带有如下四个消息2526实现栈类示例代码27继承和组合28继承和组合继承有一些独特的优点:它是自然的它是优雅的它允许编写一般的代码,例如用于Fruit的代码也可以用于Apple和Pear继承也有如下问题很难做得很好在发现设计中的不足时很难改变客户程序员很难理解层次结构会“泄漏”给客户代码,也难以改变复用易于维护强耦合性尽量降低耦合性,提高可扩展性29继承和组合组合会得到与继承相同的结果(具体类、具体消息和复用已有的代码)还有如下优点:较容易开发较容易改变客户容易理解不会泄露客户代码最大的优点是代码的安全性问题得到解决303.7多重继承313.7多重继承323.7多重继承333.7多重继承34继承通常被定义为两个类之间的“是一个”(“isa”)关系,如果类Y从类X中派生,那么Y确实是一个X的特例,因此可以对超类陈述的所有事实对其所有子类同样适用3.8使用继承的规则1.不要过度使用:不要认为必须使用继承,甚至总是使用继承。2.类应是超类的一个类型:只要从超类X中派生出Y,就要问问自己:“Y是X的一种类型吗”。3.类应是其超类的扩展:在子类中,应确保只添加新的特性,不要删除,禁用或重新解释特性,来分解超类。4.尽量少从具体类继承,继承接口或抽象类比较好。35设计原则可维护性复用的实现是以设计原则和设计模式为基础的。如果设计模式是“形”的话,那么设计原则就是“魂”。36设计原则这些设计原则是:开闭原则软件对象应该对扩展开放,对修改关闭里氏代换原则基类出现的地方,子类一定可以出现依赖倒转原则要依赖于抽象,不要依赖于实现合成/聚合复用原则尽量使用合成/聚合,而不是继承达到复用迪米特原则一个软件实体与尽可能少的实体相互作用接口隔离原则为客户端提供尽可能小的单独的接口37设计原则原则的英文名称对照表:开闭原则Open-ClosedPrincipleOCP里氏代换原则LiskovSubstitutionPrincipleLSP依赖倒转原则DependenceInversionPrincipleDIP合成/聚合复用原则Composite/AggregateReusePrincipleCARP迪米特原则LawofDemeterLoD接口隔离原则InterfaceSegregationPrincipleISP38设计原则原则之间的相互关系:开闭原则是构造可维护性复用性软件的基石,其他原则是达成开闭原则的手段和工具。