面向对象 概念整理


SDU考试特别提醒:
听说以后就没有面向对象这门课了,不知道记这些还有没有用,希望能帮点忙
简答题和往年重合度极高,推荐看这个简答整理,基本就覆盖了。
2021年考了代理模式、观察者模式、装饰模式,要熟悉模式的类图,代码根据类图就能写出来
最后补充程序几乎是白送分的,想提分主要就在应用题(给出场景,问用什么设计模式,画类图写框架)方面下点功夫。

概念

面向对象和面向过程的区别

面向过程的算法中,系统围绕着过程组织起来,通过过程间的数据传送实现系统的功能,过程和数据被分开,程序员设计的重点分别集中在数据结构、算法、顺序步骤上,分析、设计和实现之间有较大的差距,代码难以重用。
面向对象的方法中,系统围绕着对象组织起来,通过对象间的消息传递而运行,相关的数据和行为捆绑在对象中。且将问题建模为对象的过程非常自然,易于理解,包括对象、类、继承、封装、消息、多态等概念,代码可轻松复用。

对象

对象是独立存在的客观事物,由一组属性和一组操作组成。属性是对象静态特征的描述,操作是对象动态特征的描述。
属性一般只能通过执行操作来改变。操作又称方法或服务,描述了对象执行的功能。

对象的性质:封装性、自治性、通信性、暂存性、永久性,对象之间可以通过组合聚合形成复合对象。

行为的启动是通过消息传递给负责的对象来完成的。对象可以自己选择方法来完成目标,而不应受到其他对象的干预。

类是具有相同属性和相同操作的对象的集合,所有对象都是类的实例。同一类的所有对象使用相同的方法。

类被组织成只有单个节点的树状关系,称为继承层次结构。父节点的属性和操作都会自动提供到子节点。抽象父类没有实例,只是用来产生子类。泛化继承表达了对象从一般到特殊的过程,子类是父类的特化。

内部类:Java的非静态内部类可以通过outer引用外部的变量和操作,静态内部类只能调用外部的静态变量和方法。C++内部类相当于嵌套,内部类无法访问外部(除非声明外部类为友元)。

类的数据字段:所有对象共享,不同语言有不同的初始化策略。

this隐含使用了当前调用成员函数的对象,称为对象的自身引用(在有些方法中默认隐藏,可以省略)。

构造函数:初始化新创建对象,确保该函数在初始化之前不会被多次使用。初始化器

析构函数、拷贝构造函数、赋值运算符

继承

继承能实现代码复用和概念复用。

替换原则:如果类B是类A的子类,那么任何情况下都可以用B替换A,而外界毫无察觉(方法和属性)。B称为子类型
子类型不一定要有继承关系,即使B不是A的子类,只要满足定义,也可以称为子类型。

延迟方法在父类中定义但并不实现,子类分别给出实现。

继承有8种,推荐使用以下三种:特殊化继承、规范化继承、扩展继承,其中前两种是最理想的。

特殊化继承:新类是基类的一种特殊类型,符合基类的所有规范

规范化继承:保证新类和基类具有某个共同的接口,基类中既有已实现的方法,也有只定义了接口、待实现的方法,新类负责实现基类中未实现的所有方法。基类可以使用abstract定义为抽象类。

扩展继承:新类只是往基类中添加新的行为,并不修改从基类继承来的属性。

其他继承类型:
构造继承:新类和基类没有很强的抽象关联性,只是有些方法类似,可以代码复用,如堆栈—队列
泛化继承:新类的范围比基类更一般,通常是基于数据的整体设计
限制继承:新类的方法比基类还要少,限制更加严格,于是会让基类的一些方法无效,如双向队列—堆栈
变体继承:两个或多个类需要实现相似的功能,但没有抽象关系,如鼠标和触控板
合并继承:通过合并两个类或更多的抽象特性形成新的抽象,如助教(学生+老师)

多重继承
在多重继承中,子类与每一个父类仍然要符合"is-a"或"has-a"规则,即使用推荐的继承类型。
对于多重继承时可能遇到的问题(两个父类有同样签名的方法),有三种解决方案:子类调用时加入类名限定;子类用专门的两种方法调用父类;对于每个父类,产生一个专门用于继承且确保方法签名不冲突的类。

构造函数与析构函数
多重继承中的构造函数遵循先父类、后成员对象、最后自己的顺序,如果有多个父类,则从左往右调用。虚基类的构造函数在非虚基类之前调用。析构函数与构造函数的顺序完全相反。

改写、重载、遮蔽、反射

改写:子类的方法采用与父类相同的名称,但实现自己的功能(代替、改进)。
有其他对象向接收器发送消息时,接收器在类中搜索并执行相应的方法。如果没有找到,就一直沿着父类向上寻找,直到找到匹配的方法,或父类链结束。如果在父类中找到同名的方法,就称子类改写了父类的方法。

重载:同一个类中,多个方法的名称相同,但参数不同。返回值类型可以相同也可以不同。
最常用的是构造函数重载。

遮蔽:遮蔽是在编译时基于静态类型解析的。在部分语言中改写需要特别声明,否则就被视为遮蔽。
如,父类和子类中有同名变量,但赋值不同。若对父类赋值为子类,父类中的变量值被遮蔽而不会被修改。

“反射”和“内省”:程序在运行时了解自身的能力。反射允许一个组件在运行时动态查询自身信息。

高级内容

协方差和反协方差

协方差:一个类型降低层次成为子类时,称为协方差
反协方差:一个类型由子类提升为基类时,称为反协方差

复制和克隆

