面向对象多态变量泛型框架_第1页
面向对象多态变量泛型框架_第2页
面向对象多态变量泛型框架_第3页
面向对象多态变量泛型框架_第4页
面向对象多态变量泛型框架_第5页
已阅读5页,还剩88页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

多态变量多态变量是指可以引用多种对象类型的变量。这种变量在程序执行过程可以包含不同类型的数值。对于动态类型语言,所有的变量都可能是多态的。对于静态类型语言,多态变量那么是替换原那么的具体表现。Parentvariable=newChild();7/24/20231实际用法很少使用赋值,通常是伴随着函数或方法调用,通过数值和参数之间的绑定来实现的。7/24/20232多态变量形式简单变量接收器变量反多态纯多态(多态方法)7/24/20233简单多态变量publicclassSolitaire{ . . staticCardPileallPiles[]; . . publicvoidpaint(Graphicsg){ for(inti=0;i<13;i++) allPiles[].display(g); }}7/24/20234实例布局管理器LayoutManager是一个接口标准库为这个接口提供了几种不同的实现通过调用继承自Component类的setLayoutManager方法,将参数赋值给本地多态变量7/24/20235接收器变量多态变量最常用的场合是作为一个数值,用来表示正在执行的方法内部的接收器。隐藏伪变量smalltalk:self,C++,Java,C#:this7/24/20236例classThisExample{ publicvoidone(intx){ value=x+4; two(x+3); } privateintvalue; privatevoidtwo(inty){ System.out.println(“Valueis〞+(value+y)); }}7/24/20237等价的明确形式classThisExample{ publicvoidone(intx){ this.value=x+4; this.two(x+3); } privateintvalue; privatevoidtwo(inty){ System.out.println(“Valueis〞+(this.value+y)); }}7/24/20238

