第2章SimDuck案例-类与接口设计辛国栋软件学院,宋健529,5687921gdxin@hit.edu.cn目的掌握类的设计类与类之间的关系UML中类图绘制方法抽象方法、抽象类覆盖的概念SimuDuck游戏代码实现Java中的注释Java中抽象类与接口的概念接口的实现分离变化点原则匿名对象组合与继承的对比SimDuck–classandinterfacedesign2SimDuck–classandinterfacedesign3从SimDuck游戏开始问题描述Joe上班的公司要做一套相当成功的模拟鸭子游戏SImUDuck。游戏中出现各种鸭子,一边游泳戏水,一边呱呱叫。此系统的内部设计使用了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承自这个超类。SimDuck–classandinterfacedesign4FirstDesign:游戏中出现各种鸭子,一边游泳戏水,一边呱呱叫这些鸭子游泳戏水、呱呱叫,因此可以抽象出这些鸭子的超类,定义为Duck类,在Duck类中有swim,quack方法。每种鸭子都有自己的外观,因此每种鸭子都应该实现自己的外观。SimDuck–classandinterfacedesign5FirstDesign:补充内容UML:软件工程师的图纸表达•UML(UnifiedModelingLanguage)统一建模语言类图的画法:•从上到下分为类名、属性和操作。类名必须有的•类如果有属性,则每一属性都必须有一个名字,还可以有其它的描述信息,如可见性、数据类型、缺省值等;•类如果有操作,则每一个操作也都有一个名字,其它可选的信息包括可见性、参数的名字、参数类型、参数缺省值和操作的返回值的类型等SimDuck–classandinterfacedesign6FirstDesign:补充内容类图的绘制方法:Account-balance:double=1+Deposit(amount:double):int+ComputeInterest():double可见性-或代表private+或代表public#或代表protected返回值类型操作名称斜体为抽象操作缺省值类名斜体为抽象类属性名称参数列表FirstDesign:补充内容类图的绘制方法:类之间的继承关系绘制方法•泛化(继承)关系用来描述类的一般和具体之间的关系—isakindof。•UML中,用一条带有空心大箭头的有向实线表示,箭头指向父类SuperClassSuperClassSubClass7SimDuck–classandinterfacedesignSimDuck–classandinterfacedesign8FirstDesign:每种鸭子都有display方法实现,有可能发生遗忘,仅仅靠程序员的素质来保证该方法的存在不合适。因此最好能在Duck超类中声明一个display()方法;从而子类必须必须完成display()具体实现,否则就会出现语法错误。SimDuck–classandinterfacedesign9FirstDesign:所有的鸭子都会呱呱叫(Quack)也会游泳(Swim),所以由超类负责处理这部分的实现代码。每个子类型负责实现自己的display()行为,以在屏幕上显示该鸭子的外观因为每一种鸭子的外观都不同,所以display()方法是抽象的。许多其他类型的鸭子继承自Duck类SimDuck–classandinterfacedesign10FirstDesign:RubberDuck叫声和MallardDuck、ReadHeadDuck叫声不一样,因此,覆盖超类中的quack()---java中的覆盖概念新建立一种鸭子:RubberDuckSimDuck–classandinterfacedesign11代码实现:SimDuck–classandinterfacedesign12sourcecodeofDuckpublicclassDuck{publicDuck(){}publicvoidquack(){System.out.println(Duckclass'squackmethod);}publicvoidswim(){System.out.println(Duckclass'sswimmethod);}publicabstractvoiddisplay();}abstractpublicclassDuck{SimDuck–classandinterfacedesign13sourcecodeofRedheadDuckpublicclassRedheadDuckextendsDuck{//构造方法publicRedheadDuck(){}/*多行注释*/publicvoiddisplay(){/**@todoImplementthisduckanditsinheritanceclass.Duckabstractmethod*/System.out.println(ThisisRedheadDuck'sdisplay!);}}SimDuck–classandinterfacedesign14sourcecodeofMallardDuckpublicclassMallardDuckextendsDuck{publicMallardDuck(){}publicvoiddisplay(){System.out.println(ThisisMallardDuck'sdisplay!);}}SimDuck–classandinterfacedesign15sourcecodeofRubberDuckpublicclassRubberDuckextendsDuck{publicvoidquack(){System.out.println(RubberDuckclass'squackmethod,overidedthesuper'smethod);}publicvoiddisplay(){System.out.println(ThisisMallardDuck'sdisplay!);}}SimDuck–classandinterfacedesign16sourcecodeofMiniDuckSimulatorpublicclassMiniDuckSimulator{publicstaticvoidmain(String[]args){MallardDuckmallardDuck=newMallardDuck();RubberDuckrubberDuck=newRubberDuck();//printinformationofMallardDuckSystem.out.println(ThefollowingareMallardDuck'sbehavior);mallardDuck.swim();mallardDuck.quack();mallardDuck.display();//printinformationofRubberDuckSystem.out.println(ThefollowingareRubberDuck'sbehavior);rubberDuck.swim();rubberDuck.quack();rubberDuck.display();//匿名对象的概念newRedHeadDuck().display();}}SimDuck–classandinterfacedesign17游戏规则增加:让鸭子飞去年,公司的竞争压力加剧,公司主管认为该是创新的时候了,他们需要在下周股东会议上展示一些真正让人印象深刻的东西来振奋人心。主管认为,此模拟程序需要会飞的鸭子,将竞争者抛在后头在这个时候,joe的经理拍胸脯告诉主管们,Joe只需要一个星期就可以搞定,“毕竟,Joe是一个OO程序员...这有什么困难?”SimDuck–classandinterfacedesign18Joe’sdesign我只需要在Duck类中加上fly()方法,然后所有鸭子都会继承fly()。这是我大显身手,展示OO才华的时候了。所有的子类都会继承fly()。Joe加上的SimDuck–classandinterfacedesign19设计出现严重错误“Joe,我正在股东会议上刚刚看了一下展示,有一只”橡皮鸭子”在屏幕上飞来飞去,这是你开的玩笑吗?”sourceSimDuck–classandinterfacedesign20Joe设计的问题Joe忽略了一件事:并非Duck所有的子类都会飞当Joe在Duck超类中加上新的行为,这会使得某些子类也具有这个不恰当的行为。现在可好了!SimUDuck程序中有一个会飞的非动物。对代码所做的局部修改,影响层面可能不只局部(会飞的橡皮鸭)!SimDuck–classandinterfacedesign21Joerealizethis:Joe体会到了一件事:当涉及”维护”时,为了”复用”(reuse)目的而使用继承,结局并不完美。在超类中加上fly(),就会导致所有的子类都具备fly(),连那些不该具备fly()的子类也无法免除。SimDuck–classandinterfacedesign22Joethinksaboutinheritance我可以把橡皮鸭类中的fly()方法覆盖掉,就好像覆盖quack()的作法一样..把橡皮鸭类中的fly()方法覆盖掉,就好像覆盖quack()的作法一样..橡皮鸭子不会呱呱叫,所以把quack()的定义覆盖成「吱吱叫」(squeak)SimDuck–classandinterfacedesign23AddaDecoyDuckintheclasses可是,如果以后我加入木质诱饵鸭DecoyDuck,又会如何?诱饵鸭是假鸭,不会飞也不会叫...DecoyDuckquack(){//覆盖,变成什么事都不做}display(){//诱饵鸭}fly(){//覆盖,变成什么事都不做}这是继承层次中的另一个类。注意,诱饵鸭既不会飞也不会叫,可是橡皮鸭不会飞但会叫SimDuck–classandinterfacedesign24Howaboutaninterface?Joe认识到继承可能不是一个好的解决方法,因为他刚刚拿到来自主管的备忘录,希望以后每六个月更新产品(至于更新的方法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检查是否需要覆盖fly()和quark()...这简直是无穷尽的恶梦。所以,他需要一个更清晰的方法,让「某些」(而不是全部)鸭子类型可飞或可叫每当有新的鸭子子类出现,他就要被迫检视并可能需要覆盖fly()和quark()SimDuck–classandinterfacedesign25Joe’snewideaforthedesign我可以把fly()取出来,放进一个「Flyable接口」中。这么一来,只有会飞的鸭子才实现此接口。同样的方式,也可以用来设计一个「Quackable接口」,因为不是所有的鸭子都会叫SimDuck–classandinterfacedesign26WhatdoyouthinkaboutthisdesignSimDuck–classandinterfacedesign27Duck类代码-接口版本abstractpublicclassDuck{publicDuck(){}publicvoidswim(){System.out.println(Duckclass'sswimmethod);}publicabstractvoiddisplay();}SimDuck–classandinterfacedesign28DecoyDuck类代码publicclassDecoyDuckextendsDuck{publicDecoyDuck(){}publicvoiddisplay(){System.out.println(I'maduc