实际上是一回事,且都有深浅之分。
浅复制:共享实例变量 深复制:建立实例变量的副本
要实现深复制,C++需要改写拷贝构造函数,Java需要改写clone方法

静态与动态

静态通常指在编译时就被绑定,且不允许再修改的属性或特征;动态通常指运行时才绑定的属性或特征。

静态类型语言:编译时进行内存分配,确定变量类型,不能更改,如Java/C++

动态类型语言:变量的类型与值有关,变量的值被改变时类型也有可能改变,如Python

静态类:用于声明变量的类,在编译时就确定且不在改变。

动态类:与变量当前值相关的类,在程序执行过程中可以依变量值而改变。

静态方法绑定:在编译时就对响应消息执行的方法进行绑定

动态方法绑定:响应消息时对哪个方法进行绑定是由接收器包含的动态数值决定的

静态成员:实现同类间多个对象的数据共享。静态成员函数不能使用this,不能访问非静态成员。

类方法又叫静态方法,实例方法又叫动态方法。

多态

对于一般类中定义的属性和服务,特殊类不改变其名字,但通过各自不同的实现来达到不同的行为。
重载、改写、多态、泛型都是多态的不同形式。

多态变量:可以引用多种对象类型的变量。动态语言的所有变量都可以多态,静态语言的多态是替换原则的具体表现。

多态变量的形式:简单变量、接收器变量、反多态、纯多态

接收器:执行基础方法时,接收器实际上保存了子类实例的数值。执行改写方法时,执行子类的方法而非父类的方法。

反多态(向下造型):是处理多态变量的过程。将值赋给一个子类变量,从而取消多态赋值

纯多态:支持可变参数的方法,通过给方法的接收器发送延迟消息(调用还未实现的方法)实现代码剪裁。

内存分配

静态存储分配:编译时就能准确确定运行时占用的内存。代码中不能含有可变数组、嵌套递归结构等因素。

栈式存储分配:也叫动态存储分配,程序在编译时完全不知道可能占用的内存,只有运行时才会知道。但运行中进入一个程序模块时,必须能够知道该程序模块所需的数据区大小。栈中的内存遵循先进后出原则。

堆式存储分配:专门负责在编译时和模块入口处都无法得知所需内存大小的数据结构的内存分配,如可变长数组、对象实例。堆中的内存可以按任意顺序申请和释放。


最小静态空间分配(C++):

只分配超类所需的内存空间,子类若转换为超类会被切割。

最大静态空间分配

无论基类还是派生类,都分配可用于所有合法数值的最大的存储空间。

动态内存分配(Java):

只分配用于保存一个指针的内存空间,运行时通过堆来分配所需的内存空间,同时将指针设为相应的值。

框架

通过类的集合形成,类之间紧密结合,共同实现对问题的可复用解决方案。常见的框架如Java的GUI框架。
框架的开发是为了代码复用和概念复用,因此高级抽象特别重要。

签名和范畴

函数类型签名是函数名、参数类型、参数顺序。
范畴是能够使名称有效使用的一段程序(作用域),通过继承创建新类会创建新的名称范畴,是对父类范畴的扩展。

重载可以分为:基于具有不同范畴的方法、基于不同函数签名的方法

对于前者,引入转换(也叫造型),当子类被作为实参出现在使用父类作为形参的方法中时,会被转换为父类。
重定义是子类中定义了与父类名称相同但签名不同的函数。

如果两个或更多的方法具有相同的名称和相同的参数数目,编译器如何匹配?

面向对象设计原则

设计目标

可扩展性、灵活性、可插入性

开闭原则OCP

软件组成实体对扩展开放,对修改关闭。应该设计出永远也不需要修改的模块,努力最小化不满足OCP的模块。

里氏替换原则LSP

即可替换原则,凡是父类适用的地方,子类也适用,反之不成立。

依赖倒转原则DIP

抽象不应该依赖于细节,细节应该依赖于抽象。要针对接口编程。

组合复用原则CRP

优先使用对象的组合,而非类的继承。

迪米特法则LoD

最少知识原则,一个对象应该对其他对象的信息尽可能少地了解。
不要与陌生人说话/只与直接朋友通信,“朋友”:当前对象、作为参数传入的对象、当前对象的属性对象、当前对象的实例直接引用的对象、聚集中的所有对象

接口隔离原则ISP

使用多个专门的接口比使用单一的总接口好。可以实现定制服务,为同一角色提供宽窄不同的接口。

单一职责原则SRP

每个类只负责一项职责,降低耦合增强内聚。可能导致职责扩散,必须在职责扩散加剧前重构代码。

设计模式

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这里只选择个别重点模式记录。
可以参考 设计模式 | 菜鸟教程 来看。下文部分模式图来自菜鸟教程。

简单工厂模式

一个工厂类根据传入的参量决定创建出哪一种产品类的实例(具体产品→抽象产品)

工厂方法模式

简单工厂只能支持有限的开闭原则。工厂方法模式定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。

抽象工厂模式

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。

单例模式

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类叫做单例类。

适配器模式

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起的那些类可以一起工作。

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

桥接模式

把抽象部分和行为部分分离,使它们都能独立变化。

装饰模式

动态、透明地给一个对象(而非一整个类)添加一些额外的职责。

策略模式

定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。

观察者模式

定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新。

往年还考过责任链模式,祖传提纲没给,今年也没考,于是没记。

posted @ 2021-07-05 14:47  Kamigen  阅读(157)  评论(0编辑  收藏  举报

很高兴见到你,祝你旅途愉快!