多态变量在框架中的作用多态接收器功能的强大之处表现在消息传递与改写相结合时。这种结合是软件框架开发的关键。一般框架系统中的方法分为两大类:在父类中定义根底方法,被多个子类所继承,但不被改写;父类定义了关于多种活动的方法,但要延迟到子类才实现。7/24/20239例由于根底方法被子类所继承,因此它们可以用于各个子类实例。接收器变量多态性的展现。当执行根底方法时,接收器实际上保存的是一个子类实例的数值。当执行一个改写方法时,执行的是子类的方法,而不是父类的方法。7/24/202310例classWindow{ publicvoidrepaint(){

…paint(graphicsContext);… }

abstractpublicvoidpaint(Graphicsg);privateGraphicsgraphicsContext;}classGraphicsWindowextendsWindow{ publicvoidpaint(Graphicsg){ //dotheappropriatepaintingjob }}7/24/202311思考根底方法执行延迟方法的模式。该结合允许在不修改原始代码的条件下,裁剪延迟方法以适应新的形势。是解决软件复用问题的关键。7/24/202312向下造型7/24/202313向下造型是处理多态变量的过程,并且在某种意义上这个过程的取消操作就是替换。能够将其赋值给一个声明为子类的变量吗?该取消多态赋值的过程,也称为反多态。其它例7/24/202314常用的数据结构:集合、堆栈、队列、列表。容器对象。可复用的软件组件。将不同的对象放入一个集合,取出时,如何知道对象的类型呢?实现机制7/24/202315ChildaChildIf(aVariableinstanceofChild) aChild=(Child)aVariable

纯多态7/24/202316多态方法支持可变参数的函数。支持代码只编写一次、高级别的抽象以及针对各种情况所需的代码裁剪。通常是通过给方法的接收器发送延迟消息来实现这种代码裁剪的。纯多态例7/24/202317关于纯多态的一个简单实例就是用JAVA语言编写的StringBuffer类中的append方法。这个方法的参数声明为Object类型,因此可以表示任何对象类型。ClassStringbuffer{ Stringappend(Objectvalue){ returnappend(value,toString();}

…}方法toString被延迟实现。纯多态例7/24/202318方法toString在子类中得以重定义。toString方法的各种不同版本产生不同的结果。所以append方法也类似产生了各种不同的结果。Append;一个定义,多种结果。多态的价值当讨论设计优秀的软件时,通常会提到“即插即用〞(plug-and-play)的概念。即插即用的概念是指,某物体可以被“插入〞(plugged)系统并能够在不需要任何其他工作〔如重新设置系统〕的情况下立即“使用〞(played)。这个术语通常在讨论计算机硬件时使用,比方将卡片插入计算机的扩展槽中并可以立即使用。但是,这个概念也同样适用于计算机软件,尤其是如果软件是优雅的,那么就可以删除某类的一个对象,并轻松地替换,或“插入〞另一个“同等〞的类的对象,而该过程将自动完成或只需极少量的代码改动。19来看一看集合类的例子,这些类是被用做其他对象的存储容器。为了便于此处的讨论,假设您曾经屡次使用过类,并将其作为大型软件系统中的集合类。假设经过系统的测试及使用,发现LinkedList在系统的某些局部运行过于缓慢,其原因是获取列表中第n个数据需要遍历列表中前n个元素。此时,一个很好的处理方法是将某些地方使用的LinkedList类替换为另一种集合类,比方将数据存储在数组中因而可以在常数时间内进行随机访问的ArrayList类。问题在于这样的替换可能会需要对系统做一些大的改动。20如果软件设计时并没有考虑到这样的替换,那么首先就需要寻找出系统中所有声明和/或初始化了LinkedList类型的变量的地方,然后判断是否要将其替换为ArrayList类型的变量的声明和/或初始化。但是,这样的声明或者初始化可能遍布在整个程序包中。如果没有发现所有应该改动的地方并进行合理的改动,那么会导致错误的产生21第二,如果使用了某些特定于LinkedList类的方法,如addFirst和addLast那么需要修改这些方法调用。这些方法调用需要被替换为使用ArrayList对象的相应代码。不仅仅是这些修改将导致巨大的工作量,如果以后发现ArrayList对象也不够满足需求而需要引入第三个集合类时,这样的整个过程还需要再次重复。这些改动将十分耗时,而且,它们还能意外地引入更多新错误。理想情况下,可以对系统代码只进行一个改动就改变系统对集合类的使用。22为了彻底清理代码以便将来可以轻易改动代码,第一步便是要认识到,LinkedList类和ArrayList类都实现了List接口。下一步便是观察List接口的方法是否满足了系统内部对集合类的所有需求。如果能够满足需求,那么系统内所有使用这些集合的地方,都应该调用List接口的方法,而不是特定于LinkedList类或ArrayList类的方法。23第三步便是处理所有LinkedList类型的变量的声明和初始化。为了防止以后对声明的改动,所有集合变量都要被声明为“实现了List接口中所有方法的对象〞(objects-that-implement-all-the-methods-in-our-List-interface)的类型,而不是将这些变量声明为诸如LinkedList的具体类的对象。也就是说,将变量声明为某种通用的List类型,可以允许这些变量指向实现了List接口的任何对象。那么以后改变设计来使用实现了List接口的新的集合类时,就不用再改动变量声明了。24还需要再解决一个问题。这个问题和容器变量的声明无关,而是关于这些变量的初始化。如果需要改动代码来使ArrayList对象代替LinkedList对象,那么代码中所有具有如下形式的语句Listlist=newLinkedList();都需要判断是否要替换成如下的形式。Listlist=newArrayList();25如何最小化工作量,来保证在改动变量初始化的时候,能够够最小化由这样的改动而引入错误的几率呢?更进一步说,是否可以使得代码更易于以后的改动?解决问题的方法就是将需要改动的代码局部化。在需要初始化集合变量的地方,我们使用“工厂〞(factory)方法代替前面的方式利用构造函数来创立新的对象,这个方法创立或者发现并返回所需对象26它可能会调用构造函数来创立新对象,或者它可能循环利用已经存在的可用对象〔存储在对象池或/真仓库〞中,。比方,您的系统可能有一个Manager类,该类具有工厂方法createNewList方法,该方法创立并返回LinkedList类或ArrayList类的一个对象:PubliclistcreateNewList(){ReturnnewLinkedList()}27一开始,可能会觉得将对LinkedList的构造函数的调用替换为对createNewList方法的调用并没有多大的进步,因为createNewList方法也仅仅是调用LinkedList的构造函数并返回。而真正的进步在于,所有对于容器变量的初始化语句可以被替换为如下的代码。Listlist=manager.createNewList();如果系统需要使用ArrayList对象来替换LinkedList对象,那么整个系统中只有一个地方需要改动:Manager类的createNewList方法。这个情形的一个解决方式就是按如下方式编写代码。PublicCollectioncreateNewList(){ReturnnewArrayList();)28泛型泛型通过类型的使用提供了一种将类或者函数参数化的方法。与通常的函数参数一样,泛型提供了一种无需识别特定数值的定义算法的方法。7/24/202329泛型泛型将名称定义为类型参数。在编译器读取类描述时,无法知道类型的属性,但是该类型参数可以像类型一样用于类定义内部。在将来的某一时刻,会通过具体的类型来匹配这一类型参数,这样就形成了类的完整声明。C++支持7/24/202330我们先来看看一个只能持有单个对象的类。7/24/202331在JavaSE5之前,我们可以让这个类直接持有Object类型的对象:7/24/202332有些情况下,我们确实希望容器能够同时持有多种类型的对象。但是,通常而言,我们只会使用容器来存储一种类型的对象。泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。7/24/202333因此,与其使用Object,我们更喜欢暂时不指定类型,而是稍后再决定具体使用什么类型。要到达这个目的,需要使用类型参数,用尖括号括住,放在类名后面。然后在使用这个类的时候,再用实际的类型替换此类型参数。在下面的例子中,T就是类型参数:7/24/2023347/24/202335现在,当你创立Holder3对象时,必须指明想持有什么类型的对象,将其置于尖括号内。就像main()中那样。然后,你就只能在Holder3中存入该类型〔或其子类,因为多态与泛型不冲突〕的对象了。并且,在你从Holder3中取出它持有的对象时,自动地就是正确的类型。

这就是Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。

7/24/202336泛型接口泛型也可以应用于接口。例如生成器〔generator〕,这是一种专门负责创立对象的类。7/24/202337我们需要IA的doSomething返回的是一个对象这个对象extends于BaseBeanInterfaceIA<TextendsBaseBean>{publicTdoSomething();}classIAImplimplementsIA<ChildBean>{publicChildBeandoSomething(){returnnewChildBean();}}这样当你调用这个实现的时候就能明确地得到返回的对象类型

7/24/202338C++模板函数Template<classT>Tmax(Tleft,Tright){ //returnlargestargument if(left<right) returnright; returnleft;}名称T是一个参数,但是它不同于函数的两个参数。在函数的代码体中,T可以用于任何适宜的类型。7/24/202339对T的限制?代码体功能决定。并非函数头强加的本例要求可比较7/24/202340模板类Template<classT>classBox{ public: Box(Tinitial):value(initial){}

TgetValue(){returnvalue;} setValue(TnewValue){value=newValue;} private:

Tvalue;};7/24/202341模板类的使用为了创立模板实例,模板参数必须与具体类型联系起来。Box(int)iBox(7);Cout<<iBox.getValue();???iBox.setValue(12);Cout<<iBox.getValue();???参数必须与接收器的类型相匹配iBox.setValue(3.1415);//ERROR,invalidtype7/24/202342模板类的应用模板类一般用于开发容器类Template<classT>classList{Public: voidadd(T); TfirstElement(); Tvalue;Private: List<T>*nextElement7/24/202343思考?在不允许改变原有类的情况下,如何将来自两个或者更多个不同类的元素结合起来。例如以统一的表示方式来处理。7/24/202344例Apple类:使用printOn方法将自身输出。Orange类:使用writeTo方法将自身输出。二进制提供希望将Apple对象和Orange对象保存在同一个列表中,并且使用一个多态函数将它们输出。7/24/202345第一步定义一个公共的父类,具有共同行为ClassFruit{ public: virtualvoidprint(ostream&)=0;//纯虚方法};7/24/202346第二步使用模版,创立一个fruitadapter类,将以Apple或者Orange为参数,同时符合水果的定义Template<classT>ClassFruitAdapter:publicFruit{Public: FruitAdapter(T&f):theFruit(f){} T&value(){returntheFruit;} virtualvoidprint(ostream&out){print(theFruit,out);}Public: T&theFruit;}; 7/24/202347第三步使用模版函数简化适配器的创立过程Template<classT>Fruit*newFruit(T&f){ returnnewFruitAdapter<T>(f);}; 7/24/202348最终方案AppleanApple(“Rome〞);OrangeanOrange;List<Fruit*>fruitList;fruitList.insert(newFruit(anApple));fruitList.insert(newFruit(anOrange));List<Fruit*>::iteratorstart=fruitList.begin();List<Fruit*>::iteratorstop=fruitList.end();For(;start!=stop;++start){ Fruit&aFruit=*start;aFruit.print(cout);}7/24/202349框架对于一类相似问题的骨架解决方案。通过类的集合形成,类之间紧密结合,共同实现对问题的可复用解决方案继承和改写的强大能力表达最常见的框架Java中的GUI框架Web开发中的Struts框架7/24/202350复用和特化框架开发的一个重要根底使用继承的两种方式:代码复用:根本方法,对问题的现存的解决方案。概念复用:特化方法,用于特定应用的解决方案。7/24/202351高级抽象和低级抽象例:雇员排序。7/24/202352雇员类classEmployee{public: stringname; intsalary; intstartingYear; }7/24/202353插入排序-根据工作年份voidsort(Employee*data[],intn){ for(inti=1;i<n;i++){ intj=i-1; while(j>=0&& v[j+1]->startingYear<v[j]->startingYear){ //swapelements Employee*temp=v[j]; v[j]=v[j+1]; v[j+1]=temp; j=j-1; } }}7/24/202354思考按照薪水排序?按照姓名排序?不再对雇员记录排序,对一个浮点数组排序?7/24/202355观察源代码级的修改。复用的是排序的思想,不是真正的实现。7/24/202356OO方案需要源代码级的修改的地方?元素类型、元素数目、数值比较、元素交换。封装改变7/24/202357OO方案-排序框架classInsertionSorter{public: voidsort(){ intn=size(); for(inti=1;i<n;i++){ intj=i-1; while(j>=0&&lessThan(j+1,j)){

swap(j,j+1); j=j-1; } } }7/24/202358OO方案-排序框架private: virtualintsize()=0;//abstractmethods virtualbooleanlessThan(inti,intj)=0; virtualvoidswap(inti,intj)=0;}7/24/202359OO方案-特化classEmployeeSorter:publicInsertionSorter{public: EmployeeSorter(Employee*d[],intn) {data=d;sze=n;}private: Employee*data[]; intsze=n; virtualintsize(){returnsze;} virtualboollessThan(inti,intj) {returndata[i]->startingYear<data[j]->startingYear;}7/24/202360OO方案-特化 virtualvoidswap(inti,intj){ Employee*temp=v[i]; v[i]=v[j]; v[j]=temp; }}7/24/202361观察基类不再需要改变。特化子类满足不同的需求。如:改变为对收入进行排序只需改变子类,无需改变父类对浮点数进行排序也只需创立一个新的子类,而无需改变父类7/24/202362继承继承允许进行高级别算法细节的封装还允许在不改变原始代码的情况下修改或特化这些细节。7/24/202363倒置库框架改变了应用程序(开发者定义的代码)与库代码之间的关系传统的应用程序中应用程序特定的代码定义了程序执行的总体流程在框架中控制流是由框架决定的,并且随应用程序的不同而不同新的应用程序的创立者只需改变供框架调用的例程即可,而无需改变总体结构.框架占主导地位,而应用程序特定的代码处于次要位置.7/24/202364预言变化面向对象设计艺术为应用程序预言将来可能发生的变化,并对应用程序进行相应的设计做到这点不容易,需要程序员认识到可以通过类似于以前解决问题的方式或者现存软件系统的方式来解决新问题时,才能够将该问题泛化,使其适合于更广泛的应用程序象C++,需要程序员区分哪些方法可以改写,以及哪些方法不能改写,这种方式过于僵硬了由于程序员在最初无法预言改写某个方法的需求7/24/202365对象互连多个代理组成的团体?对象绑定到一起的连接特性?7/24/202366对象互连一种考虑对象互连的方式就是研究可视性和依赖性这两个概念。可视性描述了关于名称的特性,通过该名称句柄可以存取对象,如果对象的名称是合法的且代表该对象,那么在这个特定环境下该对象就是可见的。描述可视性的相关术语还包括标识符的范畴依赖性将两个对象或者类联系起来,在不存在另外一个对象的条件下,如果一个对象的存在无任何意义,就说该对象依赖于另外那个对象。例如:子类几乎总是依赖于它的父类7/24/202367耦合和内聚耦合(coupling)和内聚(cohesion)的思想提供了一个框架,用于评价对象和类的应用是否有效。耦合描述类之间的关系,内聚描述类内部的关系。7/24/202368耦合的种类从最差的耦合到较好的耦合:内部数据耦合全局数据耦合控制〔或顺序〕耦合组件耦合参数耦合子类耦合7/24/202369内部数据耦合内部数据耦合发生在当一个类的实例直接修改另外一个类中的本地数据值〔实例变量〕时。ClassSneakyModifier{ public:voidsneaky(){ //changemyfriendsname myFriend->name=“Lucy〞; } Person*myFriend;};ClassPerson{ public:Person(){ name=“Larry〞; } stringname;};7/24/202370全局数据耦合全局数据耦合发生在两个或者更多个类型都依赖于公用的全局数据结构而绑定到一起的时候。DoubletodaysDow;ClassOne{ public:voidsetDow(){ todayDow=9473; }}ClassTwo{ public:voidprintDow(){ cout<<“TodaytheDowhit〞<<todaysDow; }};7/24/202371控制或者顺序耦合一个类必须以一种由任何位置控制的特定的顺序来执行操作。classMyClass{public: doFirst(){...} doSecond(){...} doThird(){...}}7/24/202372控制或者顺序耦合被顺序化的类应确保自身能够以正确的顺序实现操作,不应仅依赖于调用者的正确处理classMyClass{public: doStuff(){ doFirst(); doSecond(); doThird(); }protected: doFirst(){...} doSecond(){...} doThird(){...}}7/24/202373组件耦合组件耦合发生在一个类包含的数据字段或数值为另外一个类的实例时ClassSet{ . . private: Listdata;};7/24/202374参数耦合参数耦合发生在一个类必须调用另外一个类的效劳和例程时,此时两个类之间所发生的唯一关系就是一个类需要为另一个类提供参数数目、类型和返回值类型。ClassmyClass{ public: voiddoSomething(Setaset){ //dosomethingusingtheargumentvalue . }}7/24/202375子类耦合子类耦合是面向对象编程所特有的,描述了一个类与其父类之间的关系。通过继承,子类的实例可以被看成父类的实例。ClassParent{ . .}Class

